I want to make a JavaScript hue inverter (add or subtract 180°), but it isn’t working. How can I fix this?

In case you don’t know, a hue inverter takes a value between 0 and 360, adds 180 if the input is less than or equal to 180, and subtracts 180 if the input is greater than 180. I added some JavaScript to do that operation, but it isn’t working.

Here is the UI (there is no CSS yet): Click to see the image.

function invertHue() {
  const inputHue = Number(document.getElementById("hue-i").value);
  const outputHue = document.getElementById("hue-o").textContent;
  var result;

  try {
    if (inputHue <= 180 && inputHue > 0) {
      result = inputHue + 180;
      outputHue = result;
    } else if (inputHue > 180 && inputHue <= 360) {
      result = inputHue - 180;
      outputHue = result;
    } else {
      window.alert("The hue must be between 0 and 360.");
    }
  } catch (error) {
    window.alert("There was an issue.");
  }
}
<div class="input">
  <input type="text" id="hue-i" placeholder="Enter hue here...">
  <button id="invert-btn" onclick="invertHue()">Invert</button>
</div>
<div class="output">
  <p id="hue-o">Inverted hue will appear here...</p>
</div>

What is going wrong in the code?

(p.s. I am using VS Code for my editor.)

PHP script returns empty JSON on cloud server but works locally – How to debug?

I’m encountering an issue with my PHP script (obtener_rutas.php) that’s supposed to return JSON data for a routes application. The script works perfectly on my local server (XAMPP), but when deployed to my cloud server, it returns an empty JSON response.

Local server (works correctly):

  • The script returns the expected JSON data with route information.

Cloud server (problem):

  • The PHP script returns an empty JSON response: []

Here’s my obtener_rutas.php script:

<?php
ob_start();

function debug_log($message) {
    file_put_contents('debug.log', date('[Y-m-d H:i:s] ') . $message . "n", FILE_APPEND);
}

debug_log("Script started");
debug_log("SERVER: " . print_r($_SERVER, true));

error_reporting(E_ALL);
ini_set('display_errors', 0);
header('Content-Type: application/json');

function handleError($errno, $errstr, $errfile, $errline) {
    $error = array(
        'error' => 'PHP Error',
        'message' => $errstr,
        'file' => $errfile,
        'line' => $errline
    );
    debug_log("Error occurred: " . json_encode($error));
    echo json_encode($error);
    exit;
}

set_error_handler('handleError');

try {
    debug_log("About to require connection files");
    require_once '../../inicio_sesion/conexion.php';
    require_once '../../inicio_sesion/config.php';  
    debug_log("Connection files included");

    if (!$conn) {
        throw new Exception('Database connection error');
    }
    debug_log("Database connection successful");

    $id = isset($_GET['id']) ? filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT) : null;

    function getImageUrl($baseUrl, $path) {
        return $baseUrl . '/paginasespecializadas/skitouring/' . $path;
    }

    debug_log("About to execute query");

    if ($id) {
        
        $query = "SELECT r.*, 
                         GROUP_CONCAT(DISTINCT ri.imagen_path) as imagenes,
                         rp.view_count, rp.download_count, rp.favorite_count, rp.complete_count, rp.popularity_score
                  FROM rutas r
                  LEFT JOIN ruta_imagenes ri ON r.id = ri.ruta_id
                  LEFT JOIN route_popularity rp ON r.id = rp.route_id
                  WHERE r.id = ?
                  GROUP BY r.id";
        
        $stmt = $conn->prepare($query);
        $stmt->bind_param("i", $id);
        $stmt->execute();
        $result = $stmt->get_result();

        if ($result->num_rows === 0) {
            throw new Exception('Route not found');
        }

        $ruta = $result->fetch_assoc();
        $ruta['imagenes'] = $ruta['imagenes'] ? explode(',', $ruta['imagenes']) : [];
        
        // Update image paths with BASE_URL and correct folder structure
        $ruta['imagenes'] = array_map(function($path) {
            return getImageUrl(BASE_URL, $path);
        }, $ruta['imagenes']);
        
        debug_log("Single route processed");
        echo json_encode($ruta);
    } else {
        
        $query = "SELECT r.*, 
                         GROUP_CONCAT(DISTINCT ri.imagen_path) as imagenes,
                         rp.view_count, rp.download_count, rp.favorite_count, rp.complete_count, rp.popularity_score
                  FROM rutas r
                  LEFT JOIN ruta_imagenes ri ON r.id = ri.ruta_id
                  LEFT JOIN route_popularity rp ON r.id = rp.route_id
                  GROUP BY r.id
                  ORDER BY rp.popularity_score DESC";  // Order by popularity

        debug_log("Executing all routes query");
        $result = $conn->query($query);
        debug_log("All routes query executed");

        if (!$result) {
            throw new Exception('Query error: ' . $conn->error);
        }

        $rutas = [];
        while ($row = $result->fetch_assoc()) {
            $row['imagenes'] = $row['imagenes'] ? explode(',', $row['imagenes']) : [];
            
            
            $row['imagenes'] = array_map(function($path) {
                return getImageUrl(BASE_URL, $path);
            }, $row['imagenes']);
            
            $rutas[] = $row;
        }

        debug_log("Number of routes fetched: " . count($rutas));
        echo json_encode($rutas);
    }

    debug_log("Query results processed and sent");

} catch (Exception $e) {
    http_response_code(500);
    $error = ['error' => $e->getMessage()];
    debug_log("Exception caught: " . json_encode($error));
    echo json_encode($error);
} finally {
    if (isset($stmt)) {
        $stmt->close();
    }
    if (isset($conn) && $conn instanceof mysqli) {
        $conn->close();
    }
    debug_log("Script ended");
}
?>

