How do I make an upload to a collection-types media field from nextjs14 to strapiv4

I am trying to create a page to upload files to a “content-type” called inscricoes, but so far I have not been successful.

Here is my code: https://pastebin.com/SELMPuRz

In this section, the last three formData.append calls were introduced later, but they don’t really matter in this case:

const formData = new FormData();
Array.from(files).forEach((file) => {
  formData.append("files", file); // Adding each file
});

formData.append("ref", "api::inscricao.inscricao"); // The content type where the file will be stored
formData.append("refId", inscricaoId); // ID of the "inscricao" to be updated
formData.append("field", "fileLink"); // The field where the file will be saved

The file upload works well:

const uploadRes = await fetch("https://api.pnp.cv/api/upload", {
  method: "POST",
  body: formData,
});

However, I was trying to manually add the media type, and only the titulo: file.name and publico: true are working. The ficheiro.data always comes back as null:

const existingFiles = inscricaoData.data.attributes.fileLink || [];

const fileIds = [
  ...existingFiles, // Keep the existing files
  ...uploadData.map((file) => ({
    titulo: file.name, // File name
    publico: true,
    ficheiro: {
      data: {
        attributes: {
          id: file.id, // File ID
          name: file.name,
          url: file.url, // Ensure that the `url` field is returned
          mime: file.mime, // MIME type of the file
          size: file.size, // File size
        },
      },
    },
  })),
];

This is the result after an upload:

{
  "data": {
    "id": 2,
    "attributes": {
      "NIF": "456564",
      "url": "626179ff-3bba-4e98-8469-58ef4a8e14ae",
      "code": "pnp-i",
      "nome_completo": "Ailtom Duarte",
      "email": "[email protected]",
      "sede": "Latada",
      "telefone": "2389536269",
      "categoria": "Branding",
      "nome_projeto": "TerraSystem",
      "con_criativo": "asasdsfasfasfasfs adfdsfd sfds fdfs ds fsd dsf dfs",
      "coord_prod": "yjygjyg",
      "dir_foto": "sdfdsf",
      "dir_art": "kuhuuluhhu",
      "realizador": "sdfsdf",
      "autor_jingle": "sdfsdf",
      "designer": "fdsfdsfds",
      "outras_consideracoes": "fdsfdsfdsf",
      "data_producao": "2024-12-24",
      "data_divulgacao": "2024-12-16",
      "data_apresentacao_publica": "2024-12-25",
      "editor": "sdfsd",
      "createdAt": "2024-05-01T11:59:56.499Z",
      "updatedAt": "2025-01-05T02:04:52.681Z",
      "publishedAt": "2024-11-06T12:49:14.559Z",
      "fileLink": [
        {
          "id": 29,
          "titulo": "20241031_114101-removebg-preview.png",
          "publico": true,
          "ficheiro": {
            "data": null
          }
        }
      ]
    }
  },
  "meta": {}
}

What I really want to do is upload multiple files on the fileLink field where ficheiro is the media field.

"fileLink": [
  {
    "id": 29,
    "titulo": "20241031_114101-removebg-preview.png",
    "publico": true,
    "ficheiro": {
      "data": null
    }
  }
]

how do I implement this?

If is there a better solution let me know, please.

Submit in .jsp file not ‘seeing’ dijit.byid tabs in associated Dojo/.js file

I have an index.jsp file that includes:

<input type="submit" class="button" id="exportGrids" value="Export Grids" />...

   <div>
    <div class="tabPane" title="User Inbox" id="userInboxTab">
     <div class="inboxTable" id="userInboxGrid></div>
    </div>
    <div class="tabPane" title="Groupbox Inbox" id="groupInboxTab">
     <div class="inboxTable" id="groupInboxGrid></div>
    </div>
   </div>

and a related Dojo/ index.js file comprising two tabs:


    on(dom.byId("exportGrids"), "click", function(){
        var exportData;
        
        dijit.byId("userInboxGrid").exportGrid("csv", function(gridData) {
            exportData = gridData;
            dom.byId("exportData").value = exportData;
        });
        
        dijit.byId("groupInboxGrid").exportGrid("csv", function(gridData) {
            exportData = gridData;
            dom.byId("exportData").value = exportData;
        });
        
        return true;
    });

Regardless of which tab is highlighted when the submit is clicked the exported csv only seems to ‘see’ the ‘User Inbox’ tab’s tabled data (coming sequentially first in the code?). I’ve tried several variations of the above code without success. Because it’s mostly successful it can’t be too far off from exporting either the User or Groupbox data depending on what tab is presently in play.

Any help/insight with this one would be greatly appreciated. It’s driving me nuts!! 😉

How to check if a track is already added to a WebRTC peer connection before adding it?

I’m working on a WebRTC application where I dynamically add audio tracks to a peerConnection after obtaining microphone permissions. I want to avoid adding duplicate tracks to prevent unnecessary signaling and offers. However, I’ve noticed that the track.id changes each time I call getUserMedia, making it difficult to check if a track has already been added.

Here’s the code I’m using to add tracks

const localStream = await navigator.mediaDevices.getUserMedia({
   video: false,
   audio: true,
})
localStream.getAudioTracks().forEach((track) => {
   mapPeerConnection.forEach((peerConnection, userId) => {
     peerConnection.addTrack(track, localStream)
     sendOffer(userId)
   })
})

Return a promise that resolves when a event is fired

