Firefox on iOS won’t open fetched blob files

In a react website, I invoke the following function to open a blob file (PDF or a few image types) which I have fetched from the server after the user clicks a button.

function viewDocument(userId, documentId, accessToken) {
    const url = new URL("https://my-api/my-controller/Document");
    url.searchParams.set("UserId", userId);
    url.searchParams.set("DocumentId", documentId);
    
    fetch(url.toString(), {
        method: 'get',
        mode: 'cors',
        credentials: 'include',
        headers: {
            authorization: "Bearer " + accessToken
        },
        body: null
    })
    .then((response) => {
        if (!response.ok) { throw response }
        response.blob().then((blob) => {
            const objUrl = window.URL.createObjectURL(new Blob([blob], {type: blob.type}));
            window.open(objUrl);
        });
    })
    .catch( error => {
        // handle error
    });
};

My content security policy includes the following:

default-src 'self';
frame-src 'self' blob:;
img-src 'self' blob:;
object-src 'self' blob:;

The .NET Core API code to fetch the document from the server looks like this:

[HttpGet("api/[controller]/Document")]
[Authorize]
public async Task<IActionResult> GetDocument(string userId, long documentId)
{
    var fileExtensions = new Dictionary<string, string>()
    {
        {".gif", "image/gif"},
        {".jpg", "image/jpg"},
        {".jpeg", "image/jpeg"},
        {".pdf", "application/pdf"},
        {".png", "image/png"},
        {".tif", "image/tif"},
        {".tiff", "image/tiff"}
    };
    var filePath = "some-file-path";
    var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    var contentType = fileExtensions[Path.GetExtension(filePath).ToLower()];
    return File(fileStream, contentType);
}

With this code and configuration, my website works as intended on the latest versions of Chrome (Android & iOS), Safari (iOS), Edge (Android & iOS), and Firefox (Android) by opening a new tab with the blob contents. In some instances, I had to give permissions or unblock popups in settings, and that is to be expected.

For some reason, it simply doesn’t open a new tab or show the fetched blob on Firefox (iOS) even though I have unblocked popups. I have connected the phone to a Mac and debugged the page … the blob is fetched without errors but the next command to open the blob URL is just being ignored for some reason. I have tested other websites on that browser and it seems to open new tabs just fine for them. For my website, it simply does nothing when I click the button. This behavior is similar to what I saw in Safari (iOS) before I unblocked popups in Safar settings, but unblocking popups in Firefox (iOS) settings has not helped at all … a new tab is not opened when I click the button. I am hoping someone has seen this behavior and knows what is going on.

Javascript: Pass an existing variable to a redirected url via Form submit(); [duplicate]

I need to swap out old html files to new asp files, these old files are currently live and in use.

Old URL: www.mysite.com/page1.html?variable1

New URL: www.mysite.com/page1.asp?variable1

I have this so far but it does not capture and pass the variable to the new URL. Oddly, it does pass along the “?” somehow:

<form id="myForm" action="page1.asp" method="get"></form>

<script language="javascript">
function myFunction() {
  document.getElementById("myForm").submit();
}
myFunction();
</script> 

How can I capture the variable from the old URL and pass it along to the new URL?

Is this even possible? I can’t find any examples of this process.

Thanks!

onClick and onclick in react.js

I have a button and two functions closeOnClick and openOnClick. The onClick property is set as openOnClick in which after carrying out the desired process the onclick property is set to closeOnClick. After I click the button again both onClick and onclick is taking place. After testing I found out that onclick and onClick are different.

function closeOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;

  sidebar.classList.remove('show');
  btn.onclick = openOnClick;
  console.log('closed menu and changed onclick to close')
}

function openOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;

  sidebar.classList.add('show');
  btn.onclick = closeOnClick;
  console.log('opened menu and changed onclick to close')
}
<button className="open-sidebar-btn" onClick={openOnClick}>
  click me       
</button>

When I click the button again closeOnClick and openOnClick runs.

  • I tried to change onClick but that isn’t a property of the element.
  • I also tried to set an if statement inside openOnClick.
let closed = true;

function closeOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;

  sidebar.classList.remove('show');
  // btn.onclick = openOnClick;
  console.log('closed menu and changed onclick to close')
  closed = true
}

function openOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;
  if (closed) {
    sidebar.classList.add('show');
  btn.onclick = closeOnClick;
  console.log('opened menu and changed onclick to close')
  closed = false;
  }
}

It looks like the onClick will run after onclick so I used useState, considering that the state is changed after a delay and after all process are done if I am correct.

But this is messing up even more.

Stimulus not working and getting MIME type error

I’m learning Ruby on Rails and wanted to start using its features together with Stimulus. Unfortunately, I’ve encountered a barrier that I don’t know the cause of. Specifically, my files are being detected as text/plain, and I’m getting a MIME type error.

I’m using Rails v8.0.2 and Bootstrap in the project. I’ve already tried many things, including reinstalling Stimulus and checking the imports—everything seems to match up and doesn’t appear to have any issues.