I’ve implemented logging to a debug.log file, but I’m not seeing any error messages or exceptions in the log on the cloud server.
What I’ve tried:

Checking the debug.log file for any error messages.
Verifying that the database connection details are correct for the cloud environment.
Ensuring that the file paths for conexion.php and config.php are correct on the cloud server.

Using react and useState is it possible to randomly decrement multiple instances of one component?

When I click the button both components are decremented at the same rate. I would like them to decrement by different values.
I am just learning React not sure if it is possible.

This is what I have so far.

const ParentComponent = () => {
  const [num, setNum] = useState(100);

  const handleClick = () => {
    if (num === 0) {
      return;
    }
    const decrement = Math.floor(Math.random() * 20) + 1;
    setNum(() => Math.max(num - decrement, 0));
  };

  return (
    <div>
      <ChildComponent value={num} />
      <ChildComponent value={num} />

      <button onClick={handleClick}>Random Decrement</button>
    </div>
  );
};
const ChildComponent = ({ value }) => <div>Num: {value}</div>;

Examples of lexical scoping in js other than nested functions?

I was trying to understand the lexical scoping. I gone through here and I found all examples are related to nested functions. Is lexical scope only related to nested functions and parent to child accessibility?

Are there any examples of lexical scoping in js other than nested functions and closures?

AND

Does function parameters also form lexical scoping? For example:

function greet(name) {
    console.log("Hello, " + name + "!");
}

greet("Alice"); // Output: "Hello, Alice!"
greet("Bob"); // Output: "Hello, Bob!"

Encrypting JavaScript ((not obfuscate))

I’ve seen many discussions that suggest JavaScript obfuscation as a way to protect code. However, I’m concerned that this approach offers security through obscurity, which is not enough for my use case. Obfuscation may discourage casual copying, but it’s not suitable for protecting critical parts of a server-side application that includes paid or restricted features.

My context:
I’m building a Node.js backend application that will be installed on users’ computers and servers. This is not a front-end app, but a fully server-side application where some features need to be restricted or licensed. I need a more robust solution to ensure that specific sections of the code are secure and cannot be tampered with or accessed without proper authorization.

My idea:
I’ve thought of a potential solution, but I’m not sure how effective or practical it is. Here’s the idea:

Disclaimer! This is just an idea, and I think it’s a little bit silly.

Use Webpack to bundle and obfuscate the JavaScript code into a single file (bundle.js).
Then, wrap the obfuscated bundle inside a C++ program. The C++ code would execute the JavaScript using Node.js by calling the command-line interface to run the obfuscated code:

#include <iostream>
#include <cstdlib>

int main() {
    // Obfuscated JavaScript code
    std::string jsCode = "obfuscated JavaScript code here";
    
    // Command to run JavaScript using Node.js
    std::string command = "node -e "" + jsCode + """;
    int result = system(command.c_str());

    if (result == 0) {
        std::cout << "JavaScript executed successfully!" << std::endl;
    } else {
        std::cout << "Failed to execute JavaScript!" << std::endl;
    }

    return 0;
}

By embedding the JavaScript inside a compiled C++ binary, I hope to add another layer of security, making it harder to reverse-engineer or tamper with the code. However, I’m not sure if this approach is practical, or if it’s a known method in the community.

My questions:
Has anyone explored this approach before? Is wrapping JavaScript inside a C++ binary a known technique for securing code, and if so, are there any best practices?
Are there better ways to protect the code for paid or licensed features in server-side JavaScript applications, especially when those applications are deployed on systems I don’t control?
Does this method have significant downsides such as performance issues or security vulnerabilities that I should consider before attempting it?
Are there other established solutions for securing sensitive parts of a Node.js backend application that need to remain inaccessible unless properly licensed?
I’ve looked into tools like pkg and nexe, but these mostly package the application into an executable and don’t seem to offer strong protection for paid or restricted features. I’m looking for a solution that goes beyond security through obscurity.

In PHP, we used to have IonCube, it’s powerful and easy to setup. But now in JavaScript, people seem lazy to develop solutions like this, because most of the community thinks that obfuscation is enough. and this is because people used to write front-end applications and server-side apps that are not distributed. But now the era is evolving and you can write desktop apps with javascript as well as mobile apps.

Any insights or guidance would be much appreciated!

How can I combine all elements of a div into a static image before printing?

I’ve inherited a project so there’s a lot of javascript and jquery going on that I don’t understand the original intent, so please bear with me with any followup questions.

