Javascript server html not loading js client script (socket.io express html

The basic issue is that when I try to remove the js code from being directly in the html file (everything works 100% perfectly with js directly in html, but as I try to add components and things become more complex, I want to have multiple html and js files for the front end) to a separate js file, and I load the server localhost page, it cannot find the js file. I understand that I have:

  • maindir/
    • client/
      • index.html
      • client.js
    • server.js
    • {other files that server.js uses for backend stuff}

And that when I start a server and navigate to localhost:3000, the app.get -> res.sendfile passes the html file to the user on the webpage and that the js file cannot be found, given that the cwd of the webpage is not the maindir/client/ dir, but I can’t figure out how to get the js file to the client through the server without directly including the code in the html file. Screenshots below:

server.js code of interest:

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);


const { Server } = require("socket.io");
const io = new Server(server);
// const io = require("socket.io")(3000, {
//    cors: {
//       origin: ['http://localhost:3000']
//    }
// })

const lib = require('./library');
const dr = require('./Driver');

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/client/index.html');
//   res.sendFile(__dirname + '/client/client.js');
//   res.sendFile(__dirname + '/client/photo_server.jpg');
});

var users = [];
var n = 0;
var data;

io.on('connection', (socket) => {

client.js code of interest:

// let msg = "pre anything";
// var item = document.createElement('li');
// item.textContent = msg;
// messages.appendChild(item);
// window.scrollTo(0, document.body.scrollHeight);

// ES6 import or TypeScript
import { io } from "socket.io-client";
// CommonJS
// const io = require("socket.io-client");
 // start javascript section
// var socket = io('http://localhost:3000/');
var socket = io();
// const socket = io.connect("http://localhost:3001");

// msg = "post socket";
// var item = document.createElement('li');
// item.textContent = msg;
// messages.appendChild(item);
// window.scrollTo(0, document.body.scrollHeight);
   
 // !!!!!!!!!!!!!!!!!!
 // START input
 // !!!!!!!!!!!!!!!!!!

 // this will submit the chat via emit to server (from 1 user)
 var form = document.getElementById('form');
 var input = document.getElementById('input');

 form.addEventListener('submit', function(e) {
 e.preventDefault();

html file

browser error when running server with code in separate file

I tried what was mentioned above

integration python JS (FastAPI)

The code is about receiving information from the user’s age, gender, etc., in order to predict the best price through a comparison between the three algorithms. i have problem with API between JS and python.
it is not work

the website show the best cost and save information into database
JS

const predictionForm = document.getElementById("predictionForm");
const result = document.getElementById("result");
const amount = document.querySelectorAll("span");
let welcomeName = document.getElementById("name");
const prophesyBtn = document.getElementById("prophesyCost");

predictionForm.addEventListener("submit", (event) => {
  resultVisibility();
  handleFormSubmit(event);
});

// name of the user
let currName = "setah";
welcomeName.innerText = currName;

// Display the amount
currAmount = "10,459";
amount.forEach((el) => (el.innerText = currAmount));

// The prediction result visibility
function resultVisibility() {
  result.style.display = "flex";
  prophesyBtn.style.display = "none";
}

function handleFormSubmit(event) {
  event.preventDefault(); // Prevent default form submission

  // Access form data and build JSON object
  const data = {
    children: document.getElementById("children").value,
    age: document.getElementById("age").value,
    sex: document.getElementById("sex").value,
    bmi: document.getElementById("bmi").value,
    region: document.getElementById("region").value,
  };

  fetchPrediction(data);
  fetch('model/Medical Insurance.py')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error))
}type here

python machine learning

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import sqlite3
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVR
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
import pickle
from sklearn.metrics import mean_squared_error
import tensorflow as tf
from tensorflow import keras
import os

app = FastAPI()

df = pd.read_csv("insurance.csv")

df['sex'] = df['sex'].apply(lambda x: 0 if x == 'male' else 1)
df['smoker'] = df['smoker'].apply(lambda x: 1 if x == 'yes' else 0)
df['region'] = df['region'].apply({'southwest': 1, 'southeast': 2, 'northwest': 3, 'northeast': 4}.get)

X = df[['age', 'sex', 'bmi', 'children', 'smoker', 'region']]
y = df['charges']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

linear_regression_model = LinearRegression()
svr_model = SVR()
decision_tree_model = DecisionTreeRegressor()

linear_regression_model.fit(X_train, y_train)
svr_model.fit(X_train, y_train)
decision_tree_model.fit(X_train, y_train)

linear_regression_predictions = linear_regression_model.predict(X_test)
svr_predictions = svr_model.predict(X_test)
decision_tree_predictions = decision_tree_model.predict(X_test)

model = keras.models.Sequential([
    keras.layers.Dense(64, activation='relu', input_shape=(6,)),
    keras.layers.Dense(64, activation='relu'),
    keras.layers.Dense(1)
])

model.compile(optimizer='adam', loss='mean_squared_error', metrics=['mae'])
model.fit(X_train, y_train, epochs=10, batch_size=32, verbose=0)

with open('linear_regression_model.pkl', 'wb') as f:
    pickle.dump(linear_regression_model, f)

with open('svr_model.pkl', 'wb') as f:
    pickle.dump(svr_model, f)

with open('decision_tree_model.pkl', 'wb') as f:
    pickle.dump(decision_tree_model, f)

model_json = model.to_json()
with open("model.json", "w") as json_file:
    json_file.write(model_json)

data = {'age': 19, 'sex': 1, 'bmi': 27.9, 'children': 0, 'smoker': 1, 'region': 1}
cust_df = pd.DataFrame(data, index=[0])
cust_df = cust_df[['age', 'sex', 'bmi', 'children', 'smoker', 'region']]

cost_pred_linear_regression = linear_regression_model.predict(cust_df)
cost_pred_svr = svr_model.predict(cust_df)
cost_pred_decision_tree = decision_tree_model.predict(cust_df)

linear_regression_mse = mean_squared_error(y_test, linear_regression_predictions)
svr_mse = mean_squared_error(y_test, svr_predictions)
decision_tree_mse = mean_squared_error(y_test, decision_tree_predictions)

conn = sqlite3.connect('prediction.db')
c = conn.cursor()

c.execute('''CREATE TABLE IF NOT EXISTS predictions
             (age INTEGER, sex INTEGER, bmi float, children INTEGER, smoker INTEGER, region INTEGER,
              linear_regression_prediction float, svr_prediction float, decision_tree_prediction float, best_model TEXT, email TEXT, name TEXT)''')

@app.post('/predict')
async def predict(request: Request):
    input_data = await request.json()

    if not all(key in input_data for key in ['age', 'sex', 'bmi', 'children', 'smoker', 'region', 'name', 'email']):
        raise HTTPException(status_code=400, detail="Some keys are missing in the input data.")

    age = input_data['age']
    sex = input_data['sex']
    bmi = input_data['bmi']
    children = input_data['children']
    smoker = input_data['smoker']
    region = input_data['region']
    name = input_data['name']
    email = input_data['email']

    input_data_array = np.array([[age, sex, bmi, children, smoker, region]])
    linear_regression_prediction = linear_regression_model.predict(input_data_array)[0]
    svr_prediction = svr_model.predict(input_data_array)[0]
    decision_tree_prediction = decision_tree_model.predict(input_data_array)[0]

    best_model = None
    best_prediction = None

    if linear_regression_mse <= svr_mse and linear_regression_mse <= decision_tree_mse:
        best_model = 'Linear Regression'
        best_prediction = linear_regression_model.predict(X_test)
    elif svr_mse <= linear_regression_mse and svr_mse <= decision_tree_mse:
        best_model = 'Support Vector Regression'
        best_prediction = svr_model.predict(X_test)
    else:
        best_model = 'Decision Tree'
        best_prediction = decision_tree_model.predict(X_test)

    c.execute('''INSERT INTO predictions (age, sex, bmi, children, smoker, region,
                 linear_regression_prediction, svr_prediction, decision_tree_prediction, best_model, email, name)
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
              (age, sex, bmi, children, smoker, region,
               linear_regression_prediction, svr_prediction, decision_tree_prediction, best_model, email, name))

    conn.commit()

    return JSONResponse({'message': 'Prediction successfully recorded'})

if __name__ == '__main__':
    origins = [
        "http://localhost",
        "http://localhost:8000",
        "http://localhost:8080",
    ]

    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"]
    )

    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)type here

main code

import pickle
import uvicorn
import numpy as np
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware


app = FastAPI()


origins = [
    "http://localhost",
    "http://localhost:8000",
    "http://localhost:8080",
]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


with open("model/decision_tree_model.pkl", "rb") as f:
    decision_tree_model = pickle.load(f)

with open("model/linear_regression_model.pkl", "rb") as f:
    linear_regression_model = pickle.load(f)

with open("model/svr_model.pkl", "rb") as f:
    svr_model = pickle.load(f)


@app.post("/predict")
async def predict(age: int, sex: int, bmi: float, children: int, smoker: int, region: int):

 
    input_data_array = np.array([[age, sex, bmi, children, smoker, region]])

    decision_tree_predicted_cost = decision_tree_model.predict(input_data_array)[0]
    linear_regression_predicted_cost = linear_regression_model.predict(input_data_array)[0]
    svr_predicted_cost = svr_model.predict(input_data_array)[0]

   
    return JSONResponse({
        "decision_tree_predicted_cost": round(decision_tree_predicted_cost, 2),
        "linear_regression_predicted_cost": round(linear_regression_predicted_cost, 2),
        "svr_predicted_cost": round(svr_predicted_cost, 2)
    })


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)type here

Mat Accordion multi=false with 2 panels A and B. How to make A open when keyup.ENTER event occurs in B?

This should be simple but I believe its being complicated by the fact that the Panel B contains an ACE editor control.

I’m capturing the (keyup.ENTER) event in the Ace editor control as well as in its parent Panel B expansion-panel container in order to (1) Collapse the Panel B that contains the ACE editor, and (2) open/expand the Panel A that contains the preview that is built from the ACE editor’s html content.

Maybe I’m overthinking this but although I can log out the keyup.ENTER event from the ACE editor as well as the keyup.ENTER event from its parent mat-expansion-panel, I’m unable to get the panel B to close and panel A to open as a result of this event.

My console.log shows that the value of the expanded variables are changing, but the expansion panels do not seem to want to cooperate.

Here is the template code:

<mat-accordion multi="false">
    <!--PANEL A-->
      <mat-expansion-panel 
        *ngIf="dragDropMode"
        #panelA
        class="canvas-panel" 
        [expanded]="dragDropMode || editorPanelExpanded"
        (opened)="setEditorPanel(true)"
      >
      <mat-expansion-panel-header>
        <mat-panel-title>Panel A</mat-panel-title>
        <mat-panel-description></mat-panel-description>
      </mat-expansion-panel-header>
      <ng-template matExpansionPanelContent>
        <mat-card>
          <mat-accordion [multi]="multiAccordion">
            Panel A
          </mat-accordion>
        </mat-card>
      </ng-template>
    </mat-expansion-panel>

    <!-- PANEL B -->
    <mat-expansion-panel
      *ngIf="ifDeveloper && (developerMode$ | async)"
      #panelB
      class="model-panel"
      (opened)="setEditorPanel(false, 'Panel B Opened')"
      (keyup.ENTER)="setEditorPanel(true, 'ACE PARENT keyup.ENTER')"
    >
      <mat-expansion-panel-header>
        <mat-panel-title>Panel B</mat-panel-title>
        <mat-panel-description></mat-panel-description>
      </mat-expansion-panel-header>
      <ng-template matExpansionPanelContent>
        <div
          ace-editor
          *ngIf="panelB.expanded"
          [text]="text$ | async"
          [options]="aceEditorOptions"
          [autoUpdateContent]="true"
          (textChanged)="updateModelFromEditor($event)"
          class="ace-editor-container"
          (keyup.ENTER)="setEditorPanel(true, 'ACE keyup.ENTER')"
        ></div>
      </ng-template>
  </mat-expansion-panel>
</mat-accordion>

And here is the typescript class code:

 editorPanelExpanded = true;

 setEditorPanel(value: boolean, from = '') {
   this.editorPanelExpanded = value;
   console.log(`setEditorPanelCalled from ${from} with value: ${value}`);
 }

At launch of the app, the Panel A is open. I then click on Panel B to auto expand it, which automatically causes Panel A to close. Then I select some text inside the ACE editor in Panel B and click ENTER. At that point I get the console.log as expected showing the variables changed, but Panel B remains open and Panel A remains closed. I must be violating some rule of the mat-accordion in this instance but I can’t seem to figure what. Can someone suggest an alternate approach perhaps or tell me where I’m going wrong with my current approach?

Google Apps Script: doGet() function always returns {“result”:”NoUserRecordFound”}

I’m working on a Google Apps Script project where I have a doGet() function that’s supposed to update user records in a Google Spreadsheet. However, it always returns {“result”:”NoUserRecordFound”} even when I expect it to update existing records.

Here’s a simplified version of my doGet() function:

function doGet(e) {
  try {
    // Extract parameters from the request
    const email = getEmailParameter(e);
    const id = getIDParameter(e);
    let count = getCountParameter(e);

    // Get the active spreadsheet and data
    const sheet = getSheet();
    const data = getData(sheet);

    // Find the index of the existing row
    const existingRowIndex = findExistingRowIndex(data, email, id);

    if (existingRowIndex !== -1) {
      updateExistingRow(sheet, data, existingRowIndex, count);
      const updatedRow = data[existingRowIndex];
      const result = checkExpiryDate(updatedRow[3]);
      return createResponse(result);
    } else {
      addNewRow(sheet, email, id, count);
      return createResponse('NoUserRecordFound');
    }
  } catch (error) {
    return createErrorResponse(error);
  }
}

Despite having existing records with the provided email and id, the function always returns {“result”:”NoUserRecordFound”}. I’ve added console.log() statements for debugging, but they don’t seem to provide any useful information.

here my complete code and Data Sheet

And This is dumy data request link

I tried adding logging statements using console.log() throughout the doGet() function to debug the code and understand its behavior.

Erros in Metafields

I have some difficulty in updating an existing metafield with Shopify API. Each time, I received an error, advising me that the variant already exists… so it’s not recognized to Update.

I thought this may be an issue with ‘put’ and ‘post’ – so changed my method to put, however the error persists. I’ve hardwired in all my variables to make it easier to test.

I’m working with Cloudinary. I’m using https://github.com/sinechris/shopify-node-api with Express.js

app.post('/upload', function(req, res){
  // upload page... assume we have uploaded our image - but have hard-wired a local file in     for now
cloudinary.uploader.upload('/Users/Rob/Pictures/testimg.jpg', function(savedImg) { 
  var imageURL = savedImg.url;
  console.log(imageURL)
},
{
 public_id: "testimg"
});


//  the saved image is returned - so we add it to our updateMetafieldData json object
   var updateMetafieldData = {
   "variant": {
      "id": '253818949',
      "metafields": [
        {
          "key": "variant_image_0",
          "value": 'testimg', // whatever the public id of our image is.
          "value_type": "string",
          "namespace": "variant_image"
        }
      ]
    }
  }
// and post the result to shopify - then redirect to /getvariants
Shopify.put('/admin/variants/253818949.json', updateMetafieldData, function(data){
  // res.redirect('/getvariants')
});

});
advising me that the variant already exists*

Sequelize model entry not creating in discord.js bot

I got a problem I can’t figure out. I’m currently developing a bot in discord.js using Sequelize, and I was following the discord.js.org guide (cause I’m new to v14). Anyways, I want to have a database with entries of every user on every guild, and for now I’m doing:

  1. When a new member enters, I create a new entry for them. If they already have one, ignore and move on.
  2. I also created a command that syncs every member. So basically it goes through every member, checks if there’s an entry, if not create one.

Now here’s the problem. In the command I defined the model, then used it to create an entry, using model.create()
However, when I run it it says that model.create() isn't a function.

Here’s the code, take a look.

Command code:

const { SlashCommandBuilder } = require('discord.js');
const Users = require('../../models/user.js')

module.exports = {
    data: new SlashCommandBuilder()
        .setName('syncuserdata')
        .setDescription('Syncs the member data with the db'),
    async execute(interaction) {
        const guild = interaction.guild
        guild.members.cache.forEach(async (member) => {
            if (member.user.bot) return
            const memberId = member.id
            try {
                console.log(Users)
                const userEntry = await Users.create({
                    user_id: memberId,
                    guild_id: member.guild.id
                })
                console.log(`[INFO] New user entry created for ${memberId}.`)
            } catch (error) {
                if (error.name === 'SequelizeUniqueConstraintError') {
                    const userEntryFetch = await Users.findOne({ where: { user_id: memberId } })
                    if (!userEntryFetch) console.error("[ERR] A user entry exists but could not be found.")
                }

                console.error(`[ERR] An error occured while creating a userEntry for ${memberId}: ${error}`)
                interaction.channel.send(`Something went wrong while trying to sync for <@${memberId}>!`)
            }
        });
        interaction.reply({
            content: 'User data sync log:',
            ephemeral: false,
        })
    },
};

Database sync file:

const Sequelize = require('sequelize');
const dbUserData = require('./events/dbUserData.js');

const sequelize = new Sequelize('database', 'username', 'password', {
    host: 'localhost',
    dialect: 'sqlite',
    logging: false,
    storage: 'database.sqlite',
});

const User = require('./models/user.js')(sequelize, Sequelize.DataTypes);

const force = process.argv.includes('--force') || process.argv.includes('-f');

sequelize.sync({ force }).then(async () => {
    console.log('[INFO] Database synced');

    sequelize.close();
}).catch(console.error);

Entire index.js:

// File handler init
const fs = require('node:fs');
// Path handler init
const path = require('node:path');
// Discord.js init
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
// Sequelize init
const Sequelize = require('sequelize')
// dotenv start and config
require('dotenv').config()
// Config file init
const { clientId, guildId, testPhase } = require('./config.json');
// Init both command deployments
require('./guilddeploycmd.js')
require('./globaldeploycmd.js')
const Users = require('./models/user.js')

// Create a new client instance
const client = new Client({ intents: 131071 });

const sequelize = new Sequelize('database', 'user', 'password', {
    host: 'localhost',
    dialect: 'sqlite',
    logging: false,
    // SQLite only
    storage: 'database.sqlite',
});

// **Command collector**

client.commands = new Collection();

// Construct path into commands folder
const foldersPath = path.join(__dirname, 'commands');
// Gets all command subfolders
const commandFolders = fs.readdirSync(foldersPath);

for (const folder of commandFolders) {
    const commandsPath = path.join(foldersPath, folder);
    const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
    for (const file of commandFiles) {
        const filePath = path.join(commandsPath, file);
        const command = require(filePath);
        if ('data' in command && 'execute' in command) {
            client.commands.set(command.data.name, command);
        } else {
            console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
        }
    }
}

// ** END **

// When the client is ready, run this code (only once).
client.once(Events.ClientReady, readyClient => {
    console.log(`[INFO] Logged in as ${readyClient.user.tag}`);
});

// Event handler
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));

for (const file of eventFiles) {
    const filePath = path.join(eventsPath, file);
    const event = require(filePath);
    if (event.once) {
        client.once(event.name, (...args) => event.execute(...args));
    } else {
        client.on(event.name, (...args) => event.execute(...args));
    }
}

// Log in to Discord with your client's token
client.login(process.env.TOKEN);

Would really appreciate it if someone could help me. Thanks in advance!

Just some problems dynamic links

basicily im having problems with making the link to comments dynamic. my goal is that no matter what number i put in it the get will enter my call and give me the id i want the call looks like http://localhost:1234/comments?id=num but im not finding anything that can let my call be dynamic any help is grateful

server.js
    
    (async () => {
        const db = require("./database");
        const users = await db.selectUsers();
        console.log(users);
        /*const assoc = await db.selectAssoc();
        console.log(assoc);*/
        const processRequest = require('./processRequest');
        const aplicacao = processRequest(1234);
        aplicacao.get('/users',function(req,res){
            res.write(JSON.stringify(users));
            res.end();
        });
        aplicacao.get('/comments?id=1',function(req,res){
            console.log("entered");
            res.end();
        });
    })();    
processRequest.js

   var http = require('http'); 
var criarProcessRequest = function(porta){
    var entrada = {};// Api de entrada
    var caminhos = {};// rotas para o pedido
    var metodos = ['GET','POST'];// O pedido é enviado com maisculas    
    metodos.forEach(function(metodo){
        caminhos[metodo] = {};
        entrada[metodo.toLowerCase()] = function(path,fn){
            caminhos[metodo][path] = fn;
        };
    });
    http.createServer(function(req,res){
        res.setHeader('Access-Control-Allow-Origin','*');
        if(!caminhos[req.method][req.url]){
            res.statusCode = 404;
            return res.end();
        }
        caminhos[req.method][req.url](req,res);
    }).listen(porta);
    return entrada;
};
module.exports = criarProcessRequest;

also sorry if the english is bad it isnt my first languege

i have tried special characters like #,$,&,? and have tried researching

After rotating the image the canvas not able to rotate only the image in rotated and fitted inside the original canvas height and width

I want canvas to height and width to change when I rotate the image and work accordingly, And the overlay should also fit the rotated canvas and crop should work accordingly.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Cropper</title>
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <style>
        html,
        body {
            background: black;
            margin: 0;
            padding: 0;
            height: 90%;
        }

        .image-container {
            position: relative;
            min-height: 80%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: auto;
            overflow: visible;
            /* background-color: #f8f9fa; */
        }

        /* @media (max-width: 480px) {
            .image-container {
                min-height: 60vh;
            }
        } */
        #canvas {
            display: block;
            max-width: 100%;
            max-height: 100%;
            margin: auto;
            /* background-color: white; */
        }

        .main-div {
            padding-top: 5%;
            width: 100%;
            height: 100%;
            background: black;
        }

        .overlay {
            position: absolute;
            border: 2px solid yellow;
            cursor: move;
            touch-action: none;
            z-index: 1;
        }

        .ui-resizable-handle {
            background-image: none;
        }

        .corner {
            position: absolute;
            width: 15px;
            height: 15px;
            background-color: yellow;
            border-radius: 50%;
        }

        .corner.top-left {
            top: -5px;
            left: -5px;
        }

        .corner.top-right {
            top: -5px;
            right: -5px;
        }

        .corner.bottom-left {
            bottom: -5px;
            left: -5px;
        }

        .corner.bottom-right {
            bottom: -5px;
            right: -5px;
        }

        .overlay-background {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            /* Default opacity for rectangle */
            pointer-events: none;
        }

        .rotate-buttons {
            margin-top: 10px;
        }
    </style>
</head>

<body>
    <div class="main-div">
        <button id="cropButton">Crop</button>
        <select id="shapeSelect" onchange="selectShape()">
            <option value="rectangle" selected>Rectangle</option>
            <option value="circle">Circle</option>
        </select>
        <div class="rotate-buttons">
            <button onclick="rotateClockwise()">Rotate Clockwise</button>
            <button onclick="rotateCounterClockwise()">Rotate Counter Clockwise</button>
        </div>
        <div class="image-container">
            <div class="overlay-background"></div>
            <canvas id="canvas"></canvas>
            <div class="overlay">
                <div class="corner top-left"></div>
                <div class="corner top-right"></div>
                <div class="corner bottom-left"></div>
                <div class="corner bottom-right"></div>
            </div>
        </div>
        <div id="croppedImagePreview"></div>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script
        src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
    <!-- Include Touch Punch for touch support -->
    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const canvas = document.getElementById("canvas");
            const ctx = canvas.getContext("2d");
            const imageObj = new Image();
            const overlay = document.querySelector('.overlay');
            const overlayBackground = document.querySelector('.overlay-background');
            const croppedCanvas = document.createElement("canvas");
            const croppedCtx = croppedCanvas.getContext("2d");
            let selectedShape = "rectangle";
            let rotation = 0; // Initial rotation angle

            // Define selectShape function to fix the Uncaught ReferenceError
            window.selectShape = function () {
                selectedShape = document.getElementById("shapeSelect").value;
                console.log(selectedShape);
                // Adjust overlay style based on selected shape
                overlay.style.borderRadius = selectedShape === "circle" ? "50%" : "0%"; // Adjust border radius for circle
                // Adjust overlay background opacity based on selected shape
                if (selectedShape === "circle") {
                    overlayBackground.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; // 50% opacity for circle
                } else {
                    overlayBackground.style.backgroundColor = "rgba(0, 0, 0, 0.7)"; // Higher opacity for rectangle
                }
            };

            // Function to rotate the image clockwise
            window.rotateClockwise = function () {
                rotation += 90;
                drawRotatedImage();
            };

            // Function to rotate the image counter-clockwise
            window.rotateCounterClockwise = function () {
                rotation -= 90;
                drawRotatedImage();
            };

            function drawRotatedImage() {
                const originalWidth = imageObj.naturalWidth;
                const originalHeight = imageObj.naturalHeight;
                const is90Or270 = Math.abs(rotation % 360) === 90 || Math.abs(rotation % 360) === 270;

                // Set canvas size based on rotation
                if (is90Or270) {
                    canvas.width = originalHeight;
                    canvas.height = originalWidth;
                } else {
                    canvas.width = originalWidth;
                    canvas.height = originalHeight;
                }

                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.save();
                ctx.translate(canvas.width / 2, canvas.height / 2);
                ctx.rotate(rotation * Math.PI / 180);
                ctx.drawImage(imageObj, -originalWidth / 2, -originalHeight / 2, originalWidth, originalHeight);
                ctx.restore();

                // Update the overlay to match the new dimensions
                initializeOverlay();
            }

            imageObj.onload = function () {
                const container = document.querySelector('.image-container');
                const ratio = Math.min(container.clientWidth / imageObj.naturalWidth, container.clientHeight / imageObj.naturalHeight);
                const imageWidth = imageObj.naturalWidth * ratio;
                const imageHeight = imageObj.naturalHeight * ratio;

                canvas.width = imageObj.naturalWidth;
                canvas.height = imageObj.naturalHeight;
                canvas.style.width = `${imageWidth}px`;
                canvas.style.height = `${imageHeight}px`;


                container.style.width = `${imageWidth}px`;
                container.style.height = `${imageHeight}px`;

                ctx.drawImage(imageObj, 0, 0, imageObj.naturalWidth, imageObj.naturalHeight);
                initializeOverlay();
            };

            imageObj.crossOrigin = "Anonymous";
            imageObj.src = 'rope41.png'; // Make sure the path is correct

            function initializeOverlay() {
                const container = document.querySelector('.image-container');
                const ratio = Math.min(container.clientWidth / imageObj.naturalWidth, container.clientHeight / imageObj.naturalHeight);
                const imageWidth = imageObj.naturalWidth * ratio;
                const imageHeight = imageObj.naturalHeight * ratio;
                var tempImageWidth = imageWidth - 4; //Adjusting width the initial overlay to fit properly in pixels
                var tempImageHeight = imageHeight - 3; //Adjusting height the initial overlay to fit properly pixels

                overlay.style.width = `${tempImageWidth}px`;
                overlay.style.height = `${tempImageHeight}px`;
                // Center the overlay
                overlay.style.top = `${(container.clientHeight - imageHeight) / 2}px`;
                overlay.style.left = `${(container.clientWidth - imageWidth) / 2}px`;

                $(overlay).resizable({
                    containment: "parent",
                    handles: 'n, e, s, w, ne, se, sw, nw',
                    minWidth: 100, // Setting minimum width for resizing
                    minHeight: 100, // Setting minimum height for resizing
                    resize: function (event, ui) {
                        updateOverlayBackground(ui.position, ui.size);
                    }
                }).draggable({
                    containment: "parent",
                    drag: function (event, ui) {
                        // Get the current dimensions using jQuery
                        const currentWidth = $(overlay).width();
                        const currentHeight = $(overlay).height();
                        // Create a size object to pass to the update function
                        const size = { width: currentWidth, height: currentHeight };
                        updateOverlayBackground(ui.position, size);
                    }
                });

                updateOverlayBackground({ top: overlay.style.top, left: overlay.style.left }, { width: tempImageWidth, height: tempImageHeight });
            }

            function updateOverlayBackground(position, size) {
                // Corrected to ensure position and size are correctly parsed as numbers
                const left = parseFloat(position.left) || 0; // Added default value to prevent undefined
                const top = parseFloat(position.top) || 0; // Added default value to prevent undefined
                const width = parseFloat(size.width) || 0; // Added default value to prevent undefined
                const height = parseFloat(size.height) || 0; // Added default value to prevent undefined

                overlayBackground.style.clipPath = `polygon(
                    0 0, 
                    0 100%, 
                    ${left}px 100%, 
                    ${left}px ${top}px, 
                    ${left + width}px ${top}px, 
                    ${left + width}px ${top + height}px, 
                    ${left}px ${top + height}px, 
                    ${left}px 100%, 
                    100% 100%, 
                    100% 0
                )`;
            }

            document.getElementById('cropButton').addEventListener('click', cropImage);

            function cropImage() {
                const selectedShape = document.getElementById("shapeSelect").value;
                const canvasRect = canvas.getBoundingClientRect();
                const bounds = overlay.getBoundingClientRect();

                const scaleX = canvas.width / canvasRect.width;
                const scaleY = canvas.height / canvasRect.height;

                const cropX = (bounds.left - canvasRect.left) * scaleX;
                const cropY = (bounds.top - canvasRect.top) * scaleY;
                const cropWidth = bounds.width * scaleX;
                const cropHeight = bounds.height * scaleY;

                croppedCanvas.width = cropWidth;
                croppedCanvas.height = cropHeight;

                if (selectedShape === "rectangle") {
                    croppedCtx.drawImage(canvas, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
                } else if (selectedShape === "circle") {
                    let rx = cropWidth / 2; // Radius along x-axis
                    let ry = cropHeight / 2; // Radius along y-axis
                    let cx = rx; // Center along x-axis
                    let cy = ry; // Center along y-axis

                    croppedCtx.save(); // Save the current context state
                    croppedCtx.beginPath();
                    croppedCtx.translate(cx, cy); // Translate to the center of the ellipse
                    croppedCtx.scale(1, ry / rx); // Scale to make the circle an ellipse
                    croppedCtx.arc(0, 0, rx, 0, Math.PI * 2);
                    croppedCtx.closePath();
                    croppedCtx.restore(); // Restore the previous context state
                    croppedCtx.clip();

                    croppedCtx.drawImage(
                        canvas,
                        cropX,
                        cropY,
                        cropWidth,
                        cropHeight,
                        0,
                        0,
                        cropWidth,
                        cropHeight
                    );
                }

                const imgPreview = document.getElementById('croppedImagePreview');
                imgPreview.innerHTML = '';
                const img = new Image();
                img.src = croppedCanvas.toDataURL("image/png");
                console.log(img.src);
                imgPreview.appendChild(img);
            }
        });
    </script>
</body>

</html>

When rotated I need the canvas to rotate as well and crop works on the rotated image.
I tried to switch the canvas height and width based on rotation but did not work.

Make Blazor stay in sync with JS manipulated DOM

My problem is that I have a page which implements IDisposable interface, and when I move away from this page I get an error

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]

Unhandled exception rendering component: 
TypeError: Cannot read properties of null (reading 'removeChild')

and also

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]

Unhandled exception rendering component: 
Error: No element is currently associated with component 40

I have a search input which finds all occurrences of the input value on the entire page and highlights them. It works through JS Interop and uses a JS function which manipulates DOM (wraps all occurrences of the input value with <span class="highlight"></span>).

I’ve investigated that Blazor has its own in-memory DOM copy and my problem derives from the fact that it does not see changes that was made by JS. That elements seem unfamiliar to Blazor…

So, can I somehow make Blazor know the changes to DOM that are made by JS? Or maybe implement this feature of highlighting with pure Blazor (but note that I need to be able to search through the text of the entire page, like Ctrl+F in browser does)?

Is it possible to translate text in a chrome extension using Chrome’s API?

I am developing a chrome extension that requires translating text between different languages. I would prefer to avoid making external API calls to a paid service such as Google Translation API and the free alternatives often depend on Google Translate and hence are blocked and require proxies.

Chrome has a rich API for developers, and I am wondering whether I could leverage it to translate text. ChatGPT has provided me with several answers that all turned out to be wrong, for instance with the chrome.tabs, chrome.scripting, and Chrome i18n APIs.

Basically, I would like a function like this:

function translateText(text, srcLanguage, destLanguage){

} 

Is there any way to implement that with Chrome’s API, or there might be in the future?

Failed to request allowed query parameters from WebPrivacy

Experiencing the error/warning: “Failed to request allowed query parameters from WebPrivacy” on a brand new CapacitorJS project on iOS using xCode 15+

The error appears in the console as shown below right after initialization of the Capacitor into the WebView. This originally happend on a main project but we haven’t been able to find any errors within the code that regards the Webprivacy query parameters. Also the app works with not bugs. We then went forward by trying to create a new Capacitor project just to see and the error still appears. And it does. So do anyone have an idea what could cause this?

Here is a link to the repo:
https://github.com/jonasbehlic/WebPrivacyIOS

Xcode Console output:
-> Warning: -[BETextInput attributedMarkedText] is unimplemented
⚡️ Loading app at capacitor://localhost…
-> Failed to request allowed query parameters from WebPrivacy.
⚡️ WebView loaded
⚡️ To Native -> SplashScreen hide 111656092
⚡️ TO JS undefined

Set different column widths for FullCalendar JS in Resource View

I’m using FullCalendar JS version 3. It doesn’t seem to have a feature for setting different column widths for resources in resource view. I haven’t found a way of doing it using options. ChatGPT suggested this:

$('#calendar').fullCalendar({
  // ...
  columnHeader: {
    dow: [
      { width: 200 }, 
      { width: 150 },
      { width: 100 },
      { width: 100 },
      { width: 100 },
      { width: 100 },
      { width: 80 }
    ]
  }
});

It didn’t work. If it does work, it probably just works in other views, not the resources view.

If I want to achieve this by modifying fullcalendar.js itself, which function do it modify. Yes, I know this is not the ideal way of adding features, but that’s what I’m choosing to do. Where do I edit?

My Canvas is not working with form Element

This is my code

<?php
$pair = "BTCUSDT";
$frame = 1;
$candle = 30;
if(isset($_POST['pair'])){

 $pair = $_POST['pair'];
 $frame = $_POST['frame'];
 $candle = $_POST['candle'];

}
?>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo $pair; ?> Live Chart</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body> Say some title

<canvas id="btcusdt-chart" width="800" height="400"></canvas>
    
    <script>
        var ctx = document.getElementById('btcusdt-chart').getContext('2d');
        var chart = null;

        // Function to fetch BTC/USDT data from PHP
        function fetchData() {
            $.ajax({
                url: 'fetch_data.php?pair=<?php echo $pair; ?>&frame=<?php echo $frame; ?>&candle=<?php echo $candle; ?>',
                type: 'GET',
                dataType: 'json',
                success: function(data) {
                    updateChart(data);
                },
                error: function(xhr, status, error) {
                    console.error(error);
                }
            });
        }

        // Function to update the chart with new data
        function updateChart(data) {
            if (chart) {
                // Update existing chart
                chart.data.labels = data.timestamps;
                chart.data.datasets[0].data = data.btcusdt_values;
                chart.update();
            } else {
                // Create new chart
                chart = new Chart(ctx, {
                    type: 'line',
                    data: {
                        labels: data.timestamps,
                        datasets: [{
                            label: '<?php echo $pair; ?>',
                            data: data.btcusdt_values,
                            borderColor: 'blue',
                            borderWidth: 1,
                            fill: false
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        scales: {
                            xAxes: [{
                                type: 'time',
                                time: {
                                    unit: 'minute'
                                }
                            }],
                            yAxes: [{
                                ticks: {
                                    beginAtZero: false
                                }
                            }]
                        }
                    }
                });
            }
        }

        // Update chart every 10 seconds
        setInterval(fetchData, 10000);

        // Initial fetch
        fetchData();
    </script>

</body>
</html>

It is not working because canvas is stressing vertically. If I remove


Say some title

it works fine. Why and how to solve it?

JavaScript: intercepting requests from workers

I’m trying to intercept and modify POST requests to an API made by a website.

I know this can be done by monkeypatching XMLHttpRequest() and fetch() however it turns out that the request is done by a worker (confirmed it with Worker() = "";) so the above methods don’t affect it.
And you can’t monkeypatch Worker() as far as I know.

So I tried registering my own worker that will intercept the requests from a string resulting in the following code:

var script = document.createElement("script");
script.id = "worker1";
script.innerHTML = `
var b = "";
self.onmessage = function(e) {
    b = self.fetch;
    self.addEventListener('fetch', function(event) {
        event.respondWith(
            fetchWithParamAddedToRequestBody(event.request)
        );
    });
    function fetchWithParamAddedToRequestBody(request) {
        serialize(request).then(function(serialized) {
            // modify serialized.body here to add your request parameter
            deserialize(serialized).then(function(request) {
                return fetch(request);
            });
        }); // fixed this
    }
    function serialize(request) {
        var headers = {};
        for (var entry of request.headers.entries()) {
            headers[entry[0]] = entry[1];
        }
        var serialized = {
            url: request.url,
            headers: headers,
            method: request.method,
            mode: request.mode,
            credentials: request.credentials,
            cache: request.cache,
            redirect: request.redirect,
            referrer: request.referrer
        };  
        if (request.method !== 'GET' && request.method !== 'HEAD') {
            return request.clone().text().then(function(body) {
                serialized.body = body;
                return Promise.resolve(serialized);
            });
        }
        return Promise.resolve(serialized);
    }
    function deserialize(data) {
        return Promise.resolve(new Request(data.url, data));
    };
    self.postMessage("hello world");
};
`;
document.head.prepend(script);

var blob = new Blob([document.querySelector('#worker1').textContent], {type:"text/javascript"});

var worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = function (e){
    console.log("Received: "+e.data);
};
worker.postMessage("hello");

However I don’t really know what I’m doing so this is probably wrong.
Besides I suspect this method will only allow me to intercept requests from the document and not from other workers…

Would replacing the real worker with my worker allow me to monkeypatch the requests APIs for it?
But when I try posting a message with self.fetch from inside a worker (worker.postMessage(b);) it tells me the function is native code unlike in window, which suggests this is not so simple…

I also saw this topic, however it seems that it doesn’t have a working answer and it uses navigator.serviceWorker.register() from file, and I’m not sure how that works with a Blob.

Can this be done at all?

Lambda function errorMessage”: “Cannot read properties of undefined (reading ‘0’)”,

I have a lambda function that is triggered whenever something is saved in my s3 bucket. The aim is to move access logs stored in S3 to CloudWatch log group.

When function is triggered i get this error. Tried trouble shooting but cant seem to figure it out

{
  "errorType": "TypeError",
  "errorMessage": "Cannot read properties of undefined (reading '0')",
  "trace": [
    "TypeError: Cannot read properties of undefined (reading '0')",
    "    at Runtime.exports.handler (/var/task/index.js:20:47)",
    "    at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)"
  ]
}

Could someone please help troubleshoot this issue? Any insights or suggestions on what might be causing this TypeError and how to resolve it would be greatly appreciated. I suspect it’s a data handling issue within the event object, but I’m not sure what specific part of the event data I’m missing or misunderstanding.
my code

'use strict';

const readline = require('readline');
const stream = require('stream');
const url = require('url');

const zlib = require('zlib');
const {
    promisify,
} = require('util');
const gunzipAsync = promisify(zlib.gunzip);

const aws = require('aws-sdk');

//specifying the log group and the log stream name for CloudWatch Logs
const logGroupName = process.env.LOG_GROUP_NAME;
const loadBalancerType = process.env.LOAD_BALANCER_TYPE;
const plaintextLogs = process.env.PLAINTEXT_LOGS;

const MAX_BATCH_SIZE = 1048576; // maximum size in bytes of Log Events (with overhead) per invocation of PutLogEvents
const MAX_BATCH_COUNT = 10000; // maximum number of Log Events per invocation of PutLogEvents
const LOG_EVENT_OVERHEAD = 26; // bytes of overhead per Log Event

const fields = {
    application: [
        "type",
        "time",
        "elb",
        "client:port",
        "target:port",
        "request_processing_time",
        "target_processing_time",
        "response_processing_time",
        "elb_status_code",
        "target_status_code",
        "received_bytes",
        "sent_bytes",
        "request",
        "user_agent",
        "ssl_cipher",
        "ssl_protocol",
        "target_group_arn",
        "trace_id",
        "domain_name",
        "chosen_cert_arn",
        "matched_rule_priority",
        "request_creation_time",
        "actions_executed",
        "redirect_url",
        "error_reason",
        "target:port_list",
        "target_status_code_list"
    ],
    classic: [
        "time",
        "elb",
        "client:port",
        "backend:port",
        "request_processing_time",
        "backend_processing_time",
        "response_processing_time",
        "elb_status_code",
        "backend_status_code",
        "received_bytes",
        "sent_bytes",
        "request",
        "user_agent",
        "ssl_cipher",
        "ssl_protocol"
    ],
    network: [
        "type",
        "version",
        "time",
        "elb",
        "listener",
        "client:port",
        "destination:port",
        "connection_time",
        "tls_handshake_time",
        "received_bytes",
        "sent_bytes",
        "incoming_tls_alert",
        "chosen_cert_arn",
        "chosen_cert_serial",
        "tls_cipher",
        "tls_protocol_version",
        "tls_named_group",
        "domain_name",
        "alpn_fe_protocol",
        "alpn_be_protocol",
        "alpn_client_preference_list"
    ]
}

exports.handler = async (event, context) => {
    const s3 = new aws.S3({
        apiVersion: '2006-03-01'
    });
    const cloudWatchLogs = new aws.CloudWatchLogs({
        apiVersion: '2014-03-28'
    });

    const logStreamName = context.logStreamName;
    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/+/g, ' '));
    const s3object = await getS3Object(bucket, key);
    const logData = await unpackLogData(s3object);
    await createLogGroupIfNotExists();

    let sequenceToken = await getLogStreamSequenceToken(logStreamName);

    console.log('Parsing log lines');
    var batches = [];
    var batch = [];
    var batch_size = 0;
    var bufferStream = new stream.PassThrough();
    bufferStream.end(logData);

    var rl = readline.createInterface({
        input: bufferStream
    });

    function portField(fieldName, element, parsed) {
        const field = fieldName.split(':')[0];
        const [ip, port] = element.split(':');
        if (ip === '-1') parsed[field] = parseInt(ip)
        else parsed[field] = ip;

        if (port) parsed[`${field}_port`] = parseInt(port)
        else parsed[`${field}_port`] = -1
    }

    // Functions that mutate the parsed object
    const fieldFunctions = {
        "request": (fieldName, element, parsed) => {
            const [request_method, request_uri, request_http_version] = element.split(/s+/)
            parsed.request_method = request_method
            parsed.request_uri = request_uri
            parsed.request_http_version = request_http_version
            const parsedUrl = url.parse(request_uri)
            parsed.request_uri_scheme = parsedUrl.protocol
            parsed.request_uri_host = parsedUrl.hostname
            if (parsedUrl.port) parsed.request_uri_port = parseInt(parsedUrl.port)
            parsed.request_uri_path = parsedUrl.pathname
            parsed.request_uri_query = parsedUrl.query
        },
        "target:port": portField,
        "client:port": portField,
        "backend:port": portField,
    }

    function readLines(line) {
        let ts;
        switch (loadBalancerType) {
            case 'classic':
                ts = line.split(' ', 1)[0];
                break;
            case 'application':
                ts = line.split(' ', 2)[1];
                break;
            case 'network':
                ts = line.split(' ', 3)[2];
                break;
            default:
                console.error('Invalid load balancer type');
                process.exit(1);
        }

        var tval = Date.parse(ts);

        if (!plaintextLogs) line = JSON.stringify(parseLine(line));
        
        var event_size = line.length + LOG_EVENT_OVERHEAD;
        batch_size += event_size;
        if (batch_size >= MAX_BATCH_SIZE ||
            batch.length >= MAX_BATCH_COUNT) {
            // start a new batch
            batches.push(batch);
            batch = [];
            batch_size = event_size;
        }

        batch.push({
            message: line,
            timestamp: tval,
        });
    }

    function parseLine(line) {
        console.log('Parsing log line')
        const parsed = {}
        let x = 0
        let end = false
        let withinQuotes = false
        let element = ''
        for (const c of line + ' ') {
            if (end) {
                if (element) {
                    const fieldName = fields[loadBalancerType][x]

                    if (element.match(/^d+.?d*$/)) element = Number(element)

                    if (fieldFunctions[fieldName]) fieldFunctions[fieldName](fieldName, element, parsed)

                    parsed[fieldName] = element;

                    element = '';
                    x++;
                }
                end = false;
            };

            if (c.match(/^s$/) && !withinQuotes) end = true;

            if (c === '"') {
                if (withinQuotes) end = true
                withinQuotes = !withinQuotes;
            }
            else if (!end) element += c;
        }
        return parsed
    }

    async function sendBatch(logEvents, seqToken) {
        console.log(`Sending batch to ${logStreamName}`);
        var putLogEventParams = {
            logEvents,
            logGroupName,
            logStreamName,
            sequenceToken: seqToken,
        }

        // sort the events in ascending order by timestamp as required by PutLogEvents
        console.log('Sorting events');
        putLogEventParams.logEvents.sort((a, b) => {
            if (a.timestamp > b.timestamp) return 1;
            if (a.timestamp < b.timestamp) return -1;
            return 0;
        });

        console.log('Calling PutLogEvents')
        const cwPutLogEvents = await cloudWatchLogs.putLogEvents(putLogEventParams).promise();
        console.log(`Success in putting ${putLogEventParams.logEvents.length} events`);
        return cwPutLogEvents.nextSequenceToken

        // try {

        // } catch (err) {
        //     console.log('Error during put log events: ', err, err.stack);
        //     return sequenceToken;
        // }
    }

    async function sendBatches() {
        batches.push(batch);
        console.log(`Finished batching, pushing ${batches.length} batches to CloudWatch`);
        let seqToken = sequenceToken;
        for (let i = 0; i < batches.length; i++) {
            const logEvents = batches[i];
            var count = 0;
            var batch_count = 0;
            try {
                seqToken = await sendBatch(logEvents, seqToken);
                ++batch_count;
                count += logEvents.length;
            } catch (err) {
                console.log('Error sending batch: ', err, err.stack);
                continue;
            }
        }
        console.log(`Successfully put ${count} events in ${batch_count} batches`);
    }

    async function getS3Object(Bucket, Key) {
        console.log(`Retrieving ${Bucket}${Key}`);
        return await s3.getObject({
            Bucket,
            Key,
        }).promise();
    }

    async function unpackLogData(s3object) {
        console.log(`Unpacking log data for ${loadBalancerType} load balancer`);
        if (loadBalancerType === "classic") return s3object.Body.toString('ascii')
        else {
            const uncompressedLogBuffer = await gunzipAsync(s3object.Body);
            return uncompressedLogBuffer.toString('ascii');
        }
    }

    async function createLogGroupIfNotExists() {
        console.log(`Checking Log Group ${logGroupName} exists`);
        const result = await cloudWatchLogs.describeLogGroups({
            logGroupNamePrefix: logGroupName,
        }).promise();
        if (!result.logGroups[0]) {
            console.log(`${logGroupName} exists`);
            await cloudWatchLogs.createLogGroup({
                logGroupName,
            }).promise();
        }
    }

    async function getLogStreamSequenceToken() {
        console.log(`Checking Log Streams ${logGroupName}/${logStreamName}`);
        let currentStream;
        const cwlDescribeStreams = await cloudWatchLogs.describeLogStreams({
            logGroupName,
            logStreamNamePrefix: logStreamName
        }).promise();

        if (cwlDescribeStreams.logStreams[0]) currentStream = cwlDescribeStreams.logStreams[0]
        else {
            console.log(`Creating Log Stream ${logGroupName}/${logStreamName}`);
            await cloudWatchLogs.createLogStream({
                logGroupName,
                logStreamName,
            }).promise();
            const cwlDescribeCreatedStream = await cloudWatchLogs.describeLogStreams({
                logGroupName: logGroupName,
                logStreamNamePrefix: logStreamName
            }).promise();
            currentStream = cwlDescribeCreatedStream.logStreams[0];
        }

        return currentStream.uploadSequenceToken;
    }
};