I’m currently using https://www.npmjs.com/package/whatsapp-web.js package and wrote this funciton. I need it to return the client ready to use client eventually. Tried to do it with a promise that resolves when the “ready” event is called. It just didn’t work, it seems like the “ready” event is never called. Any ideas?

  await mongoose.connect(process.env.MONGODB_URI!)

  const store = new MongoStore({ mongoose: mongoose })
  const client = new Client({
    authStrategy: new RemoteAuth({
      store: store,
      backupSyncIntervalMs: 300000,
    }),
  })

  console.log("initializing client")
  client.initialize()

  return new Promise<wweb.Client>((resolve, reject) => {

    client.on("ready", () => {
      console.log("client is ready")
      resolve(client)
    })
  })
}```


  [1]: https://www.npmjs.com/package/whatsapp-web.js

In Quill, how to add one button to the toolbar to add some text?

What I’d like to do seems simple: I’d like to provide the users with an additional button on the standard Quill toolbar that allows the user to select the name of an image from a list and then add a an img-tag with the selected image as text at the position of the cursor. It is strictly browser-only, so no npm here. Maybe some REST to fetch the image list, but that’s not the issue. I found several example on the quilljs.com site and here at StackOverflow, but none of them gave me the necessary clues to pull this one off.

Is there a good example somewhere, or can you guide me to where I can find the info I’m looking for? How to add one button to the already existing one? How to present a list of names using Quill, if that can be accomplished using Quill? How to add text where the cursor is at?

TIA

How to create perfect hash with ASCII symbols as input, where output hash is always the same for each ASCII sequence, even after adding more later?

I have this code so far, which performs some kind of hashing. The goal is to map each ASCII string to a single Hangul Syllable unicode point:

const HANGUL_START = 0xAC00; // Start of Hangul Syllables block
const HANGUL_END = 0xD7A3; // End of Hangul Syllables block
const HANGUL_COUNT = HANGUL_END - HANGUL_START + 1; // 11,172 syllables

// Stable hash function
const hashString = (input) => {
    return Array.from(input).reduce(
        (hash, char) => hash * 31n + BigInt(char.charCodeAt(0)),
        0n
    );
};

// Map input strings to unique Hangul syllables
const mapToHangul = (inputs) => {
    // Compute hashes for all inputs
    const hashes = inputs.map((input) => ({
        input,
        hash: hashString(input),
    }));

    // Sort inputs by their hash values
    hashes.sort((a, b) => (a.hash < b.hash ? -1 : a.hash > b.hash ? 1 : 0));

    // Map each input to a unique Hangul syllable based on its rank
    const result = new Map();
    hashes.forEach((item, index) => {
        const hangulCode = HANGUL_START + (index % HANGUL_COUNT);
        result.set(item.input, String.fromCharCode(hangulCode));
    });

    return result;
};

// Example usage
const demonstrateMapping = () => {
    const inputs = ["Hello", "World", "ASCII", "Test123", "ExtraInput"];
    const mapping = mapToHangul(inputs);

    console.log("Mapping:");
    mapping.forEach((hangul, input) => {
        console.log(`${input} -> ${hangul}`);
    });
};

demonstrateMapping();

But, as I pieced together from various ideas online, I don’t feel like it will handle the case where, at first say I have 1000 ASCII strings (from 1 to 5 characters let’s say, each), and I compute the hashes. I get one set of hash values. Then I add 20 more ASCII strings and position them at arbitrary points in the ASCII strings array, then compute the hashes again. There will either be collisions (I can’t have any collisions), or if I use a used cache (Map), then the hashes will depend on the order of the input, which I also don’t want. A variant which has a sort of cache is here:

const HANGUL_START = 0xAC00; // Start of Hangul Syllables block
const HANGUL_END = 0xD7A3; // End of Hangul Syllables block
const HANGUL_COUNT = HANGUL_END - HANGUL_START + 1n; // 11,172 syllables

// Hash function: Purely maps a string to a bigint
const hashString = (input: string): bigint => {
    return Array.from(input).reduce(
        (hash, char) => hash * 31n + BigInt(char.charCodeAt(0)),
        0n
    );
};

// Maps a hash to a Hangul syllable deterministically
const mapHashToHangul = (hash: bigint): string => {
    const normalizedHash = Number(hash % HANGUL_COUNT);
    return String.fromCharCode(HANGUL_START + normalizedHash);
};

// Main function: Convert multiple strings into unique Hangul syllables
const hashMultipleToHangul = (inputs: string[]): Map<string, string> => {
    const usedCodes = new Set<number>(); // Track used Hangul syllables
    const resultMap = new Map<string, string>();

    for (const input of inputs) {
        const hash = hashString(input);
        let hangulCode = HANGUL_START + Number(hash % HANGUL_COUNT);

        // Resolve collisions by finding the next available Hangul code
        while (usedCodes.has(hangulCode)) {
            hangulCode = (hangulCode + 1 - HANGUL_START) % HANGUL_COUNT + HANGUL_START;
        }

        usedCodes.add(hangulCode);
        resultMap.set(input, String.fromCharCode(hangulCode));
    }

    return resultMap;
};

// Example usage
const inputs = [
    "Hello",
    "World",
    "ASCII",
    "Test123",
    "HelloWorld",
    "AnotherTest",
    "FinalString",
    "ExtraInput"
];

const results = hashMultipleToHangul(inputs);
console.log("Hashed Hangul results:");
for (const [input, hangul] of results.entries()) {
    console.log(`${input} -> ${hangul}`);
}

For reference, my input ASCII strings are the i field from this dynamically generated dataset (and the x: getSingleGlyph() is what I want to replace with something more deterministic):

import st from '@lancejpollard/script-tree'

// https://en.wikipedia.org/wiki/Hangul_Syllables
// first hangul jamo syllable
const Xi = parseInt('AC00', 16)
// last hangul jamo syllable
const Xf = parseInt('D7A3', 16)
let X = Xi

const m = {
  u: {
    grave: 'u0300',
    acute: 'u0301',
    dacute: 'u030B',
    dgrave: 'u030F',
    up: 'u0302',
    down: 'u030C',
    dot: 'u0307',
    ddot: 'u0308',
    ring: 'u030A',
    tilde: 'u0303',
    macron: 'u0304',
    hook: 'u0309',
  },
  d: {
    grave: 'u0316',
    acute: 'u0317',
    ring: 'u0325',
    dot: 'u0323',
    ddot: 'u0324',
    down: 'u032C',
    tilde: 'u0330',
    macron: 'u0331',
    cedilla: 'u0327',
    up: 'u032D',
    hook: 'u0328',
  },
}

const D: Record<string, string> = {
  '--': m.u.dgrave,
  '-': m.u.grave,
  '++': m.u.dacute,
  '+': m.u.acute,
  '//': `${m.d.hook}${m.u.dacute}`, // rising 2 (vietnamese ngã)
  '/': `${m.d.hook}${m.u.acute}`, // rising (vietnamese sắc)
  '\/': m.u.down, // falling rising (vietnamese hỏi)
  '/\': m.u.up, // rising falling
  '\\': `${m.d.hook}${m.u.dgrave}`, // falling 2 (vietnamese nặng)
  '\': `${m.d.hook}${m.u.grave}`, // falling (vietnamese huyền)
  '^': m.u.dot, // accent/stress mark
  $: m.d.ddot,
  '&': m.d.tilde,
  _: m.u.macron, // long vowel
  '@': m.d.grave, // non-syllabic
  '!': m.d.macron, // short vowel
  '': '',
}

const G: Record<string, string> = {
  I: `i${m.d.dot}`,
  E: `e${m.d.dot}`,
  A: `a${m.d.dot}`,
  O: `o${m.d.dot}`,
  U: `u${m.d.dot}`,
  i: `i`,
  e: `e`,
  a: `a`,
  o: `o`,
  u: `u`,
}

export type Take = {
  i: string
  x: string
  o: string
  name?: string
  o2?: string
}

export const VOWELS: Array<Take> = []

export const BASE_VOWEL_GLYPHS = [
  'I',
  'E',
  'A',
  'O',
  'U',
  'i',
  'e',
  'a',
  'o',
  'u',
]
export const TONE_MARKS = [
  '--',
  '-',
  '++',
  '+',
  '/',
  '//',
  '\/',
  '/\',
  '\\',
  '\',
  '',
]
export const VARIANT_MARKS = ['$', '']
export const NASAL_MARKS = ['&', '']
export const DURATION_MARKS = ['_', '!', '']
export const SYLLABIC_MARKS = ['@', '']
export const ACCENT_MARKS = ['^', '']

BASE_VOWEL_GLYPHS.forEach(g => {
  ACCENT_MARKS.forEach(a => {
    DURATION_MARKS.forEach(l => {
      SYLLABIC_MARKS.forEach(s => {
        NASAL_MARKS.forEach(n => {
          VARIANT_MARKS.forEach(v => {
            TONE_MARKS.forEach(t => {
              const i = `${g}${v}${n}${s}${t}${l}${a}`
              const x = g.match(/i/i) && a === '^' ? 'ï' : G[g]
              const y = x === 'ï' ? '' : D[a]
              const x2 = v === '$' && x === 'u' ? 'r' : x
              const v2 = v === '$' && g === 'u' ? '' : `${D[v]}`
              const o =
                l === '!'
                  ? `${x2}${y}${D[l]}${D[n]}${D[s]}${D[t]}${v2}`
                  : `${x2}${D[l]}${D[n]}${D[s]}${D[t]}${v2}${y}`
              VOWELS.push({ i, x: getSingleGlyph(), o })
            })
          })
        })
      })
    })
  })
})

export const SYMBOLS = [
  { i: '=.', x: getSingleGlyph(), o: '.' },
  { i: '=?', x: getSingleGlyph(), o: '?' },
  { i: '=!', x: getSingleGlyph(), o: '!' },
  { i: '=+', x: getSingleGlyph(), o: '+' },
  { i: '=-', x: getSingleGlyph(), o: '-' },
  { i: '>', x: getSingleGlyph(), o: '>' },
  { i: '<', x: getSingleGlyph(), o: '<' },
  { i: '/', x: getSingleGlyph(), o: '/' },
  { i: '\', x: getSingleGlyph(), o: '\' },
  { i: '|', x: getSingleGlyph(), o: '|' },
  { i: '(', x: getSingleGlyph(), o: '(' },
  { i: ')', x: getSingleGlyph(), o: ')' },
  { i: '[', x: getSingleGlyph(), o: '[' },
  { i: ']', x: getSingleGlyph(), o: ']' },
  { i: ' ', x: getSingleGlyph(), o: ' ' },
]

export const NUMERALS = [
  { i: '0', x: getSingleGlyph(), o: '0' },
  { i: '1', x: getSingleGlyph(), o: '1' },
  { i: '2', x: getSingleGlyph(), o: '2' },
  { i: '3', x: getSingleGlyph(), o: '3' },
  { i: '4', x: getSingleGlyph(), o: '4' },
  { i: '5', x: getSingleGlyph(), o: '5' },
  { i: '6', x: getSingleGlyph(), o: '6' },
  { i: '7', x: getSingleGlyph(), o: '7' },
  { i: '8', x: getSingleGlyph(), o: '8' },
  { i: '9', x: getSingleGlyph(), o: '9' },
]

export const CONSONANTS = [
  { i: '@', x: getSingleGlyph(), o: `@` },
  { i: 'h~', x: getSingleGlyph(), o: `ɦ` },
  { i: 'm', x: getSingleGlyph(), o: `m` },
  { i: 'N', x: getSingleGlyph(), o: `n${m.d.dot}` },
  { i: 'n', x: getSingleGlyph(), o: `n` },
  { i: 'q', x: getSingleGlyph(), o: `n${m.u.dot}` },
  { i: 'G~', x: getSingleGlyph(), o: `g${m.u.tilde}` },
  { i: 'G', x: getSingleGlyph(), o: `g${m.u.dot}` },
  { i: 'g?', x: getSingleGlyph(), o: `g${m.u.grave}` },
  { i: 'g', x: getSingleGlyph(), o: `g` },
  { i: "'", x: getSingleGlyph(), o: `'` },
  { i: 'Q', x: getSingleGlyph(), o: `q${m.u.dot}` },
  { i: 'd?', x: getSingleGlyph(), o: `d${m.d.grave}` },
  { i: 'd!', x: getSingleGlyph(), o: `d${m.d.acute}` },
  { i: 'd*', x: getSingleGlyph(), o: `d${m.d.down}` },
  { i: 'd.', x: getSingleGlyph(), o: `d${m.d.macron}` },
  { i: 'D', x: getSingleGlyph(), o: `d${m.d.dot}` },
  { i: 'dQ~', x: getSingleGlyph(), o: `d${m.d.tilde}` },
  { i: 'd', x: getSingleGlyph(), o: `d` },
  { i: 'b?', x: getSingleGlyph(), o: `b${m.d.grave}` },
  { i: 'b!', x: getSingleGlyph(), o: `b${m.d.acute}` },
  { i: 'b', x: getSingleGlyph(), o: `b` },
  { i: 'p!', x: getSingleGlyph(), o: `p${m.u.acute}` },
  { i: 'p*', x: getSingleGlyph(), o: `p${m.u.up}` },
  { i: 'p.', x: getSingleGlyph(), o: `t${m.u.macron}` },
  { i: 'p@', x: getSingleGlyph(), o: `x${m.u.down}` },
  { i: 'p', x: getSingleGlyph(), o: `p` },
  { i: 'T!', x: getSingleGlyph(), o: `t${m.d.dot}${m.d.acute}` },
  { i: 'T', x: getSingleGlyph(), o: `t${m.d.dot}` },
  { i: 't!', x: getSingleGlyph(), o: `t${m.d.acute}` },
  { i: 't*', x: getSingleGlyph(), o: `t${m.d.down}` },
  { i: 'tQ~', x: getSingleGlyph(), o: `t${m.d.tilde}` },
  { i: 't@', x: getSingleGlyph(), o: `t${m.d.up}` },
  { i: 't.', x: getSingleGlyph(), o: `t${m.d.macron}` },
  { i: 't', x: getSingleGlyph(), o: `t` },
  { i: 'k!', x: getSingleGlyph(), o: `k${m.d.acute}` },
  { i: 'k.', x: getSingleGlyph(), o: `k${m.d.macron}` },
  { i: 'k*', x: getSingleGlyph(), o: `k${m.d.down}` },
  { i: 'K!', x: getSingleGlyph(), o: `k${m.d.dot}${m.d.acute}` },
  { i: 'K', x: getSingleGlyph(), o: `k${m.d.dot}` },
  { i: 'k', x: getSingleGlyph(), o: `k` },
  { i: 'H!', x: getSingleGlyph(), o: `h${m.d.dot}${m.d.acute}` },
  { i: 'H', x: getSingleGlyph(), o: `h${m.d.dot}` },
  { i: 'h!', x: getSingleGlyph(), o: `ħ` },
  { i: 'h', x: getSingleGlyph(), o: `h` },
  { i: 'J', x: getSingleGlyph(), o: `ȷ̈` },
  { i: 'j!', x: getSingleGlyph(), o: `j${m.u.acute}` },
  { i: 'j', x: getSingleGlyph(), o: `j` },
  { i: 'S!', x: getSingleGlyph(), o: `s${m.d.dot}${m.u.acute}` },
  { i: 's!', x: getSingleGlyph(), o: `s${m.u.acute}` },
  { i: 'S', x: getSingleGlyph(), o: `s${m.d.dot}` },
  { i: 'sQ~', x: getSingleGlyph(), o: `s${m.d.tilde}` },
  { i: 's@', x: getSingleGlyph(), o: `s${m.d.up}` },
  { i: 's', x: getSingleGlyph(), o: `s` },
  { i: 'F', x: getSingleGlyph(), o: `f${m.d.dot}` },
  { i: 'f!', x: getSingleGlyph(), o: `f${m.d.acute}` },
  { i: 'f', x: getSingleGlyph(), o: `f` },
  { i: 'V', x: getSingleGlyph(), o: `v${m.d.dot}` },
  { i: 'v', x: getSingleGlyph(), o: `v` },
  { i: 'z!', x: getSingleGlyph(), o: `z${m.u.acute}` },
  { i: 'zQ~', x: getSingleGlyph(), o: `z${m.d.tilde}` },
  { i: 'z', x: getSingleGlyph(), o: `z` },
  { i: 'Z!', x: getSingleGlyph(), o: `z${m.d.dot}${m.u.acute}` },
  { i: 'Z', x: getSingleGlyph(), o: `z${m.d.dot}` },
  { i: 'CQ~', x: getSingleGlyph(), o: `c${m.d.dot}${m.u.tilde}` },
  { i: 'C', x: getSingleGlyph(), o: `c${m.d.dot}` },
  { i: 'cQ~', x: getSingleGlyph(), o: `c${m.u.tilde}` },
  { i: 'c', x: getSingleGlyph(), o: `c` },
  { i: 'L', x: getSingleGlyph(), o: `l${m.d.dot}` },
  { i: 'l*', x: getSingleGlyph(), o: `l${m.d.down}` },
  { i: 'lQ~', x: getSingleGlyph(), o: `l${m.d.tilde}` },
  { i: 'l', x: getSingleGlyph(), o: `l` },
  { i: 'R', x: getSingleGlyph(), o: `r${m.d.dot}` },
  { i: 'rQ~', x: getSingleGlyph(), o: `r${m.u.tilde}` },
  { i: 'r', x: getSingleGlyph(), o: `r${m.u.dot}` },
  { i: 'x!', x: getSingleGlyph(), o: `x${m.u.acute}` },
  { i: 'X!', x: getSingleGlyph(), o: `x${m.d.dot}${m.u.acute}` },
  { i: 'X', x: getSingleGlyph(), o: `x${m.d.dot}` },
  { i: 'x@', x: getSingleGlyph(), o: `x${m.d.up}` },
  { i: 'x', x: getSingleGlyph(), o: `x` },
  { i: 'W', x: getSingleGlyph(), o: `w${m.u.dot}` },
  { i: 'w!', x: getSingleGlyph(), o: `w${m.u.acute}` },
  { i: 'w~', x: getSingleGlyph(), o: `w${m.d.dot}` },
  { i: 'w', x: getSingleGlyph(), o: `w` },
  { i: 'y~', x: getSingleGlyph(), o: `y${m.u.dot}` },
  { i: 'y', x: getSingleGlyph(), o: `y` },
]