If anyone has had this problem themselves and knows how to solve it, I’d really appreciate the help in advance.

application.js

```// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails           

import “@hotwired/turbo-rails”
import “controllers”
import * as bootstrap from “bootstrap”

import “trix”
import “@rails/actiontext”“`

controllers/application.js

import { Application } from "@hotwired/stimulus"
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
export const application = Application.start()

// Configure Stimulus development experience
window.Stimulus  = application
application.debug = false

eagerLoadControllersFrom("controllers", application)

hello_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    console.log("TEST")
  }
}

index

<div data-controller="hello">
  Test Stimulus
</div>

application.html.erb

<%# Includes all stylesheet files in app/assets/stylesheets %>
    <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>

console

Uncaught TypeError: Specyfikator „bootstrap” był samym specyfikatorem, ale nie był do niczego przypisany. Względne specyfikatory modułów muszą zaczynać się od „./”, „../” lub „/”. application-b280f13e.js:4:28
GET
http://localhost:3000/assets/controllers/application
NS_ERROR_CORRUPTED_CONTENT

GET
http://localhost:3000/assets/controllers/hello_controller
NS_ERROR_CORRUPTED_CONTENT

GET
http://localhost:3000/assets/controllers/previews_controller
NS_ERROR_CORRUPTED_CONTENT

Wczytanie modułu z adresu „http://localhost:3000/assets/controllers/application” zostało zablokowane z powodu niedozwolonego typu MIME („text/plain”).
new
Wczytanie modułu z adresu „http://localhost:3000/assets/controllers/hello_controller” zostało zablokowane z powodu niedozwolonego typu MIME („text/plain”).
new
Wczytanie modułu z adresu „http://localhost:3000/assets/controllers/previews_controller” zostało zablokowane z powodu niedozwolonego typu MIME („text/plain”).
new
Stimulus
Uncaught ReferenceError: Stimulus is not defined
    <anonymous> debugger eval code:1

server

Started GET "/assets/controllers/application" for at 2025-07-10 13:18:48 +0200
Started GET "/assets/controllers/hello_controller" for  at 2025-07-10 13:18:48 +0200
Started GET "/assets/controllers/previews_controller" for at 2025-07-10 13:18:48 +0200

OpenLayers GeoTIFF Color Mapping Issue – All Data Shows as Single Color

I’m trying to apply color mapping to a GeoTIFF layer in OpenLayers 9.2.4, but I’m encountering an issue where the entire GeoTIFF is displayed as a single color instead of applying my color ranges properly.

Issue Description

I have a GeoTIFF file with raster data values that should be mapped to different colors based on value ranges. I’ve created a color mapping array and applied it using WebGLTile styling, but the visualization doesn’t match my color legend.

Expected Behavior

Different areas of the GeoTIFF should display different colors based on pixel values
The colors should match the ranges defined in my colorMappings array
Actual Behavior
The entire GeoTIFF is rendered with a single color
The color mapping doesn’t seem to be applied correctly

Here’s the relevant part of my code:

// Color mapping rules
const colorMappings = [
  { min: 0, max: 2, color: [0, 97, 0] },
  { min: 2, max: 4, color: [50, 128, 27] },
  // ... more ranges ...
];

// GeoTIFF source
const source = new ol.source.GeoTIFF({
  sources: [{ url: './test.tif' }],
  convertToRGB: false
});

// Color expression for styling
const colorExpression = [
  'case',
  ...colorMappings.flatMap((mapping) => [
    ['all', 
     ['>=', ['band', 1], mapping.min], 
     ['<', ['band', 1], mapping.max]
    ],
    ['color', ...mapping.color, 255]
  ]),
  ['color', 0, 0, 0, 0]
];

// WebGL tile layer with styling
const tiffLayer = new ol.layer.WebGLTile({
  source: source,
  style: { color: colorExpression }
});

What I Suspect

I suspect the issue might be related to the band index in the color expression. I’m currently using [‘band’, 1] to reference the data values, but I’m not sure if this is correct for my GeoTIFF file.

Possible issues:

  • OpenLayers band indexing might be 0-based instead of 1-based
  • My GeoTIFF might have multiple bands and I’m referencing the wrong one
  • The data values in my GeoTIFF might be outside the ranges I’ve defined
  • There might be a problem with how WebGLTile applies styling to GeoTIFF data

Questions

1、How can I determine which band index I should be using?
Is there a way to debug what values are actually in my GeoTIFF?
2、Are there any common issues with WebGLTile styling for GeoTIFF that I should be aware of?
3、Should I be configuring the source or style differently?
4、Any assistance would be greatly appreciated!

NOW_STATUS

How to make my API call not give me a string as an error response but the error’s body instead? [duplicate]

I have a problem with the management of an error for my API call in my Angular app:

sendRequest(request: BookingRequest): Observable<any>{
const authorityCode = this.tableService.enteId;
const baseUrl = this.getCarBookingBaseUrl(authorityCode);
 const url = `${baseUrl}/api/Booking`;
 const body = JSON.stringify(request);
return this.http.post<any>(url, body, { headers: this.buildBasicAuthHeaders() }).pipe(
  map(_ => _.bookingId || 'Number not found'),
  catchError(error => {
    const errorMessage = error?.error?.message || 'Unknown error';
    return throwError(() => new Error(`Error while sending request: ${errorMessage}`));
  })
);}

This is my call which works if there are no errors. However, in case I get an error 406, in the Chrome’s console I receive this in the response of the 406 error call:

{"message":"Invalid contract"}

This is what I want to receive when I subscribe to this function. In case I get error 406, I need to show this message on screen.
Problem is that error in the catchError is always like this:

"Http failure response for https:INSERT_URL_HERE: 406 OK"

How can I manage the JSON that the error sends back to me with the message I need to show?

Data fetching with pagination on nextui-org/react Autocomplete form input

I have an issue related to the Autocomplete component in nextui-org/react. I am trying to implement a form autocomplete input, which loads multiple languages by making a paginated requests to the server. I am kind of new to React and I am struggling with these features:

  • When the user scrolls to the bottom of the list, a request with the next page should be triggered, in order to fetch the next page of data.
  • When the user types on the input form, a search should be made to the existing data (the data that is already received in the frontend). If the match is not found there, then a request should be made to the server.
  • How to implement a debounce feature, so that the search is not applied every time I press a key.

Has anyone worked with this implementation before? If so, could you provide me some ideas?

I tried creating a Context which would wrap my form input. Each time I scrolled, I would fetch the next page, but the next page would replace my already existing data.

How to upload an image using Inertia.js & Laravel

I try to upload an image using Inertia.js and React with Laravel.

In my react component I have:

    const { data, setData, post, processing, errors } = useForm({
        img: null,    
    });

    const submit = (e) => {
        e.preventDefault();
        console.log(data.img, data.img instanceof File) // File, true
        post(route('admin.templates.store'), {
            forceFormData: true,
        })
    }

and jsx

<form onSubmit={submit} className="max-w-2xl mx-auto">
<div className="mb-4 w-full">
  <label htmlFor="image" className="label">* Image</label>
    <input 
      type="file" 
      name="img" 
      id="image" 
      accept="image/*" 
      required 
      className="input-b"
      onChange={e => setData('img', e.target.files[0])}
     />
</div>
</form>

In the Laravel store method the has_file is always false and the img filed is an empty array.
The store method is a POST.

public function store(Request $request) {
        dd([
            'has_file' => $request->hasFile('img'),
            'file' => $request->file('img'),
            'all' => $request->all(),
        ]);
    } 

Any suggestions? Thanks.

Google Apps Script SyntaxError: Identifier ‘firstDataRow’ declared on save

I’m working on a Google Apps Script to synchronize events from my Google Calendar to a Google Spreadsheet. The script is designed to:

  • Fetch events for specific monthly tabs in my spreadsheet (e.g., “SEPTEMBER – FY26 EDGE Stadium – Planning Events”).
  • Parse details like event name, attendees, ticket number, and location from the event description.
  • Combine new events with existing data on the sheet and sort them chronologically.
  • Place a “Last Sync” timestamp on a dedicated “Sync Log” sheet.
  • Critically, it’s configured to handle multiple header rows (4 rows) and specific data validation rules for meeting rooms.

I am consistently encountering a SyntaxError that prevents me from even saving the script. The error message is:
Syntax error: SyntaxError: Identifier 'firstDataRow' has already been declared line: 290 file: Untitled.gs

Key details about the problem:

  • Error occurs on saving: The script cannot be saved or run due to this syntax error.
  • Specific line: The error points to let firstDataRow = HEADER_ROWS_TO_SKIP + 1;
  • Variable scope: I understand that “Identifier has already been declared” usually means a variable is declared twice. However, firstDataRow is intended to be declared once per sheet iteration and used correctly thereafter. The code includes checks and structuring to avoid this.

Troubleshooting steps I’ve already tried:

  1. Created a brand new Apps Script project and deleted all default code.
  2. Copied and pasted the entire script (provided below) carefully, ensuring no lines were missed or extra characters included.
  3. Attempted saving in different web browsers.
  4. Cleared my browser’s cache and cookies.
  5. Confirmed my Google Calendar ID and Spreadsheet ID are correct.
  6. Confirmed the sheet naming convention is correct and the script sees all relevant sheets.
  7. Confirmed HEADER_ROWS_TO_SKIP is correctly set to 4 for my sheet’s structure (data starts on Row 5).
  8. The spreadsheet has data validation rules on certain columns (e.g., Column X and Y are manual inputs with dropdowns; the “Meeting Room” column also has dropdowns). The script has been updated to exclude manual columns from writes and to correctly parse meeting room names.