I have a tool where you can replace the image on a card with any other image you want (via cropper). Once everything is sized and positioned as you see fit, upon clicking print, all elements shift back to whatever things look like without custom styles. Here’s what the original developer had to accomplish this:

    var frontPosition, backPosition;

    window.onbeforeprint = function(event){
      
      frontPosition = $(initial_image_front).position();
      backPosition = $(initial_image_back).position();

      if ( frontPosition.top > 0 ) {
        $(initial_image_front).css({ top: frontPosition.top * 0.470588235 });
      }
      if ( frontPosition.top < 0 ) {
        $(initial_image_front).css({ top: (-Math.abs(frontPosition.top) * 0.366863905) });
      }
      if ( frontPosition.left !== 0 ) {
        $(initial_image_front).css({ left: 0 });
      }

      if ( backPosition.top > 0 ) {
        $(initial_image_back).css({ top: backPosition.top * 0.470588235 });
      }
      if ( backPosition.top < 0 ) {
        $(initial_image_back).css({ top: (-Math.abs(backPosition.top) * 0.366863905) });
      }
      if ( backPosition.left !== 0 ) {
        $(initial_image_back).css({ left: backPosition.left * 0.59 });
      }

    };

    window.onafterprint = function(event){
      
      $(initial_image_front).css({ top: frontPosition.top, left: frontPosition.left });
      $(initial_image_back).css({ top: backPosition.top, left: backPosition.left });

    };

And here is the corresponding html:

<div>
  <div class="flex justify-center mb-2 print-hidden">
    <p class="font-head text-white opacity-50">FRONT</p>
  </div>
  <div class="card front flex flex-col mx-auto" style="width: 575px; height: 408px;">
    <%= render 'card_head' %>
    <div class="flex w-7/12 pl-4 pt-3 pb-6 print:pl-2 print:pt-0 print:pb-3 flex-grow flex-wrap items-center justify-center">
      <%# other stuff %>
    </div>
    <%= image_tag "laurie.png", style: "left: 267px; top: 0;", class: "card__image absolute z-0", id: "initial_image_front" %>
  </div>
  <div class="flex justify-between mb-2 mt-2 print-hidden">
    <%# other stuff %>
    <%= form_for(Card.new, remote: true, url: save_photo_path) do |f| %>
      <label>
        <p class="font-head text-white opacity-80 hover:opacity-100 hover:text-red cursor-pointer" id="change_front">Change Photo</p>
        <%= f.file_field :photo, class: "hidden input_front" %>
      </label>
      <div class="hidden">
        <%= f.submit %>
      </div>
    <% end %>
  </div>
</div>

And here’s the before and after:

Before
After

What I’m assuming is happening is all the divs are being organized on top of one another but I want to keep the styles that we had before submitting the form. I’d like to just nix all the window.onbeforeprint and window.onafterprint but I still get the same result, which is why I think the original developer made this mess lol. Is there a simpler way to achieve printing my image than adjusting all the css after it’s already been adjusted??

It’s using tailwind, but obviously there are a lot of styles that seem to be overkill, both inline and in an imported stylesheet. I’ll address that some other time, unless it’s super important in regards to resolving my issue.

How to prevent a custom field from validating on input with VeeValidate

I have a form that should validate only when clicking the “Submit” button. Every field works properly except for a custom field.

My form looks like this:

<template>
  <Form v-slot="{ validate }" :validation-schema="simpleSchema">
    First name:
    <div>
      <Field v-slot="{ field, meta }" name="firstName">
        <input v-bind="field" type="text" />
        {{ meta.validated }}
        {{ meta.valid }}
      </Field>
      <ErrorMessage name="firstName" />
    </div>
    Last name:
    <div>
      <Field v-slot="{ field, meta }" name="lastName">
        <input v-bind="field" type="text" />
        {{ meta.validated }}
        {{ meta.valid }}
      </Field>
      <ErrorMessage name="lastName" />
    </div>
    Item:
    <div>
      <InputText name="item" />
      <ErrorMessage name="item" />
    </div>

    <div>
      <button @click="validate">Submit</button>
    </div>
  </Form>
</template>

<script setup>
import { Form, Field, ErrorMessage, configure } from 'vee-validate';
import * as yup from 'yup';
import InputText from './InputText.vue';

configure({
  validateOnBlur: false,
  validateOnChange: false,
});

const simpleSchema = {
  firstName: yup.string().required(),
  lastName: yup.string().required(),
  item: yup.string().required(),
};
</script>

And custom field looks like this:

<template>
  <input v-model="value" type="text" />
  {{ meta.validated }}
  {{ meta.valid }}
</template>

<script setup>
import { useField } from 'vee-validate';

const props = defineProps({
  name: String,
});

const { value, meta } = useField(props.name);
</script>

Here is an example

As you’ll see, when you fill the First name and Last name fields, meta.validated will only evaluate to true when you press “Submit”, but if you do the same to the Item field, it evaluates to true as soon as you enter a value. How can I fix this so that the Item only evaluates when I press the “Submit” button?

VueJs 3 TypeScript: defineProps withDefaults “Type is missing the following properties from type”

I have a Button component with the following code:

<script lang="ts" setup>
import { computed, ref } from 'vue';

const props = withDefaults(defineProps<{
  href?: string;
  type: 'button' | 'submit' | 'reset' | undefined;
  color: string;
  disabled: boolean;
}>(), {
  type: 'button',
  color: 'primary',
  disabled: false,
});

const element = ref<HTMLElement | null>(null);

defineExpose({
  focus: () => {
    if (element.value) {
      element.value.focus();
    }
  },
});

const style =
  'inline-flex justify-center rounded-sm shadow-sm px-3 py-2 text-sm font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus:outline focus:outline-2 focus:outline-offset-2';