export const GLYPHS = [
  ...VOWELS,
  ...CONSONANTS,
  ...SYMBOLS,
  ...NUMERALS,
]

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const tree = st.fork(GLYPHS)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
const make = (text: string): string => st.form(text, tree)

make.inputs = (text: string): Array<string> =>
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return
  st.list(text, tree).map((x: any) => x.i)

make.readableOutput = (text: string): Array<string> =>
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return
  st.list(text, tree).map((x: any) => x.o)

make.readable = (text: string): string =>
  make.readableOutput(text).join('')

make.machineOutputs = (text: string): Array<string> =>
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return
  st.list(text, tree).map((x: any) => x.x)

make.machine = (text: string): string =>
  make.machineOutputs(text).join('')

export default make

function getSingleGlyph() {
  if (X === Xf) {
    throw new Error(`Too many glyphs created`)
  }

  return String.fromCodePoint(X++)
}

How can I make a hashing function which takes my ASCII strings in my array (GLYPHS array above, in the i field), and maps it to a Hangul Jamo Syllable, in such a way that, if in the future I add more items to the GLYPHS array (in arbitrary spots), the old mappings will stay the same, and the new ones won’t create any conflicts? Is it possible? If so, how to do? If it’s not possible, why is it not possible? In that case I might have to resort to manually selecting Hangul glyphs by hand and setting them, but that will be a pain to do and pain to maintain. Would like a hashing solution so I don’t need to do it by hand, but all the hashing solutions I’ve thought about so far seems to have the problem that if I add new items later, the hashing values will all change. Can that be avoided?