My question is:
Why would this specific SyntaxError (Identifier ‘firstDataRow’ has already been declared) occur upon saving the script, given the variable scoping appears correct, and what could be unique to my Apps Script environment that causes this? Is there a subtle corruption or state issue in the Apps Script editor itself that prevents proper compilation/saving of this code?


const now = new Date();
// --- UPDATED DATE RANGE LOGIC ---
// Set start date to September 1st of the current year
const startOfYear = new Date(now.getFullYear(), 8, 1); // Month is 0-indexed, so 8 is September

// If the current date is already past September 1st, start from 'now'. Otherwise, start from September 1st.
const startDate = (now > startOfYear) ? now : startOfYear;

// Set the end date to one year from the calculated start date
const futureDate = new Date(startDate);
futureDate.setFullYear(startDate.getFullYear() + 1);

const allEvents = calendar.getEvents(startDate, futureDate);
// --- END UPDATED DATE RANGE LOGIC ---

// --- NEW DEBUG LOG: Log ALL Sheet Names Found in the Spreadsheet ---
Logger.log('--- All Sheet Names Found in the Spreadsheet ---');
sheets.forEach(sheet => {
    Logger.log(`  Sheet Name: "${sheet.getName()}"`);
});
Logger.log('------------------------------------------------');
// --- END NEW DEBUG LOG ---

// --- DEBUG LOG: Check if your November event is even retrieved from the calendar ---
Logger.log('--- All Events Retrieved from Calendar (Title, Date, and Month Index) ---');
allEvents.forEach(event => {
    Logger.log(`  ${event.getTitle()} on ${event.getStartTime().toDateString()} (Month Index: ${event.getStartTime().getMonth()})`);
});
Logger.log('----------------------------------------------------------');
// --- END DEBUG LOG ---


