diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..1d17dae13b53adda563547053eb79233b236f797 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.venv diff --git a/.gitignore b/.gitignore index aca564c31615a4e2fa5c243648efd61416a4d314..f63a77d935094fe2dc273e075aee3b9dd299c944 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -printers.json -/printers.json +.venv +test.py +__pycache__ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..df738628c8e51d2f9b936ef30c9f66da52e1b0e9 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ +stages: + - build + - push + +build: + stage: build + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] + only: + - master + script: + - /kaniko/executor + --cache=true + --context "${CI_PROJECT_DIR}" + --dockerfile "${CI_PROJECT_DIR}/Dockerfile" + --destination "dcr.faked.org/chitui:${CI_COMMIT_SHORT_SHA}" + +push_latest: + stage: push + image: + name: gcr.io/go-containerregistry/crane:debug + entrypoint: [""] + only: + - master + script: + - crane copy dcr.faked.org/chitui:${CI_COMMIT_SHORT_SHA} dcr.faked.org/chitui:latest + +push_tag: + stage: push + image: + name: gcr.io/go-containerregistry/crane:debug + entrypoint: [""] + only: + - tags + script: + - crane copy dcr.faked.org/chitui:${CI_COMMIT_SHORT_SHA} dcr.faked.org/chitui:${CI_COMMIT_TAG} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f5f35197835d1cd1fc1e41507b878ec5995caf62 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.12-alpine + +WORKDIR /app + +RUN pip install gevent==24.2.1 gevent-websocket==0.10.1 + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY . . + +ENTRYPOINT ["python", "main.py"] diff --git a/README.md b/README.md index 5700946ad92fea5ce1cd7a8891ba0d962c3d41e8..6c3cc2c2c5ac5d4552eb8c6a3279f03863c5d45c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ # ChitUI A web UI for Chitubox SDCP 3.0 resin printers + +## Setup +``` +python -mvenv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +## Usage +After creating the virtual environment and installing the requirements, you can run ChitUI like this: +``` +python main.py +``` +and then access the web interface on port 54780, e.g. http://127.0.0.1:54780/ + +## Docker +As ChitUI needs to broadcast UDP messages on your network segment, running ChitUI in a Docker container requires host networking to be enabled for the container: +``` +docker build -t chitui:latest . +docker run --rm --name chitui --net=host chitui:latest +``` diff --git a/api.php b/api.php deleted file mode 100644 index 2e0647df495a39a86aafdb280743e04df0ed5318..0000000000000000000000000000000000000000 --- a/api.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -$printersJson = 'printers.json'; - -$output = '{"msg": "Nothing to see here..."}'; -if (isset($_GET['get'])) { - switch ($_GET['get']) { - case 'printers': - if (file_exists($printersJson)) { - $output = file_get_contents($printersJson); - } else { - $output = discoverPrinters(); - } - break; - default: - break; - } -} elseif (isset($_GET['action'])) { - switch ($_GET['action']) { - case 'discover': - $output = discoverPrinters(); - break; - default: - break; - } -} -header('Content-Type: application/json; charset=utf-8'); -echo $output; -die; - -function savePrinterInfo($response) { - global $printersJson; - - $printers = new stdClass(); - if (file_exists($printersJson)) { - $printers = json_decode(file_get_contents($printersJson)); - } - $info = json_decode($response); - $data = $info->Data; - $id = $info->Id; - $printer = array( - 'name' => $data->Name, - 'model' => $data->MachineName, - 'brand' => $data->BrandName, - 'ip' => $data->MainboardIP, - 'mainboard' => $data->MainboardID, - 'protocol' => $data->ProtocolVersion, - 'firmware' => $data->FirmwareVersion, - ); - $printers->$id = $printer; - if (file_put_contents($printersJson, json_encode($printers, JSON_PRETTY_PRINT))) { - return true; - } - return false; -} - -function discoverPrinters() { - global $printersJson; - $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); - $sockets = array($socket); - $null = NULL; - $socketTimeout = 3; - $socketOpen = true; - $msg = "M99999"; - - socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1); - socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); - socket_set_option($socket, SOL_SOCKET, SO_REUSEPORT, 1); - socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$socketTimeout, "usec"=>0)); - socket_bind($socket, '0.0.0.0'); - socket_sendto($socket, $msg, strlen($msg), 0, '255.255.255.255', 3000); - - while($socketOpen) { - if (socket_recv($socket, $data, 9999, 0)) { - //echo "Response received: ".$data.PHP_EOL; - savePrinterInfo($data); - } - $socketOpen = socket_select($sockets, $null, $null, $socketTimeout); - } - if (file_exists($printersJson)) { - return file_get_contents($printersJson); - } else { - return "{}"; - } -} diff --git a/index.html b/index.html deleted file mode 100644 index 9431c9cbd43dc6cb3ab78deaf533b33f56683ad9..0000000000000000000000000000000000000000 --- a/index.html +++ /dev/null @@ -1,114 +0,0 @@ -<!doctype html> -<html lang="en" data-bs-theme="auto"> - <head> - <script src="js/color-modes.js"></script> - - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta name="description" content="Chitubox SDCP WebUI"> - <meta name="author" content="Jan Grewe"> - <title>ChitUI</title> - <link href="css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> - - <!-- Favicons - <link rel="apple-touch-icon" href="/docs/5.3/assets/img/favicons/apple-touch-icon.png" sizes="180x180"> - <link rel="icon" href="/docs/5.3/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png"> - <link rel="icon" href="/docs/5.3/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png"> - <link rel="manifest" href="/docs/5.3/assets/img/favicons/manifest.json"> - <link rel="mask-icon" href="/docs/5.3/assets/img/favicons/safari-pinned-tab.svg" color="#712cf9"> - <link rel="icon" href="/docs/5.3/assets/img/favicons/favicon.ico"> - --> - <meta name="theme-color" content="#712cf9"> - <link href="css/bootstrap-icons.min.css" rel="stylesheet"> - <link href="css/chitui.css" rel="stylesheet"> - </head> - <body> - <svg xmlns="http://www.w3.org/2000/svg" class="d-none"> - <symbol id="check2" viewBox="0 0 16 16"> - <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/> - </symbol> - <symbol id="circle-half" viewBox="0 0 16 16"> - <path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/> - </symbol> - <symbol id="moon-stars-fill" viewBox="0 0 16 16"> - <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/> - <path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/> - </symbol> - <symbol id="sun-fill" viewBox="0 0 16 16"> - <path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/> - </symbol> - </svg> - <div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle"> - <button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center" - id="bd-theme" - type="button" - aria-expanded="false" - data-bs-toggle="dropdown" - aria-label="Toggle theme (auto)"> - <svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg> - <span class="visually-hidden" id="bd-theme-text">Toggle theme</span> - </button> - <ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text"> - <li> - <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false"> - <svg class="bi me-2 opacity-50" width="1em" height="1em"><use href="#sun-fill"></use></svg> - Light - <svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg> - </button> - </li> - <li> - <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false"> - <svg class="bi me-2 opacity-50" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg> - Dark - <svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg> - </button> - </li> - <li> - <button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true"> - <svg class="bi me-2 opacity-50" width="1em" height="1em"><use href="#circle-half"></use></svg> - Auto - <svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg> - </button> - </li> - </ul> - </div> - -<main class="d-flex flex-nowrap"> - - <div class="d-flex flex-column align-items-stretch flex-shrink-0 bg-body-tertiary shadow" style="width: 380px;"> - - <a href="/" class="d-flex flex-row justify-content-center align-items-center flex-shrink-0 ps-0 p-3 link-body-emphasis text-decoration-none border-bottom"> - <i class="bi-cloud-upload-fill fs-4 me-2"></i> - <span class="fs-4 fw-semibold">ChitUI</span> - </a> - - <div id="printersList" class="list-group list-group-flush border-bottom scrollarea"></div> - - </div> - <div class="ps-3">foobar</div> - -</main> - -<template id="printersListItem"> - <a href="#" id="" class="printerListItem list-group-item list-group-item-action py-3 lh-sm" data-connection-id="" data-printer-id=""> - <div class="d-flex flex-row"> - <img src="img/elegoo_saturn4ultra.webp" width="64px"> - <div class="d-flex flex-column w-100"> - <div class="d-flex flex-row align-items-center justify-content-between"> - <strong class="printerName text-body-emphasis mb-1"></strong> - <small class="printerStatus text-body-secondary"><i class="bi-circle"></i></small> - </div> - <div class="printerType col-10 mb-1 small"></div> - <div class="printerInfo col-10 mb-1 small">?</div> - </div> - </div> - </a> -</template> - - -<script src="js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> -<script src="js/jquery-3.7.1.min.js"></script> -<script src="js/reconnecting-websocket.min.js"></script> -<script src="js/chitui.js"></script> -</body> -</html> diff --git a/js/chitui.js b/js/chitui.js deleted file mode 100644 index 46cfe4995823f5a9b70613b5374d83b6cd582d2e..0000000000000000000000000000000000000000 --- a/js/chitui.js +++ /dev/null @@ -1,141 +0,0 @@ -var websockets = [] -var printers = {} - -const SDCP_MACHINE_STATUS_IDLE = 0 // Idle -const SDCP_MACHINE_STATUS_PRINTING = 1 // Executing print task -const SDCP_MACHINE_STATUS_FILE_TRANSFERRING = 2 // File transfer in progress -const SDCP_MACHINE_STATUS_EXPOSURE_TESTING = 3 // Exposure test in progress -const SDCP_MACHINE_STATUS_DEVICES_TESTING = 4 //Device self-check in progress - -$( document ).ready(function() { - getPrinters() -}); - -function getPrinters() { - $.getJSON( "api.php", { - 'get': 'printers' - }) - .done(function(data) { - printers = data - addPrinters(data) - //addPrinters(printers) // REMOVE_ME: Testing - }) - .fail(function() { - alert( "error" ) - }) -} - -function addPrinters(printers) { - $.each(printers, function(id, printer) { - var template = $("#printersListItem").html() - var item = $(template) - item.attr('id', 'printer_'+ printer.mainboard) - item.attr("data-connection-id", id) - item.attr("data-printer-id", printer.mainboard) - item.find(".printerName").text(printer.name) - item.find(".printerType").text(printer.brand + ' ' + printer.model) - item.on('click', function() { - $.each($('.printerListItem'), function() { - $(this).removeClass('active') - }) - $(this).addClass('active') - showPrinter($(this).data('connection-id')) - }) - $("#printersList").append(item) - connectPrinter(id, printer) - }); -} - -function showPrinter(id) { - console.log(printers) -} - -function connectPrinter(id, printer) { - var wsUrl = 'wss://'+window.location.hostname+'/ws/'+printer['ip'] - var ws = new ReconnectingWebSocket(wsUrl); - ws.onopen = function() { - sendRequest(id, printer, 0) - }; - ws.onmessage = function(data) { - msg = JSON.parse(data.data) - handleMsg(msg) - }; - websockets[id] = ws -} - -function handleMsg(msg) { - id = msg.Id - topic = msg.Topic.split("/")[1] - console.log('topic: '+ topic) - switch(topic) { - case 'response': - console.log(msg) - console.log('id: '+ id) - break - case 'status': - updateStatus(msg) - break - default: - break - } -} - -function updateStatus(data) { - console.log(data) - var info = $('#printer_'+data.MainboardID).find('.printerInfo') - switch(data.Status.CurrentStatus[0]) { - case SDCP_MACHINE_STATUS_IDLE: - info.text("Idle") - setPrinterStatus(data.MainboardID, "success") - break - case SDCP_MACHINE_STATUS_PRINTING: - break - case SDCP_MACHINE_STATUS_FILE_TRANSFERRING: - break - case SDCP_MACHINE_STATUS_EXPOSURE_TESTING: - break - case SDCP_MACHINE_STATUS_DEVICES_TESTING: - break - default: - break - } -} - -function setPrinterStatus(id, style) { - var status = $('#printer_'+id).find('.printerStatus') - status.removeClass(function(index, css) { - return (css.match(/\btext-\S+/g) || []).join(' '); - }).addClass("text-"+style); - status.find('i').removeClass().addClass('bi-circle-fill') -} - -function sendRequest(id, printer, cmd) { - var ts = new Date().getTime() / 1000; - var payload = { - "Id": id, - "Data":{ - "Cmd": cmd, - "Data": {}, - "RequestID": generateRequestId(16), - "MainboardID": printer["mainboard"], - "TimeStamp": ts, - "From": 0 - }, - "Topic": "sdcp/request/"+printer["mainboard"] - } - websockets[id].send(JSON.stringify(payload)) -} - - -function generateRequestId(size) { - return [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(''); -} - -/* global bootstrap: false */ -(() => { - 'use strict' - const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')) - tooltipTriggerList.forEach(tooltipTriggerEl => { - new bootstrap.Tooltip(tooltipTriggerEl) - }) -})() diff --git a/js/reconnecting-websocket.min.js b/js/reconnecting-websocket.min.js deleted file mode 100644 index 3015099ac17bb3ec057e545e21a4db524265d74f..0000000000000000000000000000000000000000 --- a/js/reconnecting-websocket.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a}); diff --git a/main.py b/main.py new file mode 100644 index 0000000000000000000000000000000000000000..7372e720b5c88bee11d32b01832fd9fd21631930 --- /dev/null +++ b/main.py @@ -0,0 +1,193 @@ +from flask import Flask, send_file +from flask_socketio import SocketIO +from threading import Thread +from loguru import logger +import socket +import json +import os +import websocket +import time +import sys + +debug = False +log_level = "INFO" +if os.environ.get("DEBUG") is not None and os.environ.get("DEBUG"): + debug = True + log_level = "DEBUG" + +logger.add(sys.stdout, colorize=debug, level=log_level) + +port = 54780 +discovery_timeout = 1 +app = Flask(__name__, + static_url_path='', + static_folder='web') +socketio = SocketIO(app) +websockets = {} +printers = {} + + +@app.route("/") +def web_index(): + return app.send_static_file('index.html') + + +@socketio.on('connect') +def sio_handle_connect(auth): + logger.info('Client connected') + socketio.emit('printers', printers) + + +@socketio.on('disconnect') +def sio_handle_disconnect(): + logger.info('Client disconnected') + + +@socketio.on('printers') +def sio_handle_printers(data): + logger.debug('client.printers >> '+data) + main() + + +@socketio.on('printer_info') +def sio_handle_printer_status(data): + logger.debug('client.printer_info >> '+data['id']) + get_printer_status(data['id']) + get_printer_attributes(data['id']) + + +@socketio.on('printer_files') +def sio_handle_printer_files(data): + logger.debug('client.printer_files >> '+json.dumps(data)) + get_printer_files(data['id'], data['url']) + + +def get_printer_status(id): + send_printer_cmd(id, 0) + + +def get_printer_attributes(id): + send_printer_cmd(id, 1) + + +def get_printer_files(id, url): + send_printer_cmd(id, 258, {"Url": url}) + + +def send_printer_cmd(id, cmd, data={}): + printer = printers[id] + ts = int(time.time()) + payload = { + "Id": printer['connection'], + "Data": { + "Cmd": cmd, + "Data": data, + "RequestID": os.urandom(8).hex(), + "MainboardID": id, + "TimeStamp": ts, + "From": 0 + }, + "Topic": "sdcp/request/" + id + } + logger.debug("printer << \n{p}", p=json.dumps(payload, indent=4)) + if id in websockets: + websockets[id].send(json.dumps(payload)) + + +def discover_printers(): + logger.info("Starting printer discovery.") + msg = b'M99999' + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, + socket.IPPROTO_UDP) # UDP + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + sock.settimeout(discovery_timeout) + sock.bind(('', 54781)) + sock.sendto(msg, ("255.255.255.255", 3000)) + socketOpen = True + printers = None + while (socketOpen): + try: + data = sock.recv(8192) + printers = save_discovered_printer(data) + except TimeoutError: + sock.close() + break + logger.info("Discovery done.") + return printers + + +def save_discovered_printer(data): + j = json.loads(data.decode('utf-8')) + printer = {} + printer['connection'] = j['Id'] + printer['name'] = j['Data']['Name'] + printer['model'] = j['Data']['MachineName'] + printer['brand'] = j['Data']['BrandName'] + printer['ip'] = j['Data']['MainboardIP'] + printer['protocol'] = j['Data']['ProtocolVersion'] + printer['firmware'] = j['Data']['FirmwareVersion'] + printers[j['Data']['MainboardID']] = printer + logger.info("Discovered: {n} ({i})".format( + n=printer['name'], i=printer['ip'])) + return printers + + +def connect_printers(printers): + for id, printer in printers.items(): + url = "ws://{ip}:3030/websocket".format(ip=printer['ip']) + logger.info("Connecting to: {n}".format(n=printer['name'])) + websocket.setdefaulttimeout(1) + ws = websocket.WebSocketApp(url, + on_message=ws_msg_handler, + on_open=lambda _: ws_connected_handler( + printer['name']), + on_close=lambda _, s, m: logger.info( + "Connection to '{n}' closed: {m} ({s})".format(n=printer['name'], m=m, s=s)), + on_error=lambda _, e: logger.info( + "Connection to '{n}' error: {e}".format(n=printer['name'], e=e)) + ) + websockets[id] = ws + Thread(target=lambda: ws.run_forever(reconnect=1), daemon=True).start() + + return True + + +def ws_connected_handler(name): + logger.info("Connected to: {n}".format(n=name)) + socketio.emit('printers', printers) + + +def ws_msg_handler(ws, msg): + data = json.loads(msg) + logger.debug("printer >> \n{m}", m=json.dumps(data, indent=4)) + if data['Topic'].startswith("sdcp/response/"): + socketio.emit('printer_response', data) + elif data['Topic'].startswith("sdcp/status/"): + socketio.emit('printer_status', data) + elif data['Topic'].startswith("sdcp/attributes/"): + socketio.emit('printer_attributes', data) + elif data['Topic'].startswith("sdcp/error/"): + socketio.emit('printer_error', data) + elif data['Topic'].startswith("sdcp/notice/"): + socketio.emit('printer_notice', data) + else: + logger.warning("--- UNKNOWN MESSAGE ---") + logger.warning(data) + logger.warning("--- UNKNOWN MESSAGE ---") + + +def main(): + printers = discover_printers() + if len(printers) > 0: + connect_printers(printers) + logger.info("Setting up connections: done.") + socketio.emit('printers', printers) + else: + logger.error("No printers discovered.") + + +if __name__ == "__main__": + main() + + socketio.run(app, host='0.0.0.0', port=port, + debug=debug, use_reloader=debug, log_output=True) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..2b66ca5063f7dca51ae55459ae6ecb6864daf759 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask==3.0.3 +flask-socketio==5.4.1 +websocket-client==1.8.0 +loguru==0.7.2 diff --git a/web/assets/apple-touch-icon.png b/web/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..70edbf40e10a082867692f1cab805b789fb00dfe Binary files /dev/null and b/web/assets/apple-touch-icon.png differ diff --git a/web/assets/favicon-48x48.png b/web/assets/favicon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..129b7a559561df298c454bf56fb5f1e66507076f Binary files /dev/null and b/web/assets/favicon-48x48.png differ diff --git a/web/assets/favicon.ico b/web/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ee071b49af9e724bef92a4a5e37307df1412f4b7 Binary files /dev/null and b/web/assets/favicon.ico differ diff --git a/web/assets/favicon.svg b/web/assets/favicon.svg new file mode 100644 index 0000000000000000000000000000000000000000..4526e9100688a4193e81b37e33a3ec34858f10d1 --- /dev/null +++ b/web/assets/favicon.svg @@ -0,0 +1,23 @@ +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="1000" height="1000"><style> + #light-icon { + display: inline; + } + #dark-icon { + display: none; + } + + @media (prefers-color-scheme: dark) { + #light-icon { + display: none; + } + #dark-icon { + display: inline; + } + } + </style><g id="light-icon"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="1000" height="1000"><g clip-path="url(#SvgjsClipPath1052)"><rect width="1000" height="1000" fill="#ffffff"></rect><g transform="matrix(50,0,0,50,100,124.5)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="16" height="16"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-printer-fill" viewBox="0 0 16 16"> + <path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1"></path> + <path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1"></path> +</svg></svg></g></g><defs><clipPath id="SvgjsClipPath1052"><rect width="1000" height="1000" x="0" y="0" rx="350" ry="350"></rect></clipPath></defs></svg></g><g id="dark-icon"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="1000" height="1000"><g><g transform="matrix(62.5,0,0,62.5,0,30.75)" style="filter: contrast(0.3846153846153846) brightness(4.5)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="16" height="16"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-printer-fill" viewBox="0 0 16 16"> + <path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1"></path> + <path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1"></path> +</svg></svg></g></g></svg></g></svg> \ No newline at end of file diff --git a/web/assets/site.webmanifest b/web/assets/site.webmanifest new file mode 100644 index 0000000000000000000000000000000000000000..54ffefa93f1d31d8fb409d8a04bdea094718d0bf --- /dev/null +++ b/web/assets/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "ChitUI", + "short_name": "ChitUI", + "icons": [ + { + "src": "/assets/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/assets/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/web/assets/web-app-manifest-192x192.png b/web/assets/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..03de22c2024d16108f2136ec0af96cad924a37c6 Binary files /dev/null and b/web/assets/web-app-manifest-192x192.png differ diff --git a/web/assets/web-app-manifest-512x512.png b/web/assets/web-app-manifest-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..602ce94ae1907b5cb9162231a96178cf6c15061f Binary files /dev/null and b/web/assets/web-app-manifest-512x512.png differ diff --git a/css/bootstrap-icons.min.css b/web/css/bootstrap-icons.min.css similarity index 100% rename from css/bootstrap-icons.min.css rename to web/css/bootstrap-icons.min.css diff --git a/css/bootstrap.min.css b/web/css/bootstrap.min.css similarity index 100% rename from css/bootstrap.min.css rename to web/css/bootstrap.min.css diff --git a/css/bootstrap.min.css.map b/web/css/bootstrap.min.css.map similarity index 100% rename from css/bootstrap.min.css.map rename to web/css/bootstrap.min.css.map diff --git a/css/chitui.css b/web/css/chitui.css similarity index 100% rename from css/chitui.css rename to web/css/chitui.css diff --git a/css/fonts/bootstrap-icons.woff b/web/css/fonts/bootstrap-icons.woff similarity index 100% rename from css/fonts/bootstrap-icons.woff rename to web/css/fonts/bootstrap-icons.woff diff --git a/css/fonts/bootstrap-icons.woff2 b/web/css/fonts/bootstrap-icons.woff2 similarity index 100% rename from css/fonts/bootstrap-icons.woff2 rename to web/css/fonts/bootstrap-icons.woff2 diff --git a/img/elegoo_saturn4ultra.webp b/web/img/elegoo_saturn4ultra.webp similarity index 100% rename from img/elegoo_saturn4ultra.webp rename to web/img/elegoo_saturn4ultra.webp diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000000000000000000000000000000000000..a788f29450323a3aac0564974f884d3cb0e4ecc9 --- /dev/null +++ b/web/index.html @@ -0,0 +1,150 @@ +<!DOCTYPE html> +<html lang="en" data-bs-theme="auto"> + <head> + <script src="js/color-modes.js"></script> + + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="Chitubox SDCP WebUI" /> + <meta name="author" content="Jan Grewe" /> + <title>ChitUI</title> + <link href="css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous" /> + + <link rel="icon" type="image/png" href="/assets/favicon-48x48.png" sizes="48x48" /> + <link rel="icon" type="image/svg+xml" href="/assets/favicon.svg" /> + <link rel="icon" type="image/x-icon" href="/assets/favicon.ico" /> + <link rel="shortcut icon" href="/assets/favicon.ico" /> + <link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png" /> + <meta name="apple-mobile-web-app-title" content="ChitUI" /> + <link rel="manifest" href="/assets/site.webmanifest" /> + + <meta name="theme-color" content="#712cf9" /> + <link href="css/bootstrap-icons.min.css" rel="stylesheet" /> + <link href="css/chitui.css" rel="stylesheet" /> + </head> + <body> + <svg xmlns="http://www.w3.org/2000/svg" class="d-none"> + <symbol id="check2" viewBox="0 0 16 16"> + <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" /> + </symbol> + <symbol id="circle-half" viewBox="0 0 16 16"> + <path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" /> + </symbol> + <symbol id="moon-stars-fill" viewBox="0 0 16 16"> + <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" /> + <path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" /> + </symbol> + <symbol id="sun-fill" viewBox="0 0 16 16"> + <path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" /> + </symbol> + </svg> + <div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle"> + <button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button" aria-expanded="false" data-bs-toggle="dropdown" aria-label="Toggle theme (auto)"> + <svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg> + <span class="visually-hidden" id="bd-theme-text">Toggle theme</span> + </button> + <ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text"> + <li> + <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false"> + <svg class="bi me-2 opacity-50" width="1em" height="1em"><use href="#sun-fill"></use></svg> + Light + <svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg> + </button> + </li> + <li> + <button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false"> + <svg class="bi me-2 opacity-50" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg> + Dark + <svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg> + </button> + </li> + <li> + <button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true"> + <svg class="bi me-2 opacity-50" width="1em" height="1em"><use href="#circle-half"></use></svg> + Auto + <svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg> + </button> + </li> + </ul> + </div> + + <main class="d-flex flex-nowrap"> + <div class="d-flex flex-column align-items-stretch flex-shrink-0 bg-body-tertiary shadow" style="width: 300px"> + <a href="#" class="d-flex flex-row justify-content-center align-items-center flex-shrink-0 ps-0 p-3 link-body-emphasis text-decoration-none border-bottom"> + <i class="serverStatus bi-cloud text-danger fs-4 me-2"></i> + <span class="fs-4 fw-semibold">ChitUI</span> + </a> + + <div id="printersList" class="list-group list-group-flush border-bottom scrollarea"></div> + </div> + <!-- main column --> + <div class="container w-100"> + + <div class="row p-3"> + <div class="col-12 col-lg-8 offset-lg-2"> + <div class="card bg-body-secondary mb-3 w-100"> + <div class="row g-0"> + <div class="col-md-4"> + <img src="" class="img-fluid rounded-start" id="printerIcon" alt="..." /> + </div> + <div class="col-md-8"> + <div class="card-body"> + <h3 class="card-title" id="printerName"></h3> + <p class="card-text" id="printerType"></p> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class="card"> + <div class="card-header"> + <ul class="nav nav-tabs card-header-tabs" role="tablist" id="navTabs"></ul> + </div> + <div class="tab-content" id="navPanes"></div> + </div> + + </div> + </main> + + <template id="tmplPrintersListItem"> + <a href="#" id="" class="printerListItem list-group-item list-group-item-action py-3 lh-sm" data-connection-id="" data-printer-id=""> + <div class="d-flex flex-row"> + <img class="printerIcon" src="" width="64px" /> + <div class="d-flex flex-column w-100"> + <div class="d-flex flex-row align-items-center justify-content-between"> + <strong class="printerName text-body-emphasis mb-1"></strong> + <small class="printerStatus text-body-secondary"><i class="bi-circle"></i></small> + <div class="printerSpinner text-body-secondary spinner-border spinner-border-sm visually-hidden" role="status"> + <span class="visually-hidden">Loading...</span> + </div> + </div> + <div class="printerType col-10 mb-1 small"></div> + <div class="printerInfo col-10 mb-1 small">?</div> + </div> + </div> + </a> + </template> + + <template id="tmplNavTab"> + <li class="nav-item" role="presentation"> + <button class="nav-link" id="" data-bs-toggle="tab" data-bs-target="" type="button" role="tab"></button> + </li> + </template> + + <template id="tmplNavPane"> + <div class="tab-pane" id="" role="tabpanel" tabindex="0"> + <table class="table"> + <tbody id=""></tbody> + </table> + </div> + </template> + + <script src="js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> + <script src="js/jquery-3.7.1.min.js"></script> + <script src="js/socket.io.min.js"></script> + <script src="js/sdcp.js"></script> + <script src="js/chitui.js"></script> + </body> +</html> diff --git a/js/bootstrap.bundle.min.js b/web/js/bootstrap.bundle.min.js similarity index 100% rename from js/bootstrap.bundle.min.js rename to web/js/bootstrap.bundle.min.js diff --git a/js/bootstrap.bundle.min.js.map b/web/js/bootstrap.bundle.min.js.map similarity index 100% rename from js/bootstrap.bundle.min.js.map rename to web/js/bootstrap.bundle.min.js.map diff --git a/web/js/chitui.js b/web/js/chitui.js new file mode 100644 index 0000000000000000000000000000000000000000..95f3814b8a46ed55d4d881e89feac3d31e190d5b --- /dev/null +++ b/web/js/chitui.js @@ -0,0 +1,248 @@ +const socket = io(); +var websockets = [] +var printers = {} + +socket.on("connect", () => { + console.log('socket.io connected: ' + socket.id); + setServerStatus(true) +}); + +socket.on("disconnect", () => { + console.log("socket.io disconnected"); // undefined + setServerStatus(false) +}); + +socket.on("printers", (data) => { + console.log(JSON.stringify(data)) + printers = data + $("#printersList").empty() + addPrinters(data) +}); + +socket.on("printer_response", (data) => { + switch (data.Data.Cmd) { + case SDCP_CMD_STATUS: + case SDCP_CMD_ATTRIBUTES: + //console.log(JSON.stringify(data)) + break + case SDCP_CMD_RETRIEVE_FILE_LIST: + handle_printer_files(data.Data.MainboardID, data.Data.Data.FileList) + break + default: + console.log(data) + break + } +}); + +socket.on("printer_error", (data) => { + console.log("=== ERROR ===") + console.log(data) + alert("Error Code:" + data.Data.Data.ErrorCode) +}); + +socket.on("printer_notice", (data) => { + console.log("=== NOTICE ===") + console.log(data) + alert("Notice:" + data.Data.Data.Message) +}); + +socket.on("printer_status", (data) => { + //console.log(JSON.stringify(data)) + var fields = {} + var filter = ['CurrentStatus', 'PrintScreen', 'ReleaseFilm', 'TempOfUVLED', 'TimeLapseStatus', 'PrintInfo'] + $.each(data.Status, function (key, val) { + if (filter.includes(key)) { + fields[key] = val + } + }) + printers[data.MainboardID]['status'] = fields + updatePrinterStatus(data) +}); + +socket.on("printer_attributes", (data) => { + //console.log(JSON.stringify(data)) + var fields = {} + var filter = ['Resolution', 'XYZsize', 'NumberOfVideoStreamConnected', 'MaximumVideoStreamAllowed', 'UsbDiskStatus', 'Capabilities', 'SupportFileType', 'DevicesStatus', 'ReleaseFilmMax', 'CameraStatus', 'RemainingMemory', 'TLPNoCapPos', 'TLPStartCapPos', 'TLPInterLayers'] + $.each(data.Attributes, function (key, val) { + if (filter.includes(key)) { + fields[key] = val + } + }) + printers[data.MainboardID]['attributes'] = fields + //updatePrinterAttributes(data) +}); + +function handle_printer_files(id, data) { + if (printers[id]['files'] == undefined) { + printers[id]['files'] = [] + } + $.each(data, function (i, f) { + if (f.type === 0) { + getPrinterFiles(id, f.name) + } else { + printers[id]['files'].push(f.name) + createTable('Files', [f.name]) + } + }) +} + + +function addPrinters(printers) { + $.each(printers, function (id, printer) { + var template = $("#tmplPrintersListItem").html() + var item = $(template) + var printerIcon = (printer.brand + '_' + printer.model).split(" ").join("").toLowerCase() + item.attr('id', 'printer_' + id) + item.attr("data-connection-id", printer.connection) + item.attr("data-printer-id", id) + item.find(".printerName").text(printer.name) + item.find(".printerType").text(printer.brand + ' ' + printer.model) + item.find(".printerIcon").attr("src", 'img/' + printerIcon + '.webp') + item.on('click', function () { + // $.each($('.printerListItem'), function () { + // $(this).removeClass('active') + // }) + // $(this).addClass('active') + showPrinter($(this).data('printer-id')) + }) + $("#printersList").append(item) + socket.emit("printer_info", { id: id }) + }); +} + +function showPrinter(id) { + //console.log(JSON.stringify(printers[id])) + var p = printers[id] + var printerIcon = (p.brand + '_' + p.model).split(" ").join("").toLowerCase() + $('#printerName').text(p.name) + $('#printerType').text(p.brand + ' ' + p.model) + $("#printerIcon").attr("src", 'img/' + printerIcon + '.webp') + + createTable('Status', p.status, true) + createTable('Attributes', p.attributes) + createTable('Print', p.status.PrintInfo) + + // only get files once + if (printers[id]['files'] == undefined) { + getPrinterFiles(id, '/local') + if (p.attributes.UsbDiskStatus == 1) { + getPrinterFiles(id, '/usb') + } + } +} + +function createTable(name, data, active = false) { + if ($('#tab-'+name).length == 0) { + var tTab = $("#tmplNavTab").html() + var tab = $(tTab) + tab.find('button').attr('id', 'tab-'+name) + tab.find('button').attr('data-bs-target', '#tab'+name) + tab.find('button').text(name) + if(active) { + tab.find('button').addClass('active') + } + $('#navTabs').append(tab) + } + + if ($('#tab'+name).length == 0) { + var tPane = $("#tmplNavPane").html() + var pane = $(tPane) + pane.attr('id', 'tab'+name) + pane.find('tbody').attr('id', 'table'+name) + if(active) { + pane.addClass('active') + } + $('#navPanes').append(pane) + } + fillTable(name, data) +} + +function fillTable(table, data) { + var t = $('#table'+table) + $.each(data, function (key, val) { + if (typeof val === 'object') { + val = JSON.stringify(val) + } + var row = $('<tr><td>' + key + '</td><td>' + val + '</td></tr>') + t.append(row) + }) +} + +function getPrinterFiles(id, url) { + socket.emit("printer_files", { id: id, url: url }) +} + +function updatePrinterStatus(data) { + var info = $('#printer_' + data.MainboardID).find('.printerInfo') + switch (data.Status.CurrentStatus[0]) { + case SDCP_MACHINE_STATUS_IDLE: + info.text("Idle") + updatePrinterStatusIcon(data.MainboardID, "success", false) + break + case SDCP_MACHINE_STATUS_PRINTING: + info.text("Printing") + updatePrinterStatusIcon(data.MainboardID, "primary", true) + break + case SDCP_MACHINE_STATUS_FILE_TRANSFERRING: + info.text("File Transfer") + updatePrinterStatusIcon(data.MainboardID, "warning", true) + break + case SDCP_MACHINE_STATUS_EXPOSURE_TESTING: + info.text("Exposure Test") + updatePrinterStatusIcon(data.MainboardID, "info", true) + break + case SDCP_MACHINE_STATUS_DEVICES_TESTING: + info.text("Device Self-Test") + updatePrinterStatusIcon(data.MainboardID, "warning", true) + break + default: + break + } +} + +function updatePrinterStatusIcon(id, style, spinner) { + var el = 'printerStatus' + if (spinner) { + el = 'printerSpinner' + $('.printerStatus').addClass('visually-hidden') + $('.printerSpinner').removeClass('visually-hidden') + } else { + $('.printerStatus').removeClass('visually-hidden') + $('.printerSpinner').addClass('visually-hidden') + } + var status = $('#printer_' + id).find('.' + el) + status.removeClass(function (index, css) { + return (css.match(/\btext-\S+/g) || []).join(' '); + }).addClass("text-" + style); + status.find('i').removeClass().addClass('bi-circle-fill') +} + +function setServerStatus(online) { + serverStatus = $('.serverStatus') + if (online) { + serverStatus.removeClass('bi-cloud text-danger').addClass('bi-cloud-check-fill') + } else { + serverStatus.removeClass('bi-cloud-check-fill').addClass('bi-cloud text-danger') + } +} + +$('.serverStatus').on("mouseenter", function (e) { + if ($(this).hasClass('bi-cloud-check-fill')) { + $(this).removeClass('bi-cloud-check-fill').addClass('bi-cloud-plus text-primary') + } +}); +$('.serverStatus').on("mouseleave", function (e) { + $(this).removeClass('bi-cloud-plus text-primary').addClass('bi-cloud-check-fill') +}); +$('.serverStatus').on('click', function (e) { + socket.emit("printers", "{}") +}); + +/* global bootstrap: false */ +(() => { + 'use strict' + const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')) + tooltipTriggerList.forEach(tooltipTriggerEl => { + new bootstrap.Tooltip(tooltipTriggerEl) + }) +})() diff --git a/js/color-modes.js b/web/js/color-modes.js similarity index 100% rename from js/color-modes.js rename to web/js/color-modes.js diff --git a/js/jquery-3.7.1.min.js b/web/js/jquery-3.7.1.min.js similarity index 100% rename from js/jquery-3.7.1.min.js rename to web/js/jquery-3.7.1.min.js diff --git a/js/jquery-3.7.1.min.map b/web/js/jquery-3.7.1.min.map similarity index 100% rename from js/jquery-3.7.1.min.map rename to web/js/jquery-3.7.1.min.map diff --git a/web/js/sdcp.js b/web/js/sdcp.js new file mode 100644 index 0000000000000000000000000000000000000000..e4a280295304258adebabedfe7e3ab2b0eef8ded --- /dev/null +++ b/web/js/sdcp.js @@ -0,0 +1,95 @@ +// MACHINE_STATUS +const SDCP_MACHINE_STATUS_IDLE = 0 // Idle +const SDCP_MACHINE_STATUS_PRINTING = 1 // Executing print task +const SDCP_MACHINE_STATUS_FILE_TRANSFERRING = 2 // File transfer in progress +const SDCP_MACHINE_STATUS_EXPOSURE_TESTING = 3 // Exposure test in progress +const SDCP_MACHINE_STATUS_DEVICES_TESTING = 4 //Device self-check in progress +// PRINT_STATUS +const SDCP_PRINT_STATUS_IDLE = 0 // Idle +const SDCP_PRINT_STATUS_HOMING = 1 // Resetting +const SDCP_PRINT_STATUS_DROPPING = 2 // Descending +const SDCP_PRINT_STATUS_EXPOSURING = 3 // Exposing +const SDCP_PRINT_STATUS_LIFTING = 4 // Lifting +const SDCP_PRINT_STATUS_PAUSING = 5 // Executing Pause Action +const SDCP_PRINT_STATUS_PAUSED = 6 // Suspended +const SDCP_PRINT_STATUS_STOPPING = 7 // Executing Stop Action +const SDCP_PRINT_STATUS_STOPED = 8 // Stopped +const SDCP_PRINT_STATUS_COMPLETE = 9 // Print Completed +const SDCP_PRINT_STATUS_FILE_CHECKING = 10 // File Checking in Progress +// PRINT_ERROR +const SDCP_PRINT_ERROR_NONE = 0 // Normal +const SDCP_PRINT_ERROR_CHECK = 1 // File MD5 Check Failed +const SDCP_PRINT_ERROR_FILEIO = 2 // File Read Failed +const SDCP_PRINT_ERROR_INVLAID_RESOLUTION = 3 // Resolution Mismatch +const SDCP_PRINT_ERROR_UNKNOWN_FORMAT = 4 // Format Mismatch +const SDCP_PRINT_ERROR_UNKNOWN_MODEL = 5 // Machine Model Mismatch +// FILE_TRANSFER +const SDCP_FILE_TRANSFER_ACK_SUCCESS = 0 // Success +const SDCP_FILE_TRANSFER_ACK_NOT_TRANSFER = 1 // The printer is not currently transferring files. +const SDCP_FILE_TRANSFER_ACK_CHECKING = 2 // The printer is already in the file verification phase. +const SDCP_FILE_TRANSFER_ACK_NOT_FOUND = 3 // File not found. +// PRINT_CTRL +const SDCP_PRINT_CTRL_ACK_OK = 0 // OK +const SDCP_PRINT_CTRL_ACK_BUSY = 1 // Busy +const SDCP_PRINT_CTRL_ACK_NOT_FOUND = 2 // File Not Found +const SDCP_PRINT_CTRL_ACK_MD5_FAILED = 3 // MD5 Verification Failed +const SDCP_PRINT_CTRL_ACK_FILEIO_FAILED = 4 // File Read Failed +const SDCP_PRINT_CTRL_ACK_INVLAID_RESOLUTION = 5 // Resolution Mismatch +const SDCP_PRINT_CTRL_ACK_UNKNOW_FORMAT = 6 // Unrecognized File Format +const SDCP_PRINT_CTRL_ACK_UNKNOW_MODEL = 7 // Machine Model Mismatch +// PRINT_CAUSE +const SDCP_PRINT_CAUSE_OK = 0 // Normal +const SDCP_PRINT_CAUSE_TEMP_ERROR = 1 // Over-temperature +const SDCP_PRINT_CAUSE_CALIBRATE_FAILED = 2 // Strain Gauge Calibration Failed +const SDCP_PRINT_CAUSE_RESIN_LACK = 3 // Resin Level Low Detected +const SDCP_PRINT_CAUSE_RESIN_OVER = 4 // The volume of resin required by the model exceeds the maximum capacity of the resin vat +const SDCP_PRINT_CAUSE_PROBE_FAIL = 5 // No Resin Detected +const SDCP_PRINT_CAUSE_FOREIGN_BODY = 6 // Foreign Object Detected +const SDCP_PRINT_CAUSE_LEVEL_FAILED = 7 // Auto-leveling Failed +const SDCP_PRINT_CAUSE_RELEASE_FAILED = 8 // Model Detachment Detected +const SDCP_PRINT_CAUSE_SG_OFFLINE = 9 // Strain Gauge Not Connected +const SDCP_PRINT_CAUSE_LCD_DET_FAILED = 10 // LCD Screen Connection Abnormal +const SDCP_PRINT_CAUSE_RELEASE_OVERCOUNT = 11 // The cumulative release film usage has reached the maximum value +const SDCP_PRINT_CAUSE_UDISK_REMOVE = 12 // USB drive detected as removed, printing has been stopped +const SDCP_PRINT_CAUSE_HOME_FAILED_X = 13 // Detection of X-axis motor anomaly, printing has been stopped +const SDCP_PRINT_CAUSE_HOME_FAILED_Z = 14 // Detection of Z-axis motor anomaly, printing has been stopped +const SDCP_PRINT_CAUSE_RESIN_ABNORMAL_HIGH = 15 // The resin level has been detected to exceed the maximum value, and printing has been stopped +const SDCP_PRINT_CAUSE_RESIN_ABNORMAL_LOW = 16 // Resin level detected as too low, printing has been stopped +const SDCP_PRINT_CAUSE_HOME_FAILED = 17 // Home position calibration failed, please check if the motor or limit switch is functioning properly +const SDCP_PRINT_CAUSE_PLAT_FAILED = 18 // A model is detected on the platform; please clean it and then restart printing +const SDCP_PRINT_CAUSE_ERROR = 19 // Printing Exception +const SDCP_PRINT_CAUSE_MOVE_ABNORMAL = 20 // Motor Movement Abnormality +const SDCP_PRINT_CAUSE_AIC_MODEL_NONE = 21 // No model detected, please troubleshoot +const SDCP_PRINT_CAUSE_AIC_MODEL_WARP = 22 // Warping of the model detected, please investigate +const SDCP_PRINT_CAUSE_HOME_FAILED_Y = 23 // Deprecated +// PRINT_CAUSED +const SDCP_PRINT_CAUSED_FILE_ERROR = 24 // Error File +const SDCP_PRINT_CAUSED_CAMERA_ERROR = 25 // Camera Error. Please check if the camera is properly connected, or you can also disable this feature to continue printing +const SDCP_PRINT_CAUSED_NETWORK_ERROR = 26 // Network Connection Error. Please check if your network connection is stable, or you can also disable this feature to continue printing +const SDCP_PRINT_CAUSED_SERVER_CONNECT_FAILED = 27 // Server Connection Failed. Please contact our customer support, or you can also disable this feature to continue printing +const SDCP_PRINT_CAUSED_DISCONNECT_APP = 28 // This printer is not bound to an app. To perform time-lapse photography, please first enable the remote control feature, or you can also disable this feature to continue printing +const SDCP_PIRNT_CAUSED_CHECK_AUTO_RESIN_FEEDER = 29 // lease check the installation of the "automatic material extraction / feeding machine" +const SDCP_PRINT_CAUSED_CONTAINER_RESIN_LOW = 30 // The resin in the container is running low. Add more resin to automatically close this notification, or click "Stop Auto Feeding" to continue printing +const SDCP_PRINT_CAUSED_BOTTLE_DISCONNECT = 31 // Please ensure that the automatic material extraction/feeding machine is correctly installed and the data cable is connected +const SDCP_PRINT_CAUSED_FEED_TIMEOUT = 32 // Automatic material extraction timeout, please check if the resin tube is blocked +const SDCP_PRINT_CAUSE_TANK_TEMP_SENSOR_OFFLINE = 33 // Resin vat temperature sensor not connected +const SDCP_PRINT_CAUSE_TANK_TEMP_SENSOR_ERRO = 34 // Resin vat temperature sensor indicates an over-temperature condition +const SDCP_ERROR_CODE_MD5_FAILED = 1 // File Transfer MD5 Check Failed +const SDCP_ERROR_CODE_FORMAT_FAILED = 2 // File format is incorrect +// CMD +const SDCP_CMD_STATUS = 0 +const SDCP_CMD_ATTRIBUTES = 1 +const SDCP_CMD_START_PRINTING = 128 +const SDCP_CMD_PAUSE_PRINTING = 129 +const SDCP_CMD_STOP_PRINTING = 130 +const SDCP_CMD_CONTINUE_PRINTING = 131 +const SDCP_CMD_STOP_FEEDING = 132 +const SDCP_CMD_SKIP_PREHEATING = 133 +const SDCP_CMD_CHANGE_PRINTER_NAME = 192 // "Data": {"Name": "newName"} +const SDCP_CMD_TERMINATE_FILE_TRANSFER = 255 +const SDCP_CMD_RETRIEVE_FILE_LIST = 258 // "Data": {"Url": "/usb/path"} +const SDCP_CMD_BATCH_DELETE_FILES = 259 // "Data": {"FileList": ["/usb/xx.x"], "FolderList": ["/usb/yy"]} +const SDCP_CMD_RETRIEVE_TASKS_HISTORY = 320 +const SDCP_CMD_RETRIEVE_TASK_DETAILS = 321 // "Data": {"Id": ["xxxxxxxxxxx"]} +const SDCP_CMD_VIDEO_STREAMING = 386 // "Data": {"Enable" : 0/1} +const SDCP_CMD_TIMELAPSE = 387 // "Data": {"Enable" : 0/1} diff --git a/web/js/socket.io.min.js b/web/js/socket.io.min.js new file mode 100644 index 0000000000000000000000000000000000000000..d6b2d60110922529dec9eb8f31aa02a67df4dd68 --- /dev/null +++ b/web/js/socket.io.min.js @@ -0,0 +1,7 @@ +/*! + * Socket.IO v4.7.5 + * (c) 2014-2024 Guillermo Rauch + * Released under the MIT License. + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).io=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,(i=r.key,o=void 0,"symbol"==typeof(o=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(i,"string"))?o:String(o)),r)}var i,o}function r(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function i(){return i=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},i.apply(this,arguments)}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&a(e,t)}function s(e){return s=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},s(e)}function a(e,t){return a=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},a(e,t)}function c(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function u(e,t,n){return u=c()?Reflect.construct.bind():function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&a(i,n.prototype),i},u.apply(null,arguments)}function h(e){var t="function"==typeof Map?new Map:void 0;return h=function(e){if(null===e||(n=e,-1===Function.toString.call(n).indexOf("[native code]")))return e;var n;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return u(e,arguments,s(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),a(r,e)},h(e)}function f(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function l(e){var t=c();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return function(e,t){if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return f(e)}(this,n)}}function p(){return p="undefined"!=typeof Reflect&&Reflect.get?Reflect.get.bind():function(e,t,n){var r=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=s(e)););return e}(e,t);if(r){var i=Object.getOwnPropertyDescriptor(r,t);return i.get?i.get.call(arguments.length<3?e:n):i.value}},p.apply(this,arguments)}function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function y(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return d(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?d(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,i=function(){};return{s:i,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,a=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){a=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(a)throw o}}}}var v=Object.create(null);v.open="0",v.close="1",v.ping="2",v.pong="3",v.message="4",v.upgrade="5",v.noop="6";var g=Object.create(null);Object.keys(v).forEach((function(e){g[v[e]]=e}));var m,b={type:"error",data:"parser error"},k="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),w="function"==typeof ArrayBuffer,_=function(e){return"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer instanceof ArrayBuffer},E=function(e,t,n){var r=e.type,i=e.data;return k&&i instanceof Blob?t?n(i):A(i,n):w&&(i instanceof ArrayBuffer||_(i))?t?n(i):A(new Blob([i]),n):n(v[r]+(i||""))},A=function(e,t){var n=new FileReader;return n.onload=function(){var e=n.result.split(",")[1];t("b"+(e||""))},n.readAsDataURL(e)};function O(e){return e instanceof Uint8Array?e:e instanceof ArrayBuffer?new Uint8Array(e):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}for(var T="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",R="undefined"==typeof Uint8Array?[]:new Uint8Array(256),C=0;C<64;C++)R[T.charCodeAt(C)]=C;var B,S="function"==typeof ArrayBuffer,N=function(e,t){if("string"!=typeof e)return{type:"message",data:x(e,t)};var n=e.charAt(0);return"b"===n?{type:"message",data:L(e.substring(1),t)}:g[n]?e.length>1?{type:g[n],data:e.substring(1)}:{type:g[n]}:b},L=function(e,t){if(S){var n=function(e){var t,n,r,i,o,s=.75*e.length,a=e.length,c=0;"="===e[e.length-1]&&(s--,"="===e[e.length-2]&&s--);var u=new ArrayBuffer(s),h=new Uint8Array(u);for(t=0;t<a;t+=4)n=R[e.charCodeAt(t)],r=R[e.charCodeAt(t+1)],i=R[e.charCodeAt(t+2)],o=R[e.charCodeAt(t+3)],h[c++]=n<<2|r>>4,h[c++]=(15&r)<<4|i>>2,h[c++]=(3&i)<<6|63&o;return u}(e);return x(n,t)}return{base64:!0,data:e}},x=function(e,t){return"blob"===t?e instanceof Blob?e:new Blob([e]):e instanceof ArrayBuffer?e:e.buffer},P=String.fromCharCode(30);function j(){return new TransformStream({transform:function(e,t){!function(e,t){k&&e.data instanceof Blob?e.data.arrayBuffer().then(O).then(t):w&&(e.data instanceof ArrayBuffer||_(e.data))?t(O(e.data)):E(e,!1,(function(e){m||(m=new TextEncoder),t(m.encode(e))}))}(e,(function(n){var r,i=n.length;if(i<126)r=new Uint8Array(1),new DataView(r.buffer).setUint8(0,i);else if(i<65536){r=new Uint8Array(3);var o=new DataView(r.buffer);o.setUint8(0,126),o.setUint16(1,i)}else{r=new Uint8Array(9);var s=new DataView(r.buffer);s.setUint8(0,127),s.setBigUint64(1,BigInt(i))}e.data&&"string"!=typeof e.data&&(r[0]|=128),t.enqueue(r),t.enqueue(n)}))}})}function q(e){return e.reduce((function(e,t){return e+t.length}),0)}function D(e,t){if(e[0].length===t)return e.shift();for(var n=new Uint8Array(t),r=0,i=0;i<t;i++)n[i]=e[0][r++],r===e[0].length&&(e.shift(),r=0);return e.length&&r<e[0].length&&(e[0]=e[0].slice(r)),n}function U(e){if(e)return function(e){for(var t in U.prototype)e[t]=U.prototype[t];return e}(e)}U.prototype.on=U.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},U.prototype.once=function(e,t){function n(){this.off(e,n),t.apply(this,arguments)}return n.fn=t,this.on(e,n),this},U.prototype.off=U.prototype.removeListener=U.prototype.removeAllListeners=U.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n,r=this._callbacks["$"+e];if(!r)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var i=0;i<r.length;i++)if((n=r[i])===t||n.fn===t){r.splice(i,1);break}return 0===r.length&&delete this._callbacks["$"+e],this},U.prototype.emit=function(e){this._callbacks=this._callbacks||{};for(var t=new Array(arguments.length-1),n=this._callbacks["$"+e],r=1;r<arguments.length;r++)t[r-1]=arguments[r];if(n){r=0;for(var i=(n=n.slice(0)).length;r<i;++r)n[r].apply(this,t)}return this},U.prototype.emitReserved=U.prototype.emit,U.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},U.prototype.hasListeners=function(e){return!!this.listeners(e).length};var I="undefined"!=typeof self?self:"undefined"!=typeof window?window:Function("return this")();function F(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];return n.reduce((function(t,n){return e.hasOwnProperty(n)&&(t[n]=e[n]),t}),{})}var M=I.setTimeout,V=I.clearTimeout;function H(e,t){t.useNativeTimers?(e.setTimeoutFn=M.bind(I),e.clearTimeoutFn=V.bind(I)):(e.setTimeoutFn=I.setTimeout.bind(I),e.clearTimeoutFn=I.clearTimeout.bind(I))}var K,Y=function(e){o(i,e);var n=l(i);function i(e,r,o){var s;return t(this,i),(s=n.call(this,e)).description=r,s.context=o,s.type="TransportError",s}return r(i)}(h(Error)),W=function(e){o(i,e);var n=l(i);function i(e){var r;return t(this,i),(r=n.call(this)).writable=!1,H(f(r),e),r.opts=e,r.query=e.query,r.socket=e.socket,r}return r(i,[{key:"onError",value:function(e,t,n){return p(s(i.prototype),"emitReserved",this).call(this,"error",new Y(e,t,n)),this}},{key:"open",value:function(){return this.readyState="opening",this.doOpen(),this}},{key:"close",value:function(){return"opening"!==this.readyState&&"open"!==this.readyState||(this.doClose(),this.onClose()),this}},{key:"send",value:function(e){"open"===this.readyState&&this.write(e)}},{key:"onOpen",value:function(){this.readyState="open",this.writable=!0,p(s(i.prototype),"emitReserved",this).call(this,"open")}},{key:"onData",value:function(e){var t=N(e,this.socket.binaryType);this.onPacket(t)}},{key:"onPacket",value:function(e){p(s(i.prototype),"emitReserved",this).call(this,"packet",e)}},{key:"onClose",value:function(e){this.readyState="closed",p(s(i.prototype),"emitReserved",this).call(this,"close",e)}},{key:"pause",value:function(e){}},{key:"createUri",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e+"://"+this._hostname()+this._port()+this.opts.path+this._query(t)}},{key:"_hostname",value:function(){var e=this.opts.hostname;return-1===e.indexOf(":")?e:"["+e+"]"}},{key:"_port",value:function(){return this.opts.port&&(this.opts.secure&&Number(443!==this.opts.port)||!this.opts.secure&&80!==Number(this.opts.port))?":"+this.opts.port:""}},{key:"_query",value:function(e){var t=function(e){var t="";for(var n in e)e.hasOwnProperty(n)&&(t.length&&(t+="&"),t+=encodeURIComponent(n)+"="+encodeURIComponent(e[n]));return t}(e);return t.length?"?"+t:""}}]),i}(U),z="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),J=64,$={},Q=0,X=0;function G(e){var t="";do{t=z[e%J]+t,e=Math.floor(e/J)}while(e>0);return t}function Z(){var e=G(+new Date);return e!==K?(Q=0,K=e):e+"."+G(Q++)}for(;X<J;X++)$[z[X]]=X;var ee=!1;try{ee="undefined"!=typeof XMLHttpRequest&&"withCredentials"in new XMLHttpRequest}catch(e){}var te=ee;function ne(e){var t=e.xdomain;try{if("undefined"!=typeof XMLHttpRequest&&(!t||te))return new XMLHttpRequest}catch(e){}if(!t)try{return new(I[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(e){}}function re(){}var ie=null!=new ne({xdomain:!1}).responseType,oe=function(e){o(s,e);var n=l(s);function s(e){var r;if(t(this,s),(r=n.call(this,e)).polling=!1,"undefined"!=typeof location){var i="https:"===location.protocol,o=location.port;o||(o=i?"443":"80"),r.xd="undefined"!=typeof location&&e.hostname!==location.hostname||o!==e.port}var a=e&&e.forceBase64;return r.supportsBinary=ie&&!a,r.opts.withCredentials&&(r.cookieJar=void 0),r}return r(s,[{key:"name",get:function(){return"polling"}},{key:"doOpen",value:function(){this.poll()}},{key:"pause",value:function(e){var t=this;this.readyState="pausing";var n=function(){t.readyState="paused",e()};if(this.polling||!this.writable){var r=0;this.polling&&(r++,this.once("pollComplete",(function(){--r||n()}))),this.writable||(r++,this.once("drain",(function(){--r||n()})))}else n()}},{key:"poll",value:function(){this.polling=!0,this.doPoll(),this.emitReserved("poll")}},{key:"onData",value:function(e){var t=this;(function(e,t){for(var n=e.split(P),r=[],i=0;i<n.length;i++){var o=N(n[i],t);if(r.push(o),"error"===o.type)break}return r})(e,this.socket.binaryType).forEach((function(e){if("opening"===t.readyState&&"open"===e.type&&t.onOpen(),"close"===e.type)return t.onClose({description:"transport closed by the server"}),!1;t.onPacket(e)})),"closed"!==this.readyState&&(this.polling=!1,this.emitReserved("pollComplete"),"open"===this.readyState&&this.poll())}},{key:"doClose",value:function(){var e=this,t=function(){e.write([{type:"close"}])};"open"===this.readyState?t():this.once("open",t)}},{key:"write",value:function(e){var t=this;this.writable=!1,function(e,t){var n=e.length,r=new Array(n),i=0;e.forEach((function(e,o){E(e,!1,(function(e){r[o]=e,++i===n&&t(r.join(P))}))}))}(e,(function(e){t.doWrite(e,(function(){t.writable=!0,t.emitReserved("drain")}))}))}},{key:"uri",value:function(){var e=this.opts.secure?"https":"http",t=this.query||{};return!1!==this.opts.timestampRequests&&(t[this.opts.timestampParam]=Z()),this.supportsBinary||t.sid||(t.b64=1),this.createUri(e,t)}},{key:"request",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return i(e,{xd:this.xd,cookieJar:this.cookieJar},this.opts),new se(this.uri(),e)}},{key:"doWrite",value:function(e,t){var n=this,r=this.request({method:"POST",data:e});r.on("success",t),r.on("error",(function(e,t){n.onError("xhr post error",e,t)}))}},{key:"doPoll",value:function(){var e=this,t=this.request();t.on("data",this.onData.bind(this)),t.on("error",(function(t,n){e.onError("xhr poll error",t,n)})),this.pollXhr=t}}]),s}(W),se=function(e){o(i,e);var n=l(i);function i(e,r){var o;return t(this,i),H(f(o=n.call(this)),r),o.opts=r,o.method=r.method||"GET",o.uri=e,o.data=void 0!==r.data?r.data:null,o.create(),o}return r(i,[{key:"create",value:function(){var e,t=this,n=F(this.opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");n.xdomain=!!this.opts.xd;var r=this.xhr=new ne(n);try{r.open(this.method,this.uri,!0);try{if(this.opts.extraHeaders)for(var o in r.setDisableHeaderCheck&&r.setDisableHeaderCheck(!0),this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(o)&&r.setRequestHeader(o,this.opts.extraHeaders[o])}catch(e){}if("POST"===this.method)try{r.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(e){}try{r.setRequestHeader("Accept","*/*")}catch(e){}null===(e=this.opts.cookieJar)||void 0===e||e.addCookies(r),"withCredentials"in r&&(r.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(r.timeout=this.opts.requestTimeout),r.onreadystatechange=function(){var e;3===r.readyState&&(null===(e=t.opts.cookieJar)||void 0===e||e.parseCookies(r)),4===r.readyState&&(200===r.status||1223===r.status?t.onLoad():t.setTimeoutFn((function(){t.onError("number"==typeof r.status?r.status:0)}),0))},r.send(this.data)}catch(e){return void this.setTimeoutFn((function(){t.onError(e)}),0)}"undefined"!=typeof document&&(this.index=i.requestsCount++,i.requests[this.index]=this)}},{key:"onError",value:function(e){this.emitReserved("error",e,this.xhr),this.cleanup(!0)}},{key:"cleanup",value:function(e){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=re,e)try{this.xhr.abort()}catch(e){}"undefined"!=typeof document&&delete i.requests[this.index],this.xhr=null}}},{key:"onLoad",value:function(){var e=this.xhr.responseText;null!==e&&(this.emitReserved("data",e),this.emitReserved("success"),this.cleanup())}},{key:"abort",value:function(){this.cleanup()}}]),i}(U);if(se.requestsCount=0,se.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",ae);else if("function"==typeof addEventListener){addEventListener("onpagehide"in I?"pagehide":"unload",ae,!1)}function ae(){for(var e in se.requests)se.requests.hasOwnProperty(e)&&se.requests[e].abort()}var ce="function"==typeof Promise&&"function"==typeof Promise.resolve?function(e){return Promise.resolve().then(e)}:function(e,t){return t(e,0)},ue=I.WebSocket||I.MozWebSocket,he="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase(),fe=function(e){o(i,e);var n=l(i);function i(e){var r;return t(this,i),(r=n.call(this,e)).supportsBinary=!e.forceBase64,r}return r(i,[{key:"name",get:function(){return"websocket"}},{key:"doOpen",value:function(){if(this.check()){var e=this.uri(),t=this.opts.protocols,n=he?{}:F(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(n.headers=this.opts.extraHeaders);try{this.ws=he?new ue(e,t,n):t?new ue(e,t):new ue(e)}catch(e){return this.emitReserved("error",e)}this.ws.binaryType=this.socket.binaryType,this.addEventListeners()}}},{key:"addEventListeners",value:function(){var e=this;this.ws.onopen=function(){e.opts.autoUnref&&e.ws._socket.unref(),e.onOpen()},this.ws.onclose=function(t){return e.onClose({description:"websocket connection closed",context:t})},this.ws.onmessage=function(t){return e.onData(t.data)},this.ws.onerror=function(t){return e.onError("websocket error",t)}}},{key:"write",value:function(e){var t=this;this.writable=!1;for(var n=function(){var n=e[r],i=r===e.length-1;E(n,t.supportsBinary,(function(e){try{t.ws.send(e)}catch(e){}i&&ce((function(){t.writable=!0,t.emitReserved("drain")}),t.setTimeoutFn)}))},r=0;r<e.length;r++)n()}},{key:"doClose",value:function(){void 0!==this.ws&&(this.ws.close(),this.ws=null)}},{key:"uri",value:function(){var e=this.opts.secure?"wss":"ws",t=this.query||{};return this.opts.timestampRequests&&(t[this.opts.timestampParam]=Z()),this.supportsBinary||(t.b64=1),this.createUri(e,t)}},{key:"check",value:function(){return!!ue}}]),i}(W),le=function(e){o(i,e);var n=l(i);function i(){return t(this,i),n.apply(this,arguments)}return r(i,[{key:"name",get:function(){return"webtransport"}},{key:"doOpen",value:function(){var e=this;"function"==typeof WebTransport&&(this.transport=new WebTransport(this.createUri("https"),this.opts.transportOptions[this.name]),this.transport.closed.then((function(){e.onClose()})).catch((function(t){e.onError("webtransport error",t)})),this.transport.ready.then((function(){e.transport.createBidirectionalStream().then((function(t){var n=function(e,t){B||(B=new TextDecoder);var n=[],r=0,i=-1,o=!1;return new TransformStream({transform:function(s,a){for(n.push(s);;){if(0===r){if(q(n)<1)break;var c=D(n,1);o=128==(128&c[0]),i=127&c[0],r=i<126?3:126===i?1:2}else if(1===r){if(q(n)<2)break;var u=D(n,2);i=new DataView(u.buffer,u.byteOffset,u.length).getUint16(0),r=3}else if(2===r){if(q(n)<8)break;var h=D(n,8),f=new DataView(h.buffer,h.byteOffset,h.length),l=f.getUint32(0);if(l>Math.pow(2,21)-1){a.enqueue(b);break}i=l*Math.pow(2,32)+f.getUint32(4),r=3}else{if(q(n)<i)break;var p=D(n,i);a.enqueue(N(o?p:B.decode(p),t)),r=0}if(0===i||i>e){a.enqueue(b);break}}}})}(Number.MAX_SAFE_INTEGER,e.socket.binaryType),r=t.readable.pipeThrough(n).getReader(),i=j();i.readable.pipeTo(t.writable),e.writer=i.writable.getWriter();!function t(){r.read().then((function(n){var r=n.done,i=n.value;r||(e.onPacket(i),t())})).catch((function(e){}))}();var o={type:"open"};e.query.sid&&(o.data='{"sid":"'.concat(e.query.sid,'"}')),e.writer.write(o).then((function(){return e.onOpen()}))}))})))}},{key:"write",value:function(e){var t=this;this.writable=!1;for(var n=function(){var n=e[r],i=r===e.length-1;t.writer.write(n).then((function(){i&&ce((function(){t.writable=!0,t.emitReserved("drain")}),t.setTimeoutFn)}))},r=0;r<e.length;r++)n()}},{key:"doClose",value:function(){var e;null===(e=this.transport)||void 0===e||e.close()}}]),i}(W),pe={websocket:fe,webtransport:le,polling:oe},de=/^(?:(?![^:@\/?#]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@\/?#]*)(?::([^:@\/?#]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,ye=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];function ve(e){var t=e,n=e.indexOf("["),r=e.indexOf("]");-1!=n&&-1!=r&&(e=e.substring(0,n)+e.substring(n,r).replace(/:/g,";")+e.substring(r,e.length));for(var i,o,s=de.exec(e||""),a={},c=14;c--;)a[ye[c]]=s[c]||"";return-1!=n&&-1!=r&&(a.source=t,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a.pathNames=function(e,t){var n=/\/{2,9}/g,r=t.replace(n,"/").split("/");"/"!=t.slice(0,1)&&0!==t.length||r.splice(0,1);"/"==t.slice(-1)&&r.splice(r.length-1,1);return r}(0,a.path),a.queryKey=(i=a.query,o={},i.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(e,t,n){t&&(o[t]=n)})),o),a}var ge=function(n){o(a,n);var s=l(a);function a(n){var r,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return t(this,a),(r=s.call(this)).binaryType="arraybuffer",r.writeBuffer=[],n&&"object"===e(n)&&(o=n,n=null),n?(n=ve(n),o.hostname=n.host,o.secure="https"===n.protocol||"wss"===n.protocol,o.port=n.port,n.query&&(o.query=n.query)):o.host&&(o.hostname=ve(o.host).host),H(f(r),o),r.secure=null!=o.secure?o.secure:"undefined"!=typeof location&&"https:"===location.protocol,o.hostname&&!o.port&&(o.port=r.secure?"443":"80"),r.hostname=o.hostname||("undefined"!=typeof location?location.hostname:"localhost"),r.port=o.port||("undefined"!=typeof location&&location.port?location.port:r.secure?"443":"80"),r.transports=o.transports||["polling","websocket","webtransport"],r.writeBuffer=[],r.prevBufferLen=0,r.opts=i({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!1},o),r.opts.path=r.opts.path.replace(/\/$/,"")+(r.opts.addTrailingSlash?"/":""),"string"==typeof r.opts.query&&(r.opts.query=function(e){for(var t={},n=e.split("&"),r=0,i=n.length;r<i;r++){var o=n[r].split("=");t[decodeURIComponent(o[0])]=decodeURIComponent(o[1])}return t}(r.opts.query)),r.id=null,r.upgrades=null,r.pingInterval=null,r.pingTimeout=null,r.pingTimeoutTimer=null,"function"==typeof addEventListener&&(r.opts.closeOnBeforeunload&&(r.beforeunloadEventListener=function(){r.transport&&(r.transport.removeAllListeners(),r.transport.close())},addEventListener("beforeunload",r.beforeunloadEventListener,!1)),"localhost"!==r.hostname&&(r.offlineEventListener=function(){r.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",r.offlineEventListener,!1))),r.open(),r}return r(a,[{key:"createTransport",value:function(e){var t=i({},this.opts.query);t.EIO=4,t.transport=e,this.id&&(t.sid=this.id);var n=i({},this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port},this.opts.transportOptions[e]);return new pe[e](n)}},{key:"open",value:function(){var e,t=this;if(this.opts.rememberUpgrade&&a.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))e="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((function(){t.emitReserved("error","No transports available")}),0);e=this.transports[0]}this.readyState="opening";try{e=this.createTransport(e)}catch(e){return this.transports.shift(),void this.open()}e.open(),this.setTransport(e)}},{key:"setTransport",value:function(e){var t=this;this.transport&&this.transport.removeAllListeners(),this.transport=e,e.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(function(e){return t.onClose("transport close",e)}))}},{key:"probe",value:function(e){var t=this,n=this.createTransport(e),r=!1;a.priorWebsocketSuccess=!1;var i=function(){r||(n.send([{type:"ping",data:"probe"}]),n.once("packet",(function(e){if(!r)if("pong"===e.type&&"probe"===e.data){if(t.upgrading=!0,t.emitReserved("upgrading",n),!n)return;a.priorWebsocketSuccess="websocket"===n.name,t.transport.pause((function(){r||"closed"!==t.readyState&&(f(),t.setTransport(n),n.send([{type:"upgrade"}]),t.emitReserved("upgrade",n),n=null,t.upgrading=!1,t.flush())}))}else{var i=new Error("probe error");i.transport=n.name,t.emitReserved("upgradeError",i)}})))};function o(){r||(r=!0,f(),n.close(),n=null)}var s=function(e){var r=new Error("probe error: "+e);r.transport=n.name,o(),t.emitReserved("upgradeError",r)};function c(){s("transport closed")}function u(){s("socket closed")}function h(e){n&&e.name!==n.name&&o()}var f=function(){n.removeListener("open",i),n.removeListener("error",s),n.removeListener("close",c),t.off("close",u),t.off("upgrading",h)};n.once("open",i),n.once("error",s),n.once("close",c),this.once("close",u),this.once("upgrading",h),-1!==this.upgrades.indexOf("webtransport")&&"webtransport"!==e?this.setTimeoutFn((function(){r||n.open()}),200):n.open()}},{key:"onOpen",value:function(){if(this.readyState="open",a.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade)for(var e=0,t=this.upgrades.length;e<t;e++)this.probe(this.upgrades[e])}},{key:"onPacket",value:function(e){if("opening"===this.readyState||"open"===this.readyState||"closing"===this.readyState)switch(this.emitReserved("packet",e),this.emitReserved("heartbeat"),this.resetPingTimeout(),e.type){case"open":this.onHandshake(JSON.parse(e.data));break;case"ping":this.sendPacket("pong"),this.emitReserved("ping"),this.emitReserved("pong");break;case"error":var t=new Error("server error");t.code=e.data,this.onError(t);break;case"message":this.emitReserved("data",e.data),this.emitReserved("message",e.data)}}},{key:"onHandshake",value:function(e){this.emitReserved("handshake",e),this.id=e.sid,this.transport.query.sid=e.sid,this.upgrades=this.filterUpgrades(e.upgrades),this.pingInterval=e.pingInterval,this.pingTimeout=e.pingTimeout,this.maxPayload=e.maxPayload,this.onOpen(),"closed"!==this.readyState&&this.resetPingTimeout()}},{key:"resetPingTimeout",value:function(){var e=this;this.clearTimeoutFn(this.pingTimeoutTimer),this.pingTimeoutTimer=this.setTimeoutFn((function(){e.onClose("ping timeout")}),this.pingInterval+this.pingTimeout),this.opts.autoUnref&&this.pingTimeoutTimer.unref()}},{key:"onDrain",value:function(){this.writeBuffer.splice(0,this.prevBufferLen),this.prevBufferLen=0,0===this.writeBuffer.length?this.emitReserved("drain"):this.flush()}},{key:"flush",value:function(){if("closed"!==this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){var e=this.getWritablePackets();this.transport.send(e),this.prevBufferLen=e.length,this.emitReserved("flush")}}},{key:"getWritablePackets",value:function(){if(!(this.maxPayload&&"polling"===this.transport.name&&this.writeBuffer.length>1))return this.writeBuffer;for(var e,t=1,n=0;n<this.writeBuffer.length;n++){var r=this.writeBuffer[n].data;if(r&&(t+="string"==typeof(e=r)?function(e){for(var t=0,n=0,r=0,i=e.length;r<i;r++)(t=e.charCodeAt(r))<128?n+=1:t<2048?n+=2:t<55296||t>=57344?n+=3:(r++,n+=4);return n}(e):Math.ceil(1.33*(e.byteLength||e.size))),n>0&&t>this.maxPayload)return this.writeBuffer.slice(0,n);t+=2}return this.writeBuffer}},{key:"write",value:function(e,t,n){return this.sendPacket("message",e,t,n),this}},{key:"send",value:function(e,t,n){return this.sendPacket("message",e,t,n),this}},{key:"sendPacket",value:function(e,t,n,r){if("function"==typeof t&&(r=t,t=void 0),"function"==typeof n&&(r=n,n=null),"closing"!==this.readyState&&"closed"!==this.readyState){(n=n||{}).compress=!1!==n.compress;var i={type:e,data:t,options:n};this.emitReserved("packetCreate",i),this.writeBuffer.push(i),r&&this.once("flush",r),this.flush()}}},{key:"close",value:function(){var e=this,t=function(){e.onClose("forced close"),e.transport.close()},n=function n(){e.off("upgrade",n),e.off("upgradeError",n),t()},r=function(){e.once("upgrade",n),e.once("upgradeError",n)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(function(){e.upgrading?r():t()})):this.upgrading?r():t()),this}},{key:"onError",value:function(e){a.priorWebsocketSuccess=!1,this.emitReserved("error",e),this.onClose("transport error",e)}},{key:"onClose",value:function(e,t){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&(removeEventListener("beforeunload",this.beforeunloadEventListener,!1),removeEventListener("offline",this.offlineEventListener,!1)),this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this.prevBufferLen=0)}},{key:"filterUpgrades",value:function(e){for(var t=[],n=0,r=e.length;n<r;n++)~this.transports.indexOf(e[n])&&t.push(e[n]);return t}}]),a}(U);ge.protocol=4,ge.protocol;var me="function"==typeof ArrayBuffer,be=function(e){return"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):e.buffer instanceof ArrayBuffer},ke=Object.prototype.toString,we="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===ke.call(Blob),_e="function"==typeof File||"undefined"!=typeof File&&"[object FileConstructor]"===ke.call(File);function Ee(e){return me&&(e instanceof ArrayBuffer||be(e))||we&&e instanceof Blob||_e&&e instanceof File}function Ae(t,n){if(!t||"object"!==e(t))return!1;if(Array.isArray(t)){for(var r=0,i=t.length;r<i;r++)if(Ae(t[r]))return!0;return!1}if(Ee(t))return!0;if(t.toJSON&&"function"==typeof t.toJSON&&1===arguments.length)return Ae(t.toJSON(),!0);for(var o in t)if(Object.prototype.hasOwnProperty.call(t,o)&&Ae(t[o]))return!0;return!1}function Oe(e){var t=[],n=e.data,r=e;return r.data=Te(n,t),r.attachments=t.length,{packet:r,buffers:t}}function Te(t,n){if(!t)return t;if(Ee(t)){var r={_placeholder:!0,num:n.length};return n.push(t),r}if(Array.isArray(t)){for(var i=new Array(t.length),o=0;o<t.length;o++)i[o]=Te(t[o],n);return i}if("object"===e(t)&&!(t instanceof Date)){var s={};for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(s[a]=Te(t[a],n));return s}return t}function Re(e,t){return e.data=Ce(e.data,t),delete e.attachments,e}function Ce(t,n){if(!t)return t;if(t&&!0===t._placeholder){if("number"==typeof t.num&&t.num>=0&&t.num<n.length)return n[t.num];throw new Error("illegal attachments")}if(Array.isArray(t))for(var r=0;r<t.length;r++)t[r]=Ce(t[r],n);else if("object"===e(t))for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(t[i]=Ce(t[i],n));return t}var Be,Se=["connect","connect_error","disconnect","disconnecting","newListener","removeListener"];!function(e){e[e.CONNECT=0]="CONNECT",e[e.DISCONNECT=1]="DISCONNECT",e[e.EVENT=2]="EVENT",e[e.ACK=3]="ACK",e[e.CONNECT_ERROR=4]="CONNECT_ERROR",e[e.BINARY_EVENT=5]="BINARY_EVENT",e[e.BINARY_ACK=6]="BINARY_ACK"}(Be||(Be={}));var Ne=function(){function e(n){t(this,e),this.replacer=n}return r(e,[{key:"encode",value:function(e){return e.type!==Be.EVENT&&e.type!==Be.ACK||!Ae(e)?[this.encodeAsString(e)]:this.encodeAsBinary({type:e.type===Be.EVENT?Be.BINARY_EVENT:Be.BINARY_ACK,nsp:e.nsp,data:e.data,id:e.id})}},{key:"encodeAsString",value:function(e){var t=""+e.type;return e.type!==Be.BINARY_EVENT&&e.type!==Be.BINARY_ACK||(t+=e.attachments+"-"),e.nsp&&"/"!==e.nsp&&(t+=e.nsp+","),null!=e.id&&(t+=e.id),null!=e.data&&(t+=JSON.stringify(e.data,this.replacer)),t}},{key:"encodeAsBinary",value:function(e){var t=Oe(e),n=this.encodeAsString(t.packet),r=t.buffers;return r.unshift(n),r}}]),e}();function Le(e){return"[object Object]"===Object.prototype.toString.call(e)}var xe=function(e){o(i,e);var n=l(i);function i(e){var r;return t(this,i),(r=n.call(this)).reviver=e,r}return r(i,[{key:"add",value:function(e){var t;if("string"==typeof e){if(this.reconstructor)throw new Error("got plaintext data when reconstructing a packet");var n=(t=this.decodeString(e)).type===Be.BINARY_EVENT;n||t.type===Be.BINARY_ACK?(t.type=n?Be.EVENT:Be.ACK,this.reconstructor=new Pe(t),0===t.attachments&&p(s(i.prototype),"emitReserved",this).call(this,"decoded",t)):p(s(i.prototype),"emitReserved",this).call(this,"decoded",t)}else{if(!Ee(e)&&!e.base64)throw new Error("Unknown type: "+e);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");(t=this.reconstructor.takeBinaryData(e))&&(this.reconstructor=null,p(s(i.prototype),"emitReserved",this).call(this,"decoded",t))}}},{key:"decodeString",value:function(e){var t=0,n={type:Number(e.charAt(0))};if(void 0===Be[n.type])throw new Error("unknown packet type "+n.type);if(n.type===Be.BINARY_EVENT||n.type===Be.BINARY_ACK){for(var r=t+1;"-"!==e.charAt(++t)&&t!=e.length;);var o=e.substring(r,t);if(o!=Number(o)||"-"!==e.charAt(t))throw new Error("Illegal attachments");n.attachments=Number(o)}if("/"===e.charAt(t+1)){for(var s=t+1;++t;){if(","===e.charAt(t))break;if(t===e.length)break}n.nsp=e.substring(s,t)}else n.nsp="/";var a=e.charAt(t+1);if(""!==a&&Number(a)==a){for(var c=t+1;++t;){var u=e.charAt(t);if(null==u||Number(u)!=u){--t;break}if(t===e.length)break}n.id=Number(e.substring(c,t+1))}if(e.charAt(++t)){var h=this.tryParse(e.substr(t));if(!i.isPayloadValid(n.type,h))throw new Error("invalid payload");n.data=h}return n}},{key:"tryParse",value:function(e){try{return JSON.parse(e,this.reviver)}catch(e){return!1}}},{key:"destroy",value:function(){this.reconstructor&&(this.reconstructor.finishedReconstruction(),this.reconstructor=null)}}],[{key:"isPayloadValid",value:function(e,t){switch(e){case Be.CONNECT:return Le(t);case Be.DISCONNECT:return void 0===t;case Be.CONNECT_ERROR:return"string"==typeof t||Le(t);case Be.EVENT:case Be.BINARY_EVENT:return Array.isArray(t)&&("number"==typeof t[0]||"string"==typeof t[0]&&-1===Se.indexOf(t[0]));case Be.ACK:case Be.BINARY_ACK:return Array.isArray(t)}}}]),i}(U),Pe=function(){function e(n){t(this,e),this.packet=n,this.buffers=[],this.reconPack=n}return r(e,[{key:"takeBinaryData",value:function(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){var t=Re(this.reconPack,this.buffers);return this.finishedReconstruction(),t}return null}},{key:"finishedReconstruction",value:function(){this.reconPack=null,this.buffers=[]}}]),e}(),je=Object.freeze({__proto__:null,protocol:5,get PacketType(){return Be},Encoder:Ne,Decoder:xe});function qe(e,t,n){return e.on(t,n),function(){e.off(t,n)}}var De=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1}),Ue=function(e){o(a,e);var n=l(a);function a(e,r,o){var s;return t(this,a),(s=n.call(this)).connected=!1,s.recovered=!1,s.receiveBuffer=[],s.sendBuffer=[],s._queue=[],s._queueSeq=0,s.ids=0,s.acks={},s.flags={},s.io=e,s.nsp=r,o&&o.auth&&(s.auth=o.auth),s._opts=i({},o),s.io._autoConnect&&s.open(),s}return r(a,[{key:"disconnected",get:function(){return!this.connected}},{key:"subEvents",value:function(){if(!this.subs){var e=this.io;this.subs=[qe(e,"open",this.onopen.bind(this)),qe(e,"packet",this.onpacket.bind(this)),qe(e,"error",this.onerror.bind(this)),qe(e,"close",this.onclose.bind(this))]}}},{key:"active",get:function(){return!!this.subs}},{key:"connect",value:function(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}},{key:"open",value:function(){return this.connect()}},{key:"send",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.unshift("message"),this.emit.apply(this,t),this}},{key:"emit",value:function(e){if(De.hasOwnProperty(e))throw new Error('"'+e.toString()+'" is a reserved event name');for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];if(n.unshift(e),this._opts.retries&&!this.flags.fromQueue&&!this.flags.volatile)return this._addToQueue(n),this;var i={type:Be.EVENT,data:n,options:{}};if(i.options.compress=!1!==this.flags.compress,"function"==typeof n[n.length-1]){var o=this.ids++,s=n.pop();this._registerAckCallback(o,s),i.id=o}var a=this.io.engine&&this.io.engine.transport&&this.io.engine.transport.writable;return this.flags.volatile&&(!a||!this.connected)||(this.connected?(this.notifyOutgoingListeners(i),this.packet(i)):this.sendBuffer.push(i)),this.flags={},this}},{key:"_registerAckCallback",value:function(e,t){var n,r=this,i=null!==(n=this.flags.timeout)&&void 0!==n?n:this._opts.ackTimeout;if(void 0!==i){var o=this.io.setTimeoutFn((function(){delete r.acks[e];for(var n=0;n<r.sendBuffer.length;n++)r.sendBuffer[n].id===e&&r.sendBuffer.splice(n,1);t.call(r,new Error("operation has timed out"))}),i),s=function(){r.io.clearTimeoutFn(o);for(var e=arguments.length,n=new Array(e),i=0;i<e;i++)n[i]=arguments[i];t.apply(r,n)};s.withError=!0,this.acks[e]=s}else this.acks[e]=t}},{key:"emitWithAck",value:function(e){for(var t=this,n=arguments.length,r=new Array(n>1?n-1:0),i=1;i<n;i++)r[i-1]=arguments[i];return new Promise((function(n,i){var o=function(e,t){return e?i(e):n(t)};o.withError=!0,r.push(o),t.emit.apply(t,[e].concat(r))}))}},{key:"_addToQueue",value:function(e){var t,n=this;"function"==typeof e[e.length-1]&&(t=e.pop());var r={id:this._queueSeq++,tryCount:0,pending:!1,args:e,flags:i({fromQueue:!0},this.flags)};e.push((function(e){if(r===n._queue[0]){if(null!==e)r.tryCount>n._opts.retries&&(n._queue.shift(),t&&t(e));else if(n._queue.shift(),t){for(var i=arguments.length,o=new Array(i>1?i-1:0),s=1;s<i;s++)o[s-1]=arguments[s];t.apply(void 0,[null].concat(o))}return r.pending=!1,n._drainQueue()}})),this._queue.push(r),this._drainQueue()}},{key:"_drainQueue",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(this.connected&&0!==this._queue.length){var t=this._queue[0];t.pending&&!e||(t.pending=!0,t.tryCount++,this.flags=t.flags,this.emit.apply(this,t.args))}}},{key:"packet",value:function(e){e.nsp=this.nsp,this.io._packet(e)}},{key:"onopen",value:function(){var e=this;"function"==typeof this.auth?this.auth((function(t){e._sendConnectPacket(t)})):this._sendConnectPacket(this.auth)}},{key:"_sendConnectPacket",value:function(e){this.packet({type:Be.CONNECT,data:this._pid?i({pid:this._pid,offset:this._lastOffset},e):e})}},{key:"onerror",value:function(e){this.connected||this.emitReserved("connect_error",e)}},{key:"onclose",value:function(e,t){this.connected=!1,delete this.id,this.emitReserved("disconnect",e,t),this._clearAcks()}},{key:"_clearAcks",value:function(){var e=this;Object.keys(this.acks).forEach((function(t){if(!e.sendBuffer.some((function(e){return String(e.id)===t}))){var n=e.acks[t];delete e.acks[t],n.withError&&n.call(e,new Error("socket has been disconnected"))}}))}},{key:"onpacket",value:function(e){if(e.nsp===this.nsp)switch(e.type){case Be.CONNECT:e.data&&e.data.sid?this.onconnect(e.data.sid,e.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case Be.EVENT:case Be.BINARY_EVENT:this.onevent(e);break;case Be.ACK:case Be.BINARY_ACK:this.onack(e);break;case Be.DISCONNECT:this.ondisconnect();break;case Be.CONNECT_ERROR:this.destroy();var t=new Error(e.data.message);t.data=e.data.data,this.emitReserved("connect_error",t)}}},{key:"onevent",value:function(e){var t=e.data||[];null!=e.id&&t.push(this.ack(e.id)),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t))}},{key:"emitEvent",value:function(e){if(this._anyListeners&&this._anyListeners.length){var t,n=y(this._anyListeners.slice());try{for(n.s();!(t=n.n()).done;){t.value.apply(this,e)}}catch(e){n.e(e)}finally{n.f()}}p(s(a.prototype),"emit",this).apply(this,e),this._pid&&e.length&&"string"==typeof e[e.length-1]&&(this._lastOffset=e[e.length-1])}},{key:"ack",value:function(e){var t=this,n=!1;return function(){if(!n){n=!0;for(var r=arguments.length,i=new Array(r),o=0;o<r;o++)i[o]=arguments[o];t.packet({type:Be.ACK,id:e,data:i})}}}},{key:"onack",value:function(e){var t=this.acks[e.id];"function"==typeof t&&(delete this.acks[e.id],t.withError&&e.data.unshift(null),t.apply(this,e.data))}},{key:"onconnect",value:function(e,t){this.id=e,this.recovered=t&&this._pid===t,this._pid=t,this.connected=!0,this.emitBuffered(),this.emitReserved("connect"),this._drainQueue(!0)}},{key:"emitBuffered",value:function(){var e=this;this.receiveBuffer.forEach((function(t){return e.emitEvent(t)})),this.receiveBuffer=[],this.sendBuffer.forEach((function(t){e.notifyOutgoingListeners(t),e.packet(t)})),this.sendBuffer=[]}},{key:"ondisconnect",value:function(){this.destroy(),this.onclose("io server disconnect")}},{key:"destroy",value:function(){this.subs&&(this.subs.forEach((function(e){return e()})),this.subs=void 0),this.io._destroy(this)}},{key:"disconnect",value:function(){return this.connected&&this.packet({type:Be.DISCONNECT}),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}},{key:"close",value:function(){return this.disconnect()}},{key:"compress",value:function(e){return this.flags.compress=e,this}},{key:"volatile",get:function(){return this.flags.volatile=!0,this}},{key:"timeout",value:function(e){return this.flags.timeout=e,this}},{key:"onAny",value:function(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}},{key:"prependAny",value:function(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}},{key:"offAny",value:function(e){if(!this._anyListeners)return this;if(e){for(var t=this._anyListeners,n=0;n<t.length;n++)if(e===t[n])return t.splice(n,1),this}else this._anyListeners=[];return this}},{key:"listenersAny",value:function(){return this._anyListeners||[]}},{key:"onAnyOutgoing",value:function(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.push(e),this}},{key:"prependAnyOutgoing",value:function(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.unshift(e),this}},{key:"offAnyOutgoing",value:function(e){if(!this._anyOutgoingListeners)return this;if(e){for(var t=this._anyOutgoingListeners,n=0;n<t.length;n++)if(e===t[n])return t.splice(n,1),this}else this._anyOutgoingListeners=[];return this}},{key:"listenersAnyOutgoing",value:function(){return this._anyOutgoingListeners||[]}},{key:"notifyOutgoingListeners",value:function(e){if(this._anyOutgoingListeners&&this._anyOutgoingListeners.length){var t,n=y(this._anyOutgoingListeners.slice());try{for(n.s();!(t=n.n()).done;){t.value.apply(this,e.data)}}catch(e){n.e(e)}finally{n.f()}}}}]),a}(U);function Ie(e){e=e||{},this.ms=e.min||100,this.max=e.max||1e4,this.factor=e.factor||2,this.jitter=e.jitter>0&&e.jitter<=1?e.jitter:0,this.attempts=0}Ie.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),n=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-n:e+n}return 0|Math.min(e,this.max)},Ie.prototype.reset=function(){this.attempts=0},Ie.prototype.setMin=function(e){this.ms=e},Ie.prototype.setMax=function(e){this.max=e},Ie.prototype.setJitter=function(e){this.jitter=e};var Fe=function(n){o(s,n);var i=l(s);function s(n,r){var o,a;t(this,s),(o=i.call(this)).nsps={},o.subs=[],n&&"object"===e(n)&&(r=n,n=void 0),(r=r||{}).path=r.path||"/socket.io",o.opts=r,H(f(o),r),o.reconnection(!1!==r.reconnection),o.reconnectionAttempts(r.reconnectionAttempts||1/0),o.reconnectionDelay(r.reconnectionDelay||1e3),o.reconnectionDelayMax(r.reconnectionDelayMax||5e3),o.randomizationFactor(null!==(a=r.randomizationFactor)&&void 0!==a?a:.5),o.backoff=new Ie({min:o.reconnectionDelay(),max:o.reconnectionDelayMax(),jitter:o.randomizationFactor()}),o.timeout(null==r.timeout?2e4:r.timeout),o._readyState="closed",o.uri=n;var c=r.parser||je;return o.encoder=new c.Encoder,o.decoder=new c.Decoder,o._autoConnect=!1!==r.autoConnect,o._autoConnect&&o.open(),o}return r(s,[{key:"reconnection",value:function(e){return arguments.length?(this._reconnection=!!e,this):this._reconnection}},{key:"reconnectionAttempts",value:function(e){return void 0===e?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}},{key:"reconnectionDelay",value:function(e){var t;return void 0===e?this._reconnectionDelay:(this._reconnectionDelay=e,null===(t=this.backoff)||void 0===t||t.setMin(e),this)}},{key:"randomizationFactor",value:function(e){var t;return void 0===e?this._randomizationFactor:(this._randomizationFactor=e,null===(t=this.backoff)||void 0===t||t.setJitter(e),this)}},{key:"reconnectionDelayMax",value:function(e){var t;return void 0===e?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,null===(t=this.backoff)||void 0===t||t.setMax(e),this)}},{key:"timeout",value:function(e){return arguments.length?(this._timeout=e,this):this._timeout}},{key:"maybeReconnectOnOpen",value:function(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}},{key:"open",value:function(e){var t=this;if(~this._readyState.indexOf("open"))return this;this.engine=new ge(this.uri,this.opts);var n=this.engine,r=this;this._readyState="opening",this.skipReconnect=!1;var i=qe(n,"open",(function(){r.onopen(),e&&e()})),o=function(n){t.cleanup(),t._readyState="closed",t.emitReserved("error",n),e?e(n):t.maybeReconnectOnOpen()},s=qe(n,"error",o);if(!1!==this._timeout){var a=this._timeout,c=this.setTimeoutFn((function(){i(),o(new Error("timeout")),n.close()}),a);this.opts.autoUnref&&c.unref(),this.subs.push((function(){t.clearTimeoutFn(c)}))}return this.subs.push(i),this.subs.push(s),this}},{key:"connect",value:function(e){return this.open(e)}},{key:"onopen",value:function(){this.cleanup(),this._readyState="open",this.emitReserved("open");var e=this.engine;this.subs.push(qe(e,"ping",this.onping.bind(this)),qe(e,"data",this.ondata.bind(this)),qe(e,"error",this.onerror.bind(this)),qe(e,"close",this.onclose.bind(this)),qe(this.decoder,"decoded",this.ondecoded.bind(this)))}},{key:"onping",value:function(){this.emitReserved("ping")}},{key:"ondata",value:function(e){try{this.decoder.add(e)}catch(e){this.onclose("parse error",e)}}},{key:"ondecoded",value:function(e){var t=this;ce((function(){t.emitReserved("packet",e)}),this.setTimeoutFn)}},{key:"onerror",value:function(e){this.emitReserved("error",e)}},{key:"socket",value:function(e,t){var n=this.nsps[e];return n?this._autoConnect&&!n.active&&n.connect():(n=new Ue(this,e,t),this.nsps[e]=n),n}},{key:"_destroy",value:function(e){for(var t=0,n=Object.keys(this.nsps);t<n.length;t++){var r=n[t];if(this.nsps[r].active)return}this._close()}},{key:"_packet",value:function(e){for(var t=this.encoder.encode(e),n=0;n<t.length;n++)this.engine.write(t[n],e.options)}},{key:"cleanup",value:function(){this.subs.forEach((function(e){return e()})),this.subs.length=0,this.decoder.destroy()}},{key:"_close",value:function(){this.skipReconnect=!0,this._reconnecting=!1,this.onclose("forced close"),this.engine&&this.engine.close()}},{key:"disconnect",value:function(){return this._close()}},{key:"onclose",value:function(e,t){this.cleanup(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect()}},{key:"reconnect",value:function(){var e=this;if(this._reconnecting||this.skipReconnect)return this;var t=this;if(this.backoff.attempts>=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{var n=this.backoff.duration();this._reconnecting=!0;var r=this.setTimeoutFn((function(){t.skipReconnect||(e.emitReserved("reconnect_attempt",t.backoff.attempts),t.skipReconnect||t.open((function(n){n?(t._reconnecting=!1,t.reconnect(),e.emitReserved("reconnect_error",n)):t.onreconnect()})))}),n);this.opts.autoUnref&&r.unref(),this.subs.push((function(){e.clearTimeoutFn(r)}))}}},{key:"onreconnect",value:function(){var e=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",e)}}]),s}(U),Me={};function Ve(t,n){"object"===e(t)&&(n=t,t=void 0);var r,i=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,r=e;n=n||"undefined"!=typeof location&&location,null==e&&(e=n.protocol+"//"+n.host),"string"==typeof e&&("/"===e.charAt(0)&&(e="/"===e.charAt(1)?n.protocol+e:n.host+e),/^(https?|wss?):\/\//.test(e)||(e=void 0!==n?n.protocol+"//"+e:"https://"+e),r=ve(e)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var i=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+i+":"+r.port+t,r.href=r.protocol+"://"+i+(n&&n.port===r.port?"":":"+r.port),r}(t,(n=n||{}).path||"/socket.io"),o=i.source,s=i.id,a=i.path,c=Me[s]&&a in Me[s].nsps;return n.forceNew||n["force new connection"]||!1===n.multiplex||c?r=new Fe(o,n):(Me[s]||(Me[s]=new Fe(o,n)),r=Me[s]),i.query&&!n.query&&(n.query=i.queryKey),r.socket(i.path,n)}return i(Ve,{Manager:Fe,Socket:Ue,io:Ve,connect:Ve}),Ve})); +//# sourceMappingURL=socket.io.min.js.map