Note: There are about 11,000 Hangul Jamo Syllables in that unicode block. I will have much less than that number of input ASCII strings, so we won’t run out of space.

TypeError with firebase authentication

I have an application that is relying on firebase auth, and I’m getting an error stating “Cannot read properties of undefined (reading ‘settings)”. Nothing seems to be wrong on the server side, anyone know how to fix this?

Here’s my code:

import {initializeApp} from "https://www.gstatic.com/firebasejs/11.1.0/firebase-app.js";
import {
    getAuth,
    signInWithEmailAndPassword,
    signOut,
} from "https://www.gstatic.com/firebasejs/11.1.0/firebase-auth.js";

const firebaseConfig = {
    //correct config
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const form = document.querySelector("form");

window.addEventListener("beforeunload", (event) => {
    // Check if you really want to prevent the refresh
    if (confirm("Are you sure you want to leave this page?")) {
        // Allow the refresh
    } else {
        // Prevent the refresh
        event.preventDefault();
        event.returnValue = ""; // Required for some browsers
    }
});
async function login() {
    const email = document.getElementById("username").value;
    const password = document.getElementById("pw").value;

    try {
        const userCredential = await signInWithEmailAndPassword(email, password);
        console.log(userCredential);
        const idToken = await userCredential.user.getIdToken();
        alert("signin function complete");

        const response = await fetch("http://localhost:3000/auth/login", {
            method: "POST",
            credentials: "include",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ idToken }),
        });

        if (response.ok) {
            alert("Logged in successfully!");
        } else {
            alert("Login failed: " + (await response.text()));
        }
    } catch (error) {
        alert("Error: " + error.message);
        console.error(error);
    }
}