const colors = new Map([
  [
    'primary',
    'bg-lime-600 text-white hover:bg-lime-700 focus-visible:outline-lime-700 focus:outline-lime-700',
  ],
  [
    'base',
    'bg-neutral-600 text-white hover:bg-neutral-700 focus-visible:outline-neutral-700  focus:outline-neutral-700',
  ],
  [
    'red',
    'bg-red-600 text-white hover:bg-red-700 focus-visible:outline-red-700 focus:outline-red-700',
  ],
  [
    'white',
    'bg-white text-neutral-900 ring-1 ring-inset ring-neutral-300 hover:bg-neutral-50 focus-visible:outline-neutral-400 focus:outline-neutral-400',
  ],
]);

const disabled = computed(() =>
  props.disabled ? ' opacity-50 cursor-not-allowed' : null
);

const styles = computed(
  () => `${style} ${colors.get(props.color)}${disabled.value}`
);
</script>
<template>
  <a v-if="href" ref="element" :class="styles" :href="href">
    <slot />
  </a>
  <button v-else ref="element" :class="styles" :type="type">
    <slot />
  </button>
</template>

and I’m using it from within another component as such:

<script lang="ts" setup>
import Button from '@/components/Button.vue';
import { onMounted, ref } from 'vue';

const emit = defineEmits(['cancel', 'proceed']);

const buttonRef = ref<HTMLElement | null>(null);

onMounted(() => {
  if (buttonRef.value) {
    buttonRef.value.focus();
  }
});
</script>

<template>
  <div>
    // ...
    <div class="mt-6 grid grid-flow-row-dense grid-cols-2 gap-3">
      <Button ref="buttonRef" @click="emit('cancel')">Abort</Button>
      <Button color="red" @click="emit('proceed')">
        <slot name="button" />
      </Button>
    </div>
  </div>
</template>

When I compile the code for production I get the error:

Argument of type ‘{ ref: string; onClick: any; }’ is not assignable to parameter of type ‘{ readonly href?: string | undefined; readonly type: “button” | “submit” | “reset” | undefined; readonly color: string; readonly disabled: boolean; } & VNodeProps & … 4 more … & Record<…>’.

Type ‘{ ref: string; onClick: any; }’ is missing the following properties from type ‘{ readonly href?: string | undefined; readonly type: “button” | “submit” | “reset” | undefined; readonly color: string; readonly disabled: boolean; }’: type, color, disabled

<Button ref=”buttonRef” @click=”emit(‘cancel’)”>Abort

Cannot figure out how to resolve it so any help would be much appreciated.

Making the text clickable inside the input field

I am trying to learn vocabulary in my own way. I have created this page that modifies the input sentence by placing the meanings next to the difficult words (in parenthesis) in that sentence. This way, I can learn vocabulary in context. I have added different features and want to add one last feature: making the difficult words in the sentence clickable and pasting them into the next input field, so there’s no need to type those words again. I have tried it, but my code is not working. Here’s my code:

document.getElementById("sentence").focus();

function modifySentence() {
  const sentence = document.getElementById("sentence").value.trim();
  const wordMeaningsInput = document.getElementById("wordMeanings").value.trim();

  if (!sentence || !wordMeaningsInput) {
    alert("Please enter both a sentence and word-meaning pairs.");
    return;
  }

  const wordMeanings = {};
  const lines = wordMeaningsInput.split('n');
  for (const line of lines) {
    const [word, meaningsStr] = line.split(':').map(s => s.trim());
    if (word && meaningsStr) {
      wordMeanings[word] = meaningsStr.split(',').map(s => s.trim());
    }
  }

  let modifiedSentence = sentence;
  for (const word in wordMeanings) {
    const meanings = wordMeanings[word];
    const replacement = `<b>${word} (${meanings.join(', ')})</b>`;
    modifiedSentence = modifiedSentence.replace(new RegExp(`\b${word}\b`, 'gi'), replacement);
  }

  document.getElementById("modifiedsentence").innerHTML = modifiedSentence;

  const formattedSentence = document.getElementById("modifiedsentence").innerHTML;
  addToHistory(formattedSentence);

  document.getElementById("sentence").value = "";
  document.getElementById("wordMeanings").value = "";
}

function addToHistory(sentence) {
  const historyList = document.getElementById("history");
  const listItem = document.createElement("li");
  listItem.innerHTML = sentence;
  historyList.appendChild(listItem);
}

function copyHistoryToClipboard() {
  const historyList = document.getElementById("history");

  const range = document.createRange();
  range.selectNodeContents(historyList);

  window.getSelection().removeAllRanges();
  window.getSelection().addRange(range);
  document.execCommand("copy");
  window.getSelection().removeAllRanges();

  const copySuccessMessage = document.getElementById("copySuccessMessage");
  copySuccessMessage.style.display = "block";
  setTimeout(() => {
    copySuccessMessage.style.display = "none";
  }, 1500);
}

document.addEventListener('keydown', function(event) {
  if (event.ctrlKey && event.key === 'Enter') {
    modifySentence();
  }
});

document.getElementById("modifiedsentence").addEventListener("click", function() {
  document.getElementById("sentence").focus();
});