sheets.forEach(sheet => {
  // Log for all sheets to debug if NOVEMBER tab is being skipped
  Logger.log(`Checking sheet: "${sheet.getName()}"`); 
  if (!monthTabPattern.test(sheet.getName())) {
    Logger.log(`Skipping sheet (name mismatch): "${sheet.getName()}"`); 
    return; 
  }

  const sheetMonthName = sheet.getName().split(" ")[0].toUpperCase();
  Logger.log(`Processing sheet: "${sheet.getName()}" (Sheet Month Name: ${sheetMonthName})`);


  // --- CRITICAL FIX FOR READING EXISTING TICKETS ON EMPTY SHEETS ---
  let existingTickets = new Set(); // Initialize as empty Set

  let lastRowWithContent = sheet.getLastRow(); // Get the last row with any content
  let firstDataRow = HEADER_ROWS_TO_SKIP + 1; // This is the first row where actual data should be (e.g., Row 5)

  // Only attempt to read existing tickets if there are actual data rows in the sheet
  if (lastRowWithContent >= firstDataRow) {
      const numRowsToRead = lastRowWithContent - firstDataRow + 1; // Calculate number of rows to read
      // Read existing tickets from the Ticket Number column
      const ticketsData = sheet.getRange(firstDataRow, COLUMN_INDICES['Ticket Number'] + 1, numRowsToRead, 1).getValues();
      ticketsData.flat().filter(Boolean).forEach(ticket => existingTickets.add(ticket));
  }
  // --- END CRITICAL FIX ---


  const eventsForMonth = allEvents.filter(e => {
      const eventMonthName = Utilities.formatDate(e.getStartTime(), "CET", "MMMM").toUpperCase(); // Get full month name
      Logger.log(`  FILTER CHECK: Event '${e.getTitle()}' (Cal Month Name: ${eventMonthName}) vs Sheet '${sheet.getName()}' (Sheet Month Name: ${sheetMonthName}). Match: ${eventMonthName === sheetMonthName}`);
      return eventMonthName === sheetMonthName;
  });

  Logger.log(`Found ${eventsForMonth.length} events for "${sheet.getName()}"`);

  const newRows = [];

  eventsForMonth.forEach(event => {
    const title = event.getTitle();
    const description = event.getDescription() || "";
    const location = event.getLocation() || "";

    Logger.log(`--- Processing Event: ${title} on ${event.getStartTime().toDateString()} ---`);

    const start = event.getStartTime();
    const end = event.getEndTime();

    // Helper to extract specific data from the description using regex
    const extract = (label) => {
      // FIXED REGEX: Ensured the lookahead group is properly terminated.
      const regex = new RegExp(`${label}:\s*(.*?)(?=(?:<br>|<p[^>]*>|<\/p>|<a[^>]*>|<\/a>|\n)|$)`, "i");
      
      const match = description.match(regex);
      const extractedValue = match ? match[1].trim().replace(/<[^>]*>/g, '') : ""; // Strip all HTML tags
      Logger.log(`  Extracted '${label}': '${extractedValue}'`); 
      return extractedValue;
    };

    // --- Data Extraction ---
    let eventNameFromDesc = extract("Name of event");
    const actualEventName = eventNameFromDesc || title; // Use extracted name, fallback to event title

    const rawPax = extract("Number of attendees").replace(/[^0-9]/g, ""); // Extract numbers only
    const pax = rawPax ? `${rawPax} PAX` : ''; // Format as 'XX PAX'
    Logger.log(`  Raw PAX: '${rawPax}', Formatted PAX: '${pax}'`);

    // SF Event Host: Now relies solely on extraction from description
    const sfEventHost = extract("Who the IBP is"); 
    Logger.log(`  SF Event Host: '${sfEventHost}'`);
    
    const ticket = extract("Event number");
    Logger.log(`  Ticket Number: '${ticket}'`);

    // --- Type Column Logic (Column C) ---
    let eventType = '';
    if (title.toUpperCase().includes("SIC")) {
      eventType = "SIC";
    } else {
      const typeMatch = description.match(/Whether it is internal/ external:s*(Internal|External)/i);
      eventType = typeMatch ? typeMatch[1] : ""; // Will be "Internal" or "External" based on description
    }
    Logger.log(`  Event Type (derived): '${eventType}'`);

    // --- Meeting Room Logic (Column F) ---
    let finalRoomName = "";
    const normalizedCalendarLocation = location.toLowerCase(); // For matching keys in LOCATION_MAPPING

    // 1. Check for exact direct matches in the LOCATION_MAPPING (case-insensitive)
    for (const calendarLocMapKey in LOCATION_MAPPING) {
        if (normalizedCalendarLocation.includes(calendarLocMapKey)) {
            finalRoomName = LOCATION_MAPPING[calendarLocMapKey];
            break;
        }
    }

    // 2. If not found in direct mapping, try to match against the dropdown options pattern
    if (!finalRoomName) {
        // Clean up calendar location for regex matching (remove prefixes like "Amsterdam - Edge Stadium-")
        let cleanedLocationForMatch = location.replace(/Amsterdam - Edge Stadium-/, '').replace(/ [Google Meet]/i, '').trim();
        // Also normalize "(XX)" to "(XX pax)" if that's how dropdowns are
        cleanedLocationForMatch = cleanedLocationForMatch.replace(/((d+))$/, '($1 pax)');

        // Escape special characters for regex from dropdown options
        // Handle both '&' and '&amp;' as they might appear.
        const escapedDropdownOptions = DROPDOWN_ROOM_OPTIONS.map(option => 
            option.replace(/[.*+?^${}()|[]\]/g, '\$&') // Escape common regex special chars
                  .replace(/&/g, '(?:&|&amp;)') // Handle both & and &amp;
        );
        
        // This regex tries to find any of the dropdown options anywhere in the cleaned location
        const roomPattern = new RegExp(`(${escapedDropdownOptions.join('|')})`, 'i');

        const match = cleanedLocationForMatch.match(roomPattern);
        if (match && match[1]) {
            finalRoomName = match[1].replace(/&amp;/g, '&'); // Use the captured group and convert &amp; back to &
        } else {
            Logger.log(`  Warning: No regex match found for cleaned location: '${cleanedLocationForMatch}'`);
        }
    }
    Logger.log(`  Final Meeting room name: '${finalRoomName}'`);


    // --- Duplicate Check ---
    Logger.log(`  Checking duplicate: Ticket '${ticket}', exists in sheet: ${existingTickets.has(ticket)}`);
    if (!ticket || existingTickets.has(ticket)) {
        if (!ticket) Logger.log(`Skipping event "${title}" because no ticket number was found.`);
        else Logger.log(`Skipping event "${title}" with ticket ${ticket} because it already exists.`);
        return; // This 'return' skips the event
    }
    Logger.log(`  Event "${title}" passed duplicate check.`);

    // --- Construct Row Data ---
    // Initialize with empty strings for columns A to W (TOTAL_COLUMNS: 23)
    const rowData = new Array(COLUMN_INDICES.TOTAL_COLUMNS).fill(''); 

    rowData[COLUMN_INDICES['Dayporter Assigned']] = ""; // Manual
    rowData[COLUMN_INDICES['Date of event']] = Utilities.formatDate(start, "CET", "dd/MM/yyyy");
    rowData[COLUMN_INDICES['Type']] = eventType;
    rowData[COLUMN_INDICES['Name of Event']] = actualEventName;
    rowData[COLUMN_INDICES['SF Event Host']] = sfEventHost;
    rowData[COLUMN_INDICES['Meeting room']] = finalRoomName; // Use the cleaned and validated room name
    rowData[COLUMN_INDICES['PAX']] = pax;
    rowData[COLUMN_INDICES['Start Time (CET)']] = Utilities.formatDate(start, "CET", "HH:mm");
    rowData[COLUMN_INDICES['Finish Time (CET)']] = Utilities.formatDate(end, "CET", "HH:mm");
    rowData[COLUMN_INDICES['Ticket Number']] = ticket;

    // Populate other fields if data is extractable or has defaults
    // These fields are filled up to Column W (index 22). Columns X and Y are left untouched.
    rowData[COLUMN_INDICES['Slack Channel']] = ""; 
    rowData[COLUMN_INDICES['SIC Guest Registration Done?']] = "";
    rowData[COLUMN_INDICES['Ground Floor Check-In']] = "";
    // Basic check: if 'Catering:' label exists, assume 'Yes', otherwise 'No'.
    rowData[COLUMN_INDICES['Catering YES/NO']] = extract("Catering") ? "Yes" : "No"; 
    rowData[COLUMN_INDICES['Catering What, Caterer & Time Status: request/pending/confirmed']] = extract("Catering"); // Full catering info
    rowData[COLUMN_INDICES['Menu']] = "";
    rowData[COLUMN_INDICES['Furniture Set Up']] = "";
    rowData[COLUMN_INDICES['Comments']] = description; // Full description can go here, or more parsed details
    rowData[COLUMN_INDICES['AV Requirements']] = extract("AV support");
    rowData[COLUMN_INDICES['AV Ticket']] = "";
    rowData[COLUMN_INDICES['Alcohol (SVP Approval) YES/NO']]= "";
    rowData[COLUMN_INDICES['Minors']]= "";
    rowData[COLUMN_INDICES['OOH Event']]= "";
    // 'Extra security requested?': Column X (index 23) - Manual, not managed by script
    // 'VP / C-Level': Column Y (index 24) - Manual, not managed by script

    newRows.push(rowData);
    Logger.log(`  Event "${title}" added to newRows.`); // Log if event is added
  });

  // --- DEBUG LOG: Check newRows.length before the 'if' condition ---
  Logger.log(`DEBUG: newRows.length for "${sheet.getName()}": ${newRows.length}`);
  // --- END NEW DEBUG LOG ---

  // --- CRITICAL LOGIC FIX: Explicitly handle empty newRows array ---
  if (newRows.length === 0) {
      Logger.log(`Skipping write operation for "${sheet.getName()}" because newRows is empty.`);
      // This explicit 'return' prevents the error "The number of rows in the range must be at least 1."
      // when newRows is truly empty for a sheet (e.g., April, July in your logs)
      return; // Exit processing for this sheet if no new rows to add/sort.
  }
  // --- END CRITICAL LOGIC FIX ---


  // This block will now only execute if newRows > 0
  let firstDataRow = HEADER_ROWS_TO_SKIP + 1;
  let lastUsedRowInSheet = sheet.getLastRow();
  
  // Determine the actual range of existing data to read.
  let numExistingDataRows = 0;
  if (lastUsedRowInSheet >= firstDataRow) {
      numExistingDataRows = lastUsedRowInSheet - firstDataRow + 1;
  }

  let allData = [];
  if (numExistingDataRows > 0) {
      // Read existing values within the defined data columns (A to W)
      allData = sheet.getRange(firstDataRow, 1, numExistingDataRows, COLUMN_INDICES.TOTAL_COLUMNS).getValues();
  }

  const combinedData = allData.concat(newRows);
  const sorted = combinedData.sort((a, b) => {
      // Get the date value from the appropriate column
      let aDateValue = a[COLUMN_INDICES['Date of event']];
      let bDateValue = b[COLUMN_INDICES['Date of event']];

      let aDate = new Date(0); // Default to epoch for empty/invalid dates
      let bDate = new Date(0);

      // Attempt to parse/use aDateValue
      if (aDateValue instanceof Date) {
        aDate = aDateValue;
      } else if (typeof aDateValue === 'string' && aDateValue.trim() !== '') {
        try {
          aDate = Utilities.parseDate(aDateValue.trim(), "CET", "dd/MM/yyyy");
        } catch (e) {
          Logger.log(`Warning: Could not parse date for A: '${aDateValue}' Error: ${e.message}`);
        }
      }

      if (bDateValue instanceof Date) {
        bDate = bDateValue;
      } else if (typeof bDateValue === 'string' && bDateValue.trim() !== '') {
        try {
          bDate = Utilities.parseDate(bDateValue.trim(), "CET", "dd/MM/yyyy");
        } catch (e) {
          Logger.log(`Warning: Could not parse date for B: '${bDateValue}' Error: ${e.message}`);
        }
      }
      
      return aDate.getTime() - bDate.getTime();
  });

  const newNumberOfRowsToWrite = sorted.length;
  const newNumberOfColsToWrite = COLUMN_INDICES.TOTAL_COLUMNS; 

  // Define the target range for writing the sorted data (A to W)
  const targetRange = sheet.getRange(firstDataRow, 1, newNumberOfRowsToWrite, newNumberOfColsToWrite);
  targetRange.setValues(sorted); // This overwrites the target area

  // IMPORTANT: If the number of sorted rows is *less* than the previously used rows,
  // clear the excess rows below the new data (in columns A to W)
  if (newNumberOfRowsToWrite < numExistingDataRows) {
      const clearStartRow = firstDataRow + newNumberOfRowsToWrite;
      const rowsToClearCount = numExistingDataRows - newNumberOfRowsToWrite;
      // Clear only the cells in the columns the script manages (A to W)
      sheet.getRange(clearStartRow, 1, rowsToClearCount, newNumberOfColsToWrite).clearContent();
      Logger.log(`Cleared ${rowsToClearCount} excess rows for tab: "${sheet.getName()}"`);
  }
  
  Logger.log(`${newRows.length} new events added and sheet sorted for tab: "${sheet.getName()}"`);

});