async function logout() {
    await fetch("http://localhost:3000/logout", {
        method: "POST",
        credentials: "include",
    });
    alert("Logged out successfully!");
}

async function checkProfile() {
    const response = await fetch("http://localhost:3000/profile", {
        method: "GET",
        credentials: "include",
    });

    if (response.ok) {
        alert(await response.text());
    } else {
        alert("Access denied: " + (await response.text()));
    }
}

window.login = login;
window.logout = logout;
window.checkProfile = checkProfile;

and the error:
enter image description here

TypeError: Cannot read properties of undefined (reading 'settings')
    at _isFirebaseServerApp (internal.ts:173:37)
    at signInWithEmailAndPassword (email_and_password.ts:352:7)
    at login (login.js:37:36)
    at HTMLButtonElement.onclick (home/:182:76)

I don’t think my config is wrong since i’ve used it in other parts. Anyone know a fix?

I tried reading other questions, but none of the fixes seem to work. I even switched the version to a previous version, and still didn’t work.

Eris read embeds

I wanted to work on reading embeds with my eris bot in Javascript
Well it dosnt realy work as expected
If someone knows how to make it work please tell me
if it dosnt work please tell me i worked kinda a long time on it and i think it would be sad to just throw it away
here are the logs

Received message: <@1204873782519398520> test from toxiclikeme1599 (ID: 1015941241533366366) in channel 1325089634946125868
Received message: .dep 1015941241533366366 1000 from EHFB | Finance Bank Bot (ID: 1204873782519398520) in channel 1325089634946125868
Detected .dep command
User mention: 1015941241533366366, Amount: 1000
Waiting for .pay message…
Received message: .pay <@1325065822636609578> 1000 from toxiclikeme1599 (ID: 1015941241533366366) in channel 1197500600791863346
Filter check – Channel: true, Command: true, Mention: false
Received message: from UnbelievaBoat (ID: 292953664492929025) in channel 1197500600791863346
Filter check – Channel: true, Command: false, Mention: false