function handleWordClick(clickedWord) {
  const wordMeaningsTextArea = document.getElementById("wordMeanings");
  const existingText = wordMeaningsTextArea.value;

  if (!existingText.includes(clickedWord + ":")) {
    wordMeaningsTextArea.value += (existingText ? "n" : "") + clickedWord + ": ";

    // Highlight the clicked word WITHOUT re-wrapping all words:
    const sentenceInput = document.getElementById("sentence");
    const wordElements = sentenceInput.querySelectorAll('span'); // Get all existing spans

    wordElements.forEach(wordElement => {
      if (wordElement.textContent === clickedWord && !wordElement.classList.contains('highlighted')) {
        wordElement.classList.add('highlighted');
      }
    });

    wordMeaningsTextArea.focus();
  }
}

function wrapWordsInSpans(sentenceInputId) {
  const sentenceInput = document.getElementById(sentenceInputId);
  const sentenceText = sentenceInput.value;
  const words = sentenceText.split(/s+/);

  // Create a temporary element to hold the wrapped words
  const tempSpan = document.createElement('span');
  tempSpan.innerHTML = words.map(word => {
    // Check if the word is already highlighted
    const isHighlighted = sentenceInput.querySelector(`span.highlighted:contains("${word}")`);

    // Wrap with or without the highlighted class
    return isHighlighted ?
      `<span class="highlighted" onclick="handleWordClick('${word}')">${word}</span>` :
      `<span onclick="handleWordClick('${word}')">${word}</span>`;
  }).join(" ");

  // Replace the content of the input with the wrapped words
  sentenceInput.innerHTML = tempSpan.innerHTML;
}
document.getElementById("sentence").addEventListener("input", function() {
  wrapWordsInSpans("sentence");
});
#one {
  margin-left: auto;
  margin-right: auto;
  width: 90%;
}

.copy-success {
  display: none;
  color: green;
  font-weight: bold;
  margin-top: 5px;
}


/* Style for modified sentence and history */

#modifiedsentence,
#history li {
  font-family: Arial, sans-serif;
  font-size: 12pt;
}


/* Style for highlighted words */