// --- FINAL FIX for "Last Sync" placement ---
// Place "Last Sync" on the "Sync Log" sheet, cell A1
const syncLogSheet = spreadsheet.getSheetByName("Sync Log");
if (syncLogSheet) {
  syncLogSheet.getRange('A1').setValue("Last Sync: " + Utilities.formatDate(now, "CET", "dd/MM/yyyy HH:mm:ss"));
  Logger.log('Last Sync timestamp updated on "Sync Log" sheet, A1.');
} else {
  Logger.log('Warning: "Sync Log" sheet not found. Could not update Last Sync timestamp.');
}
// --- END FINAL FIX ---

Thank you in advance for any insights or assistance!

AppsScript + JDBC + MySQL – retrieve DECIMAL value and add it to sheet as number

I have MySQL db which contains a stored procedure named spYearlyTotals.
The procedure accepts a year as a param and returns 13 columns. The first one is some string label, the latter ones are DECIMAL(15,2) which represent currency totals for the given label for each month.

I have an AppScript function which basically runs this procedure and displays the values in a google sheet:

function displayYearlyTotals(ss) {
  let totalsTab = ss.getSheetByName("totals tab");
  let year = totalsTab.getRange("A3").getValue();

  let conn = Jdbc.getConnection(_dbUrl, _dbUsername, _dbPassword);  
  try {
    let stmt = conn.prepareStatement('CALL spYearlyTotals(?)');
    stmt.setInt(1, year);
    let results = stmt.executeQuery();

    let row = Array();
    let data = Array();
    
    while (results.next()) {
      row = [];
      row.push(results.getString(1)); // label
      for (let i = 1; i <= 12; i++) {
        let amnt = results.getFloat(1 + i); // monthly values

        row.push(results.wasNull() ? null : amnt);
      }

      data.push(row);
    }

    totalsTab.getRange(3, 1, data.length, 13).setValues(data);
  }
  finally {
    conn.close();
  }

}