bot.on('messageCreate', async (msg) => {
    console.log(
        `Received message: ${msg.content} from ${msg.author.username} (ID: ${msg.author.id}) in channel ${msg.channel.id}`
    );

    if (
        msg.channel.id === BOT_CHANNEL_ID &&
        msg.content.startsWith('.dep ') &&
        msg.author.id === '1204873782519398520'
    ) {
        console.log('Detected .dep command');
        const args = msg.content.split(' ');

        if (args.length !== 3 || isNaN(args[2])) {
            bot.createMessage(msg.channel.id, 'Invalid command format. Usage: `.dep <@user> <amount>`');
            return;
        }

        const userMention = args[1];
        const userID = userMention.replace(/[<@!>]/g, ''); // Extract user ID from mention
        const amount = args[2];
        console.log(`User mention: ${userMention}, Amount: ${amount}`);

        const filterPay = (m) => {
            const isCorrectChannel = m.channel.id === PAY_CHANNEL_ID;
            const isPayCommand = m.content.startsWith('.pay');
            const includesUserMention =
            m.content.includes(`<@${userID}>`) || m.content.includes(`<@!${userID}>`);

            console.log(
                `Filter check - Channel: ${isCorrectChannel}, Command: ${isPayCommand}, Mention: ${includesUserMention}`
            );
            return isCorrectChannel && isPayCommand && includesUserMention;
        };

        console.log('Waiting for .pay message...');
        const payMessage = await waitForMessage(filterPay, 600000);
        if (!payMessage) {
            bot.createMessage(msg.channel.id, 'No `.pay` message received within the time limit.');
            return;
        }
        console.log('Received .pay message:', payMessage.content);

        const filterResponse = (m) => {
            const hasEmbed = m.embeds && m.embeds.length > 0;
            if (!hasEmbed) return false;

            const embed = m.embeds[0];
            const descriptionMatches =
            embed.description.includes(`<@${userID}>`) || embed.description.includes(`<@!${userID}>`);
            const authorMatches = embed.author && embed.author.name === payMessage.author.username;

            console.log(
                `Response filter check - Has Embed: ${hasEmbed}, Description Matches: ${descriptionMatches}, Author Matches: ${authorMatches}`
            );
            return descriptionMatches && authorMatches;
        };

        console.log('Waiting for response embed from UnbelievaBoat...');
        const responseMessage = await waitForMessage(filterResponse, 600000);
        if (!responseMessage) {
            bot.createMessage(msg.channel.id, 'No response embed received within the time limit.');
            return;
        }
        console.log('Received response embed:', responseMessage.embeds[0].description);
    }
});