.highlighted {
  background-color: yellow;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<div align="center">
  <h1>Meaning In Sentence</h1>

  <div>
    <h3>Sentence:</h3>
    <input type="text" id="sentence" class="form-control" style="width:80%" placeholder="Enter the sentence containing difficult words" autofocus>
  </div>
  <br>
  <h3>Difficult Words and Meanings:</h3>
  <textarea id="wordMeanings" class="form-control" style="width:80%" placeholder="Enter difficult words and their meanings (one word-meaning pair per line, separated by ':'). 
For multiple meanings, separate them by commas. 
Example: word1: meaning1, meaning2
         word2: meaning3"></textarea>
  <br>
  <div align="center">
    <button type="button" class="btn btn-default" onclick="modifySentence()" id="modifyButton">Modify Sentence</button>
    <button type="button" class="btn btn-default" onclick="copyHistoryToClipboard()">Copy History</button>
    <div class="copy-success" id="copySuccessMessage">Copied!</div>
  </div>
  <br>
  <div class="h4" align="center">
    <h3>Modified Sentence:</h3>
    <p id="modifiedsentence"></p>
  </div>

  <div id="historySection">
    <h3>History:</h3>
    <ul id="history"></ul>
  </div>

</div>

How do you handle call rejections, no-answer, and voicemails

I’m using Twilio to automate my calls, but I’m running into an issue with the call not knowing how to handle declined or no-answer calls. When either of these events happen, the phone call is sent to voicemail (this is a default behavior for most phones). Twilio has no idea how to properly handle this event. I’ve tried adding in AMD support to detect machines, but shorter voicemails or too natural sounding voicemails default to “human.” The funny thing, when I pick up the call, I’m listed as “unknown” rather than human.

My call flow is:

Call Created -> Automated message plays -> Based on condition, it may route to "askName" for more details and confirmations

When I initiate my call, and do the original call creation, I add in the voicemail configuration. But there’s no way to handle this information appropriately. Twilio can’t recognize it as a voicemail quick enough. Moreover, /handleCall will always send me to /askName because there’s no way the call to wait for the AMD status to update.

How do you handle Twilio interacting with a voicemail and thinking it’s human? Or how to handle the call routing appropriately based on the machine detection if machine detection is so slow?

Here’s my code:

import { Router } from 'express';
import twilio from 'twilio';
import axios from 'axios';
import mondayService from '../monday/mondayService.js';

const router = Router();

const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = twilio(accountSid, authToken);
const baseUrl = process.env.BASE_URL || 'http://localhost:3000';
const awsS3Url = process.env.AWS_GOOGLE_S3_BASE_URL;

const recognizedNames = new Map(); // used to capture (in-memory) the SpeechResult
const exitedCalls = new Map();
const amdResults = new Map();

router.post('/initiateCall', async (req, res) => {
    const { to, contactName, pulseId, linkedPulseId, patientName } = req.body;

    console.log('Request:', req.body);

    if (!to || !patientName) {
        return res
            .status(400)
            .send('Please provide "to" and "patientName" in the request body.');
    }

    try {
        const call = await client.calls.create({
            from: process.env.TWILIO_PHONE_NUMBER,
            to: to,
            url: `${baseUrl}/twilio/handleCall?contactName=${encodeURIComponent(
                contactName
            )}&patientName=${encodeURIComponent(patientName)}`,
            statusCallback: `${baseUrl}/twilio/callStatus?pulseId=${pulseId}&linkedPulseId=${linkedPulseId}`,
            statusCallbackEvent: [
                'initiated',
                'ringing',
                'answered',
                'completed',
            ],
            statusCallbackMethod: 'POST',
            machineDetection: 'Enable',
            asyncAmd: 'true',
            asyncAmdStatusCallback: `${baseUrl}/twilio/amdStatus`,
            timeout: 15,
            machineDetectionSpeechThreshold: 2400,
            machineDetectionSpeechEndThreshold: 500,
            machineDetectionSpeechTimeout: 3000,
        });

        res.send({ message: 'Call initiated successfully', callSid: call.sid });
    } catch (error) {
        console.error('Error during call initiation:', error.message);
        res.status(500).send('Failed to initiate call.');
    }
});

router.post('/amdStatus', async (req, res) => {
    const { CallSid, AnsweredBy } = req.body;
    console.log(`AMD Status for call ${CallSid}: ${AnsweredBy}`);

    if (CallSid && AnsweredBy) {
        amdResults.set(CallSid, AnsweredBy);

        if (
            [
                'machine_start',
                'machine_end_beep',
                'machine_end_silence',
                'machine_end_other',
            ].includes(AnsweredBy)
        ) {
            console.log('Detected voicemail or machine, hanging up.');
            const twiml = new twilio.twiml.VoiceResponse();
            twiml.hangup();

            await client.calls(CallSid).update({ twiml: twiml.toString() });
        }
    }

    res.sendStatus(200);
});

router.post('/handleCall', async (req, res) => {
    const { contactName, patientName } = req.query;
    const twiml = new twilio.twiml.VoiceResponse();

    const messageText = contactName
        ? `Hi ${contactName}. This is West Coast Wound. I'm reaching out to express our sincere gratitude for referring ${patientName} to us. We truly appreciate your trust in our care, and we're committed to providing the highest level of service and support for your referrals. Thanks again for partnering with us, and have a great day!`
        : `Hi, this is West Coast Wound. I'm reaching out to express our sincere gratitude for referring ${patientName} to us. We truly appreciate your trust in our care, and we're committed to providing the highest level of service and support for your referrals.`;
    try {
        const response = await axios.post(`${baseUrl}/googleTTS/synthesize`, {
            text: messageText,
        });
        const audioUrl = response.data.url;

        twiml.play(audioUrl);
        twiml.pause({ length: 1 });

        if (!contactName) {
            twiml.redirect(`${baseUrl}/twilio/askName`);
        }
    } catch (error) {
        console.error('Error during Google TTS synthesis:', error.message);
        twiml.hangup();
    }

    res.type('text/xml');
    res.send(twiml.toString());
});

router.post('/askName', async (req, res) => {
    const twiml = new twilio.twiml.VoiceResponse();
    const retryCount = parseInt(req.query.retryCount) || 0;
    const retry = req.query.retry;

    if (retryCount >= 3) {
        console.log('Max retry limit reached, ending the call.');
        twiml.hangup();
        res.type('text/xml');
        res.send(twiml.toString());
        return;
    }

    twiml
        .gather({
            input: 'speech',
            action: '/twilio/confirmName',
            method: 'POST',
            speechTimeout: '2',
            language: 'en-US',
        })
        .play(
            retry ? `${awsS3Url}/askNameRetry.wav` : `${awsS3Url}/askName.wav`
        );

    twiml.redirect(`/twilio/askName?retry=true&retryCount=${retryCount + 1}`);

    res.type('text/xml');
    res.send(twiml.toString());
});

router.post('/confirmName', async (req, res) => {
    const twiml = new twilio.twiml.VoiceResponse();

    const recognizedName = req.body.SpeechResult?.replace(/.$/, '').trim();
    const digits = req.body.Digits;
    const callSid = req.body.CallSid;

    console.log('Received Request:', req.body);

    if (recognizedName) {
        recognizedNames.set(callSid, recognizedName);
        const text = `It sounds like you said --  ${recognizedName}. If that sounds right, please press 1 to confirm. Press 2 to try again, or press 3 to end the call.`;

        try {
            const response = await axios.post(
                `${baseUrl}/googleTTS/synthesize`,
                { text }
            );
            const audioUrl = response.data.url;

            twiml
                .gather({
                    action: '/twilio/handleConfirmation',
                    method: 'POST',
                    numDigits: 1,
                })
                .play(audioUrl);

            twiml.redirect('/twilio/confirmName');

            res.type('text/xml');
            res.send(twiml.toString());
            return;
        } catch (error) {
            console.error('Error during Google TTS synthesis:', error.message);
            twiml.hangup();
            res.status(500).send('Failed to synthesize speech.');
            return;
        }
    }

    if (digits) {
        twiml.redirect('/twilio/handleConfirmation');
    } else {
        twiml.redirect('/twilio/askName?retry=true');
    }

    res.type('text/xml');
    res.send(twiml.toString());
});

router.post('/handleConfirmation', (req, res) => {
    const twiml = new twilio.twiml.VoiceResponse();

    const digits = req.body.Digits;
    const callSid = req.body.CallSid;

    const confirmAudioUrl = `${awsS3Url}/thankYouGoodBye.wav`;
    const exitAudioUrl = `${awsS3Url}/exitThankYou.wav`;
    const retryInputAudioUrl = `${awsS3Url}/retryInput.wav`;

    switch (digits) {
        case '1': // User confirmed the name
            twiml.play(confirmAudioUrl);
            twiml.hangup();
            break;
        case '2': // User wants to retry
            twiml.redirect('/twilio/askName?retry=true');
            recognizedNames.delete(callSid);
            break;
        case '3': // User wants to exit
            twiml.play(exitAudioUrl);
            twiml.hangup();
            recognizedNames.delete(callSid);
            exitedCalls.set(callSid, true);
            break;
        default: // Invalid input, ask again
            twiml.play(retryInputAudioUrl);
            twiml.redirect('/twilio/confirmName');
            break;
    }

    res.type('text/xml');
    res.send(twiml.toString());
});

router.post('/callStatus', async (req, res) => {
    const callStatus = req.body.CallStatus;
    const callSid = req.body.CallSid;
    const pulseId = req.query.pulseId;
    const linkedPulseId = req.query.linkedPulseId;
    const exitedCall = exitedCalls.get(callSid);
    const answeredBy = amdResults.get(callSid);

    console.log('Full request body:', JSON.stringify(req.body, null, 2));
    console.log('Call SID:', req.body.CallSid);
    console.log('Call Status:', req.body.CallStatus);
    console.log('Call Duration:', req.body.CallDuration);
    console.log('Answered By::', answeredBy);

    if (pulseId && linkedPulseId) {
        try {
            let statusText;

            if (
                answeredBy &&
                [
                    'machine_start',
                    'machine_end_beep',
                    'machine_end_silence',
                    'machine_end_other',
                ].includes(answeredBy)
            ) {
                statusText = 'Incomplete: No Answer';
                console.log(
                    `Call ${callSid} ended due to machine detection: ${answeredBy}`
                );
            } else if (
                ['initiated', 'ringing', 'answered', 'in-progress'].includes(
                    callStatus
                )
            ) {
                statusText = `In Progress: ${callStatus}`;
            } else {
                switch (callStatus) {
                    case 'completed':
                        const recognizedName = recognizedNames.get(callSid);
                        if (recognizedName) {
                            await mondayService.updateMondayContactName(
                                pulseId,
                                recognizedName
                            );
                            statusText = 'Completed: Name Received';
                        } else if (exitedCall) {
                            statusText = 'Completed: Call Exited';
                        } else {
                            statusText = 'Completed';
                        }
                        break;
                    case 'no-answer':
                        statusText = 'Incomplete: No Answer';
                        break;
                    case 'busy':
                        statusText = 'Incomplete: Busy';
                        break;
                    case 'failed':
                        statusText = 'Incomplete: Failed';
                        break;
                    case 'canceled':
                        statusText = 'Incomplete: Canceled';
                        break;
                    default:
                        statusText = 'Unknown Call Completion Status';
                }
            }

            await mondayService.updateCallStatus(linkedPulseId, statusText);
            console.log(`Call status updated to: ${statusText}`);

            if (
                [
                    'completed',
                    'no-answer',
                    'busy',
                    'failed',
                    'canceled',
                ].includes(callStatus)
            ) {
                recognizedNames.delete(callSid);
                exitedCalls.delete(callSid);
                amdResults.delete(callSid);
            }
        } catch (error) {
            console.error(
                'Error updating call status on Monday.com:',
                error.message
            );
            await mondayService.updateCallStatus(
                linkedPulseId,
                'Update Status Error'
            );
        }
    } else {
        console.log(
            'Missing linkedPulseId or pulseId, cannot update Monday.com'
        );
    }

    res.status(200).send('Call status received and processed.');
});

export default router;

I’ve tried:

  1. Different machine detection configurations

  2. Setting timeOuts to try and wait for AMD and putting it in handleCall before the call is made, but this of course causes a poor experience for real call recipients

Any suggestions? I’m about to ditch Twilio altogether.

Shift object to last index of an array

Let’s say I have this array of objects:

var array = [
    {
        "identifier": "A",
    },
    {
        "identifier": "B",
    },
    {
        "identifier": "C",
    },
    {
        "identifier": "D",
    },
]

How can I map/reduce this array to get this result:

var array = [
    {
        "identifier": "A",
    },
    {
        "identifier": "C",
    },
    {
        "identifier": "D",
    },
    {
        "identifier": "B",
    },
]

In other words, how can I put “B” at the end of array no matter how large the array is?

array.map((o) => {
  if (o.identifier === 'B') {
     // do what here?
  }
 return o;
})

How to modify requestHeaders on browser extentions javascript

I am developing a “basic” extension (addon) in firefox to modify the request headers I followed the official documentation here (copy/paste but didn’t work) https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeSendHeaders#details_2 changed it to the example below and still can’t make it work.

In my manifest I am asking for all the permits mentioned in the documentation:
//manifest.json

{
  ...
  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "storage",
        "tabs"
  ],
  ...
}