Using getFloat leads to small inaccuracies – decimal values are treated as floats which sometimes makes my values to differ by 0.01. This is not acceptable for me.
From the other hand – values are pushed to the range as numbers and google sheet formatting works fine for my output.

I have tried replacing getFloat(i+1) by getString(i+1) and getBigDecimal(i+1).toFixed(2). Values are retrieved accurately but they are treated as strings – thus my formatting in the sheet is lost.

What is the proper way to handle DECIMAL columns so that they are correctly passed to the sheet as numbers?

OATH 2.0 Code Implementation of Client Credentials Grant

I am currently working on an implementation of OATH 2.0 to generate access tokens. I would use the client credentials grant from OpenId:
https://www.npmjs.com/package/openid-client?activeTab=readme
But the Code is not working for me. I have used the exact same code as in the tutorial:

import * as client from "openid-client";

const server: URL = new URL(
  "*"
);
const clientId = "*";
const clientSecret = "*";
const scope = "*";
const resource = "*";

let config: client.Configuration = await client.discovery(
  server,
  clientId,
  clientSecret
);

let tokens: client.TokenEndpointResponse = await lib.clientCredentialsGrant(
  config,
  { scope, resource }
);

console.log("Token Endpoint Response", tokens);

These are my errors:

Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
Cannot find name 'lib'.

Has anyone experience with this? Thanks in regards!

How to transcribe local audio File/Blob with Transformers.js pipeline? (JSON.parse error)

I’m working on a browser-based audio transcription app using Transformers.js by Xenova. I’m trying to transcribe a .wav file selected by the user using the following code:

import { pipeline } from '@xenova/transformers';

async function transcribe(file) {
    console.log(file.name); // logs the selected .wav filename

    const transcriber = await pipeline(
        'automatic-speech-recognition',
        'Xenova/whisper-tiny.en'
    );

    if (!file) return;

    const url = file;
    const text = await transcriber(url);
}

This function is triggered when a user uploads a local .wav file (from an <input type="file" accept=".wav"></input>).

What I’ve Tried / Verified:

  • I’m importing the pipeline correctly from @xenova/transformers

  • The model name ‘Xenova/whisper-tiny.en’ is spelled correctly

  • I’m online (not offline mode)

  • I used the exact example from the docs which works with a remote URL like:

    const url = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/jfk.wav';
    

    But I’m trying to use a local File object (user uploads a .wav)