async function waitForMessage(filter, time) {
    return new Promise((resolve) => {
        const timeout = setTimeout(() => resolve(null), time);

        const listener = (message) => {
            if (filter(message)) {
                clearTimeout(timeout);
                bot.off('messageCreate', listener);
                resolve(message);
            }
        };
        bot.on('messageCreate', listener);
    });
}```

Responsiveness textContent (graphic)

I’m trying to adjust the textContent.width property so that it responds as follows:

When the screen size increases (CTRL +) the overflow: truncate and ellipsis: “...” appear gradually so as not to leave the element (vBox). And when it’s the other way around (CTRL -), the text appears normally inside vBox. In other words, the text must always adapt to the screen resize.

In other words, the textContent must match the size of the vBox in both cases. I managed to make the vBox responsive, but I need to adjust the text inside it as well.

document.addEventListener("DOMContentLoaded", () => {
    // rounded rectangle
    const valueBox = echarts.graphic.extendShape({
        type: "vBox",
        shape: {
            r: 5
        },
        style: {
            lineWidth: .5,
            stroke: "#000",
            lineDash: 'solid',
            shadowBlur: 7.5,
            shadowColor: '#000', 
            shadowOffsetX: 3,
            shadowOffsetY: 3
        },
        buildPath: (ctx, shape) => {
            echarts.graphic.Rect.prototype.buildPath.call(this, ctx, shape);
        }
    });

    // register shape
    echarts.graphic.registerShape("vBox", valueBox);

    // init
    const chartUse = echarts.init(document.getElementsByClassName("chart")[0]);

    // dimensions
    const dimensionsInit = [chartUse.getWidth(), chartUse.getHeight()];

    window.addEventListener("resize", () => {
        // plot resize
        chartUse.resize();
        // graphic resize
        const dimensionsResize = [chartUse.getWidth(), chartUse.getHeight()];
        chartUse.setOption({
            graphic: [
                {
                    children: [
                        {
                            shape: {
                                x: (dimensionsResize[0] - dimensionsResize[0] / 5) / 2, // Centraliza horizontalmente
                                y: (dimensionsResize[1] - dimensionsResize[1] / 5) / 2, // Centraliza verticalmente
                                width: dimensionsResize[0] / 5,
                                height: dimensionsResize[1] / 5
                            },
                            textContent: {
                                width: dimensionsResize[0],
                                overflow: 'truncate',
                                ellipsis: '...',
                                align: "center",
                                style: {
                                    rich: {
                                        text: {
                                            lineHeight: dimensionsResize[1] / 28,
                                            fill: '#000',
                                            fontSize: dimensionsResize[0] / 70
                                        }
                                    }
                                }
                            },
                            textConfig: {
                                position: 'inside'
                            }
                        }
                    ] 
                }
            ]
        });
    });

    function chartFrameSwitch0 () {

        const gradColor1 = new echarts.graphic.LinearGradient(0, 0, 1, 1, [
            { offset: .5, color: "#fafac0" },
            { offset: 1, color: "#008cba" }
        ])

        const graphic0 = [
            {
                type: "group",
                children: [
                    {
                        type: 'vBox',
                        shape: {
                            x: (dimensionsInit[0] - dimensionsInit[0] / 5) / 2, // Centraliza horizontalmente
                            y: (dimensionsInit[1] - dimensionsInit[1] / 5) / 2, // Centraliza verticalmente
                            width: dimensionsInit[0] / 5,
                            height: dimensionsInit[1] / 5
                        },
                        style: {
                            fill: gradColor1,
                            lineCap: "x",
                            lineWidth: 2,
                            stroke: '#000'
                        },
                        textContent: {
                            style: {
                                text: "{text|Overflowwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwn50%}",
                                width: ( (dimensionsInit[0] / 10) - (dimensionsInit[0] / 10) * .15 ),
                                overflow: 'truncate',
                                stroke: '#fff',
                                align: "center",
                                rich: {
                                    text: {
                                        lineHeight: dimensionsInit[1] / 28,
                                        fill: '#000',
                                        fontSize: dimensionsInit[0] / 70
                                    }
                                }
                            }
                        },
                        textConfig: {
                            position: "inside"
                        }
                    }
                ]
            }
        ];

        const option = {
            graphic: graphic0
        };

        chartUse.setOption(option);

    }

    chartFrameSwitch0();

});
<head>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
    <title>Custom Shape Example</title>
</head>

<div class="chart" style="width: 100%; height: 100vh;"></div>

So how do you make the text inside a graphic component always responsive to its size?

This is the way I found, I don’t know if there is another solution that is more robust than this and perhaps simpler.

Note that you will need to run this code in an IDE and process it in the browser to check for this resizing problem.

JavaScript event loop does NOT “run to completion”

According to MDN, messages in the JavaScript event loop “run to completion”.
(https://developer.mozilla.org/docs/Web/JavaScript/Event_loop#run-to-completion)

But I have created a case where this does not seem to happen.

abortController1 = new AbortController()
abortController2 = new AbortController()

abortController1.signal.addEventListener("abort", () =>
{
    console.log("abort 1 start")
    abortController2.abort()
    console.log("abort 1 end")
})

abortController2.signal.addEventListener("abort", () =>
{
    console.log("abort 2")
})

abortController1.abort()

Output:

abort 1 start
abort 2
abort 1 end

I was expecting to see this output:

abort 1 start
abort 1 end
abort 2

Can someone please explain what’s going on here?

Is there a way to get the difference between 2 points by click and hold with the mouse on a line on chart.js?

I have seen so many custom charts and I would like to know how to modify them too.

Is there a way on such a chart chart.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.js"></script>

    <!-- this 2 are for zoom plugins -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-plugin-zoom.min.js"></script>

    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
        }
        .chart-container {
            position: relative;
            width: 90%;
            height: 200px;
            background-color: black;
            color: white;
        }
        
        #chart {
            width: 100%;
        }
        </style>
    </head>
<body>
    <div class="chart-container" id="chart-container" style="text-align:center;">
        <canvas id="chart" style="display: inline-block;"></canvas>
    </div>


    <script>
        const ctx = document.getElementById('chart').getContext('2d');

        let data = [];
        
        function generateRandomData(numPoints) {
            let currentDate = new Date();

            for (let i = 0; i < numPoints; i++) {
                const rand_plus_or_minus = Math.round(Math.random()) * 2 - 1;
                
                const y = Math.random() * 100 + 50;

                new_date = parseInt(currentDate.getTime()/1000)*1000 + i * 24 * 60 * 60 * 1000;

                data.push({
                    x: new_date,
                    y: y,
                });
            }
            return data;
        }

        var rawData = generateRandomData(50);

    

        const chart = new Chart(ctx, {
            type: 'line',
            data: {
                datasets: [
                    {
                        type: 'line',
                        label: 'Dataset 1',
                        data: data,
                        borderColor: chartLineColor,
                        backgroundColor: chartLineColor,
                        borderWidth: 1,
                        fill: false,
                        pointStyle: 'circle',
                        hoverRadius: 5,
                    },
                ],
            },
            options: {
                    animation: false,
                    responsive: true,
                    maintainAspectRatio: false,
                elements: {
                    point:{
                        radius: 0
                    },
                },
                plugins: {
                    tooltip: {
                        mode: 'index',
                        intersect: false,
                        enabled: true,
                    },


                        zoom: { //plugin
                            zoom: {
                                wheel: {
                                    enabled: true,
                                },
                                pinch: {
                                    enabled: true
                                },
                                mode: 'x',
                                limits: {
                                    x: {min: 0, max: 100},
                                },
                                onZoomStart: ({ chart, event, point }) => {
                                    document.getElementById('button_reset_mainchart_zoom').style = "display: block;";
                                },
                            },
                        },



                },
                interaction: {
                    mode: 'index',
                    intersect: false,
                },
                scales: {
                    x: {
                        type: 'linear',
                        title: {
                            display: true,
                            text: 'X Axis',
                        },
                        offset: false,
                        //offset: true,
                    },
                    y: {
                        title: {
                            display: true,
                            text: 'Y Axis',
                        },
                    },
                },
            },
        });
    
    
        function chartLineColor (firstvalue, lastvalue) {
       firstvalue = data[0].y;
       lastvalue = data[data.length - 1].y;
                if (firstvalue < lastvalue) {
                    return 'red';
                } else if (firstvalue > lastvalue) {
                    return 'green';
                } else {
                    return 'grey';
                }
            }
      

    
        </script>
    
    
            <script>
                resetZoomBtn = (chart) => {
                    chart.resetZoom()
                };
                </script>

                <button id="button_reset_mainchart_zoom" type="button" class="btn btn-secondary" onclick="resetZoomBtn(chart); this.style = 'display: none;';">Reset Zoom</button>

    </body>
</html>

Working sample:

https://jsfiddle.net/p1ocb8wf/2/

to get the difference between two points by clicking on an area of ​​the graph with the mouse and dragging the mouse to the desired point of the canvas?

Look at the image to understand the result I would like to achieve:

chart result

How can you use a React hook conditionally in a function component

I have a react function component that needs to use a framer motion hook to build up the props of an object called styles, however you can’t use hooks conditionally, so I’d like to know how to use hooks conditionally?

let styles = {};
if (scaleUp) {
  styles.scale = useTransform(scrollYProgress, [0, 1], [1, 1.2]);    
}
if (scaleDown) {
  styles.scale = useTransform(scrollYProgress, [0, 1], [1.2, 1]);    
}  
if (fadeIn) {
  styles.opacity = useTransform(scrollYProgress, [0, 1], [0, 1]);
}
if (fadeOut) {
  styles.opacity = useTransform(scrollYProgress, [0, 1], [1, 0]);
}

how can I access my servers with javascript via external IP

why can I only access my servers via my static external IP address using this URL “https://cors-anywhere.herokuapp.com”? Without this URL, CORS throws an error.

fetch(‘https://cors-anywhere.herokuapp.com/http://my external IP address’, { redirect: ‘follow’ }) .then(response => response.text()) .then(data => console.log(data)) .catch(error => console.error(‘Error:’, error));

Alphabetize options in a select list, retain event listeners

I need to alphabetize the options in a select list. I can’t just switch the innerHTML and value attributes because some options have classes and event listeners attached. Here is what I have so far:

function alphabetizeSelectListItems(theOldList) {
    var theNewList;
    var insertOptionHere;
    theNewList = theOldList.cloneNode(true);
    while(theOldList.options.length > 0) {
        theOldList.options[0].remove();
    }
    while(theNewList.options.length > 0) {
        insertOptionHere = null;
        for(var optionCounter = 0; optionCounter < theOldList.length; optionCounter++) {
            if(theNewList.options[0].innerHTML.toLocaleUpperCase().localeCompare(theOldList.options[optionCounter].innerHTML.toLocaleUpperCase()) < 0) {
                insertOptionHere = optionCounter;
                break;
            }
        }
        if(insertOptionHere == null) {
            theOldList.appendChild(theNewList.options[0]);
        } else {
            theOldList.insertBefore(theNewList.options[0], theOldList.options[insertOptionHere]);
        }
    }
    theNewList.remove();
}

This clones the old list into a temp element, empties the options from the old list, and then copies each option from the temp list back into the old list in its alphabetical place. Then it deletes the temp list.

Problem with this is it copies everything except the event listeners. So I have to use the same select list with the same options but move them around. I’ve looked at bubble sorting examples and tried to adapt them to option elements instead of array elements, but when I swap two elements, I have to store one in a temporary element, and that removes the event listener.

Is this possible? Is there a way to swap around select list options while retaining the event listeners?

Alpine.js menu toggle issue

I’m currently working on a project using Laravel and Alpine.js, and I’ve run into a bit of a problem with a menu toggle feature. I have a button that is supposed to toggle the visibility of a menu when clicked. However, the menu is not showing.

Here’s a simplified version of my code:

resources/views/test.blade.php:

<!DOCTYPE html>
<html x-data="data()">

<head>
    @vite(['resources/js/app.js'])
</head>
<body>
    <button @click="toggleMenu" @keydown.escape="closeMenu">Toggle menu</button>
    <template x-if="isMenuOpen">
        <div @click.away="closeMenu" @keydown.escape="closeMenu">Menu is open.</div>
    </template>
</body>
</html>

resources/js/app.js:

import Alpine from 'alpinejs';
window.Alpine = Alpine;
window.data = function data() {
    return {
        isMenuOpen: false,
        toggleMenu() {
            this.isMenuOpen = !this.isMenuOpen;
        },
        closeMenu() {
            this.isMenuOpen = false;
        },
    }
}
Alpine.start();

I have installed a debugbar for Alpine.js to investigate the problem. The issue seems to be that the isMenuOpen variable is set to true when I click the button, but it quickly changes back to false, causing the menu to close immediately.

Does anyone have any suggestions on how to make the menu stay open after clicking the button? Any help would be greatly appreciated!

Thanks in advance!