//background.js

let currentUserAgent = '';

function updateUserAgent(userAgent) {
  currentUserAgent = userAgent;
  console.log('User agent updated:', currentUserAgent); // User agent updated: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0
  console.log("Registering onBeforeSendHeaders event listener..."); //Registering onBeforeSendHeaders event listener...
  chrome.webRequest.onBeforeSendHeaders.addListener(
    handleRequest,
    { urls: ["<all_urls>"] },
    ["blocking", "requestHeaders"]
  );
  console.log(chrome.webRequest.onBeforeSendHeaders.hasListener(handleRequest)); //true
}

function handleRequest(details) {
  console.log('Handling request:', details); //NO LOG

  // Check if the 'user-agent' header exists
  const userAgentHeader = details.requestHeaders.find(header => header.name.toLowerCase() === 'user-agent');
  if (userAgentHeader) {
    console.log('User-Agent header found:', userAgentHeader.value); //NO LOG
    userAgentHeader.value = currentUserAgent;
    console.log('User-Agent header updated:', userAgentHeader.value); //NO LOG
  } else {
    console.log('User-Agent header not found in the request headers.'); // NO LOG
  }

  return { requestHeaders: details.requestHeaders };
}

// Retrieve the saved user agent on startup
chrome.storage.local.get('userAgent', (data) => {
  updateUserAgent(data.userAgent || 'Default User Agent');
});