How to add php script into script? [duplicate]

In the PHP code below;

How can we modify it so that when the input changes, the value in it is assigned to $_SESSION[“roomId”]?
Latterly, I want to call the $_SESSION[“roomId”] from “myPhp.php”.
Thanks in advance for your help,

Best Wishes

<?php

    session_start();
    $_SESSION["roomId"]="";

    
?>

<!DOCTYPE html>
<html>
    <body>
        <script>

        function chkLen() {
                if(document.getElementById("dCell").value.length >0) {
                    var deg = document.getElementById("dCell").value;
                    <?php $_SESSION['roomId']=deg; ?>
                }

            }

        </script>

        <form name="myForm" onsubmit="myPhp.php">
        <input id="dCell" type="text" onChange="chkLen();" value="" />
        </form>
    </body> 
</html>

Handling Events Between Two Different Web Pages

Suppose Page 1 opens a new window (Page 2) using window.open.
Is it technically possible for an event on Page 2 (like clicking a button) to affect the content or behavior of Page 1?

For example, when a user clicks a “Confirm” button on Page 2, the popup window (Page 2) closes using window.close(), and at the same time, Page 1 navigates to a detail view or updates its content.

Is this kind of interaction possible between two pages?
If so, what would be the recommended or standard way to implement it?

Thank you in advance for your advice.

White space shown in page when displayed in browser [closed]

I have below HTML and Java script code.

function isNumberKey(evt) {
  var charCode = (evt.which) ? evt.which : evt.keyCode
  if (charCode > 31 && (charCode < 48 || charCode > 57)) {
    alert("Enter only numbers!");
    return false;
  }
  return true;
}

var getRdnUrl = "/peccgi/xmlhttp/getrdninfo"; // The server-side script

var animstart = 0;
var ErrorCnt = 0;
var MaxError = 0;
var Commerror = 0;
var timerID = 0;
var StopUpdate = true;
var Commerror = 0;
var UpdateRate = 1000;


function cycle_update(error, width, height) {
  const c = document.getElementById("progressCanvas");
  const ctx = c.getContext("2d");

  ctx.strokeRect(20, 20, width, height);
  // Create linear gradient
  const grad = ctx.createLinearGradient(0, 0, width, height);
  //Color c = Color.parseColor("#c0c0c0");
  if (error) {
    grad.addColorStop(0, "rgb(255, 0, 0)");
    grad.addColorStop(1, "rgb(255, 0, 0)");
  } else {
    //animstart = animstart > 1?animstart-1:animstart;
    grad.addColorStop(animstart, "rgb(0, 228, 0)");
    animstart += 0.25
    animstart = animstart > 1 ? animstart - 1 : animstart;
    grad.addColorStop(animstart, "rgb(0, 160, 0)");
    animstart += 0.25
    animstart = animstart > 1 ? animstart - 1 : animstart;
    grad.addColorStop(animstart, "rgb(255, 255,255)");
    animstart += 0.25
    animstart = animstart > 1 ? animstart - 1 : animstart;
    grad.addColorStop(animstart, "rgb(0, 196, 0)");
    animstart += 0.25
    animstart = animstart > 1 ? animstart - 1 : animstart;
    grad.addColorStop(animstart, "rgb(0, 228, 0)");
  }

  // Fill circle with gradient
  ctx.beginPath();
  ctx.rect(20, 20, width, height);
  //ctx.arc(30,30, 20, 0, 2 * Math.PI);
  ctx.fillStyle = grad;
  ctx.fill();
  animstart += 0.25;
  if (animstart > 1) animstart -= 1;
}

function StartUpdate() {
  cycle_update(false, 100, 40);
}
.rdnTableHeader {
  background-color: #005E60;
  color: #ffffff;
  font-weight: bold;
}

.progress {}

.ctrlbtn {
  background-color: #005E60;
  border: none;
  color: white;
  padding: 15px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
  cursor: pointer;
}
<html>

<head>
  <TITLE> Venkata info Page </TITLE>
</head>

<body>
  <div class="pageMain full">
    <p align="center">
    <h2>Venkata's Information </h2>
    </p>
  </div>
  <label for="lblUpdatePeriod">Update Period(sec): </label>
  <input name="period" type="number" id="period" onkeypress="return isNumberKey(event)" />
  <br><br>
  <p>Click on the Start button update page dynamically.</p>
  <button class="ctrlbtn" type="button" id="StartBtn" onclick="StartUpdate();">Start update</button>
  <div id="divCanvas"><canvas class="progress" width="200" height="200" id="progressCanvas"></canvas></div>
  <p> Copy right </p>
</body>

</html>

When I open page in browser I am seeing below snapshot. I require help on following points

  1. I am not able to find from where space is coming between Start update button and copy right.
  2. I also want to show progress button next to Start update button, when I click “Start update” button.
    Can any one help me in resolving above points. Thanks for your guidance
    enter image description here