// Listen for messages to update the user agent
chrome.runtime.onMessage.addListener(message => {
  if (message.action === 'updateUserAgent') {
    updateUserAgent(message.userAgent);
  }
});

Any ideas on how to make it work?

Other things I tried:
Changing the object chrome to browser, the result doesn’t change.

How do I know is not working?

When I go to the network tab the UA header hasn’t change. I also used burpsuite to capture the request and in fact the headers did not change.

ScrollTrigger from GSAP Freezes When Element Is Pinned

I’m experiencing an issue with GSAP’s ScrollTrigger plugin. Here’s the setup:

  • Inside a parent div with the ID #fimages, there are two child div elements: #fright and #fleft.
  • I want to pin the #fright div when it reaches the top of the viewport, while allowing the contents of #fleft to scroll.
  • The ScrollTrigger should end when the final element inside #fleft reaches the end of the viewport.

Issue:

Everything works as expected while scrolling through the entire website without stopping. However, when the #fright element gets pinned, if I stop scrolling, the page seems to freeze—meaning I can’t scroll using the scroll wheel. The scrollbar on the side still works, and if I use it, I notice that the website scrolls in the background, but the pinned div remains stuck.

I’ve provided a link to the website where you can see the issue in action.
live-server
github link to view the entire code
Does anyone have insights into why this might be happening or how to resolve it?

here is the code
Javascript:

     gsap.to(".fleftelm", {
          scrollTrigger: {
               trigger: "#fimages",
               pin: true,
               start: "top top",
               end: "bottom bottom",
               endTrigger: ".last",
               scrub: 1,
               // markers:true,    // use this markes if you want
          },
          y: "-300%",
          ease: Power1,
     });

HTML :

     <div id="fimages">
            <div id="fleft">
                <div class="fleftelm">
                    <h3>Riyadh</h3>
                    <h1>official website of riyadh, saudi arabia's capital.</h1>
                    <h4>design, development, product</h4>
                </div>
                <div class="fleftelm">
                    <h3>Riyadh</h3>
                    <h1>official website of riyadh, saudi arabia's capital.</h1>
                    <h4>design, development, product</h4>
                </div>
                <div class="fleftelm">
                    <h3>Riyadh</h3>
                    <h1>official website of riyadh, saudi arabia's capital.</h1>
                    <h4>design, development, product</h4>
                </div>
                <div class="fleftelm last">
                    <h3>Riyadh</h3>
                    <h1>official website of riyadh, saudi arabia's capital.</h1>
                    <h4>design, development, product</h4>
                </div>
            </div>
            <div id="fright">
                <div class="images">
                     <img src="https://cuberto.com/assets/projects/riyadh/cover.jpg" alt="">
                </div>
            </div>
        </div>

And yes i have included all the MDN

I implemented GSAP ScrollTrigger to pin #fimages and animate .fleftelm vertically. I expected smooth scrolling and pinned elements, but the scroll wheel becomes unresponsive when pinned.

Web worker does not have cache-control header

When I use fetch in javascript, the file has a cache-control header.

var StockFish='https://www.example.com/stockfish-16.1-single.js';
params={cache:'force-cache'};
try {await fetch(StockFish,params)} 

Fetch - Devtools

When I use a web worker, the file has no cache-control header.

stockfish=new Worker('https://www.example.com/stockfish-16.1-single.js')

Web server- Chrome Devtools

Does anyone know why this happens?

Deepest World uses Javascript trying to learn to mod [closed]

So i been playing a free game called Deepest World. i made a wood chopping bot and other.
Now i am trying to mod my inventory items. like change the wooden sword damage value or increase the number of wood i have. i am curious on what i can do with Javascript and this is the first game that got me interested in modding. i plan on learning Lua though to mod Hades ( it might Hades 2) and levels etc.

so i dont know much of Javascript or Lua ( i have only studied some C programming), i dont know what i can do with a scripting language.

so if i can use Javascript to alert my weapons and inventory please let me know? also just some good advice.

i tried using console.log(physwoodsword1) to bring up the objects info and tried
dw.log(physwoodsword1) to see it in chat wind also tried using Alert which just said “physwoodsword1”.

i was just going to try swapping the attack value from 24 to anything more as test if i am on the right track. i love the challenge but its been a week and no progress.