How do I play audio one after another without delay?

I’m attempting to make a game, and in it I have music (like most games). However, changing the track or even looping the same one tends to come with a very slight delay. I want to remove that using pure vanilla Javascript.

I tried this:

// Sets an object with music URLs
const mus = {
    'Surface': "https://codeberg.org/NerdB0I/dungeoncrawlerost/raw/branch/main/mus/Surface.wav",
    'Deeper': "https://codeberg.org/NerdB0I/dungeoncrawlerost/raw/branch/main/mus/Deeper.wav"
} 
// This ensures that music will play and wait for each other using Promises 
function musPlay(name='') {
     return new Promise(resolve => {
         let audio = new Audio(mus[name]);
         audio.onended = () => resolve();
         audio.play();
     });
 }  
// This runs once everytime the song ends and plays a new song based on the variable currSong 
let currSong = 'Surface'; 
async function run() {
     await musPlay(currSong);
     run();
} 
run();  
// This allows you to press keys to change the song (and to see what key pressed) 
// I do not mind that the song continues playing after keydown, I just need it to set the currSong variable for when the files end 
window.addEventListener('keydown', (event) => {
     if (event.key == 'a') {
         currSong = 'Surface';
     } else if (event.key == 'd') {
         currSong = 'Deeper';
     }
     document.getElementById('body').innerHTML = event.key;
});

My Liveweave
If this is just a problem of Liveweave and not code, please let me know.

EDIT: Fixed File Formatting
EDIT: This is more an additional note: I set the body tag to have an id of “body”, in case that wasn’t clear here

E2E Competition Tests Forbidden 403 Error

So basically i am trying to implement some integration tests for competition, task, subtask creation and deletion, etc (e2e tests), but i keep being given Error 403 Forbidden. I will also attach what i get after running npm run. Previously I got an error regarding permissions so I modified beforeAll and this seems to fix it but now I get this.

import { HttpStatus, INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { Test, TestingModule } from '@nestjs/testing';
import { CompetitionController } from '../src/competition/competition.controller';
import { CompetitionService } from '../src/competition/competition.service';
import { SubmissionService } from '../src/competition/submission.service';
import { AuthService } from '../src/auth/auth.service';
import { Reflector } from '@nestjs/core';
import { getRepositoryToken } from '@nestjs/typeorm';
import RoleEntity from '../src/auth/entity/role.entity';
import PermissionEntity from '../src/auth/entity/permission.entity';

describe('CompetitionController (e2e)', () => {
  let app;
  let requestAgent: request.SuperTest<request.Test>;

  const mockAuthService = {
    validateUser: jest.fn().mockResolvedValue({
      id: 1,
      username: 'testuser',
      roles: ['admin'],
      permissions: [
        { resourceType: 'competition', accessOperation: 'create' },
        { resourceType: 'competition', accessOperation: 'delete' },
        { resourceType: 'task', accessOperation: 'create' },
        { resourceType: 'task', accessOperation: 'delete' },
      ],
    }),
    login: jest.fn().mockResolvedValue({
      access_token: 'valid-token',
    }),
  };

  const mockCompetitionService = {};

  const mockSubmissionService = {};

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      controllers: [CompetitionController],
      providers: [
        {
          provide: CompetitionService,
          useValue: mockCompetitionService,
        },
        {
          provide: SubmissionService,
          useValue: mockSubmissionService,
        },
        {
          provide: AuthService,
          useValue: mockAuthService,
        },
        Reflector,
        {
          provide: getRepositoryToken(RoleEntity),
          useValue: {
            save: jest.fn(),
            findOne: jest.fn().mockResolvedValue({
              name: 'admin',
              permissions: [
                { resourceType: 'competition', accessOperation: 'create' },
                { resourceType: 'competition', accessOperation: 'delete' },
                { resourceType: 'task', accessOperation: 'create' },
                { resourceType: 'task', accessOperation: 'delete' },
              ],
            }),
          },
        },
        {
          provide: getRepositoryToken(PermissionEntity),
          useValue: {
            save: jest.fn(),
            findOne: jest.fn().mockResolvedValue(null),
          },
        },
      ],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
    requestAgent = request(app.getHttpServer()) as unknown as request.SuperTest<request.Test>;

    const roleRepository = moduleFixture.get(getRepositoryToken(RoleEntity));
    const permissionRepository = moduleFixture.get(getRepositoryToken(PermissionEntity));

    const existingRole = await roleRepository.findOne({ where: { name: 'admin' } });
    if (!existingRole) {
      await roleRepository.save({
        name: 'admin',
        permissions: [
          { resourceType: 'competition', accessOperation: 'create' },
          { resourceType: 'competition', accessOperation: 'delete' },
          { resourceType: 'task', accessOperation: 'create' },
          { resourceType: 'task', accessOperation: 'delete' },
        ],
      });
    }

    const existingPermission = await permissionRepository.findOne({
      where: { resourceType: 'competition', accessOperation: 'create' },
    });

    if (!existingPermission) {
      await permissionRepository.save([
        { resourceType: 'competition', accessOperation: 'create', resourceRelationship: 'admin' },
        { resourceType: 'competition', accessOperation: 'delete', resourceRelationship: 'admin' },
        { resourceType: 'task', accessOperation: 'create', resourceRelationship: 'admin' },
        { resourceType: 'task', accessOperation: 'delete', resourceRelationship: 'admin' },
      ]);
    }
  });

  afterAll(async () => {
    if (app) {
      await app.close();
    }
  });

  it('/comp (PUT) - Create Competition', async () => {
    const competitionDTO = {};

    await requestAgent
      .put('/comp')
      .set('Authorization', 'Bearer valid-token')
      .send(competitionDTO)
      .expect(HttpStatus.CREATED)
      .expect((res) => {
        expect(res.body).toEqual(true);
      });
  });

  it('/comp/:slug/available (GET) - Check Slug Availability', async () => {
    await requestAgent
      .get('/comp/test-slug/available')
      .set('Authorization', 'Bearer valid-token')
      .expect(HttpStatus.OK)
      .expect((res) => {
        expect(res.body).toEqual(true);
      });
  });

  it('/comp/:slug/task (PUT) - Create Task', async () => {
    const taskDTO = {};

    await requestAgent
      .put('/comp/test-slug/task')
      .set('Authorization', 'Bearer valid-token')
      .send(taskDTO)
      .expect(HttpStatus.CREATED)
      .expect((res) => {
        expect(res.body).toEqual(true);
      });
  });

  it('/comp/:slug (DELETE) - Delete Competition', async () => {
    await requestAgent
      .delete('/comp/test-slug')
      .set('Authorization', 'Bearer valid-token')
      .expect(HttpStatus.OK)
      .expect((res) => {
        expect(res.body).toEqual(true);
      });
  });

  it('/comp/:slug/task/:taskID (DELETE) - Delete Task', async () => {
    await requestAgent
      .delete('/comp/test-slug/task/12345')
      .set('Authorization', 'Bearer valid-token')
      .expect(HttpStatus.OK)
      .expect((res) => {
        expect(res.body).toEqual(true);
      });
  });

  it('/comp/:slug/task/:taskID/submit (POST) - Submit Task', async () => {
    const submissionDTO = {};

    await requestAgent
      .post('/comp/test-slug/task/12345/submit')
      .set('Authorization', 'Bearer valid-token')
      .send(submissionDTO)
      .expect(HttpStatus.OK)
      .expect((res) => {
        expect(res.body).toEqual(true);
      });
  });
});

Also this is what I get after running

● CompetitionController (e2e) › /comp (PUT) - Create Competition
 
    expected 201 "Created", got 403 "Forbidden"
 
      127 |       .set('Authorization', 'Bearer valid-token')
      128 |       .send(competitionDTO)
    > 129 |       .expect(HttpStatus.CREATED)
          |        ^
      130 |       .expect((res) => {
      131 |         expect(res.body).toEqual(true);
      132 |       });
 
      at Object.<anonymous> (competition.e2e-spec.ts:129:8)
      ----
      at Test._assertStatus (../../../node_modules/supertest/lib/test.js:252:14)
      at ../../../node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (../../../node_modules/supertest/lib/test.js:285:13)
      at Test.assert (../../../node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (../../../node_modules/supertest/lib/test.js:120:14)
 
  ● CompetitionController (e2e) › /comp/:slug/available (GET) - Check Slug Availability
 
    expected 200 "OK", got 403 "Forbidden"
 
      137 |       .get('/comp/test-slug/available')
      138 |       .set('Authorization', 'Bearer valid-token')
    > 139 |       .expect(HttpStatus.OK)
          |        ^
      140 |       .expect((res) => {
      141 |         expect(res.body).toEqual(true);
      142 |       });
 
      at Object.<anonymous> (competition.e2e-spec.ts:139:8)
      ----
      at Test._assertStatus (../../../node_modules/supertest/lib/test.js:252:14)
      at ../../../node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (../../../node_modules/supertest/lib/test.js:285:13)
      at Test.assert (../../../node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (../../../node_modules/supertest/lib/test.js:120:14)
 
  ● CompetitionController (e2e) › /comp/:slug/task (PUT) - Create Task
 
    expected 201 "Created", got 403 "Forbidden"
 
      150 |       .set('Authorization', 'Bearer valid-token')
      151 |       .send(taskDTO)
    > 152 |       .expect(HttpStatus.CREATED)
          |        ^
      153 |       .expect((res) => {
      154 |         expect(res.body).toEqual(true);
      155 |       });
 
      at Object.<anonymous> (competition.e2e-spec.ts:152:8)
      ----
      at Test._assertStatus (../../../node_modules/supertest/lib/test.js:252:14)
      at ../../../node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (../../../node_modules/supertest/lib/test.js:285:13)
      at Test.assert (../../../node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (../../../node_modules/supertest/lib/test.js:120:14)
 
  ● CompetitionController (e2e) › /comp/:slug (DELETE) - Delete Competition
 
    expected 200 "OK", got 403 "Forbidden"
 
      160 |       .delete('/comp/test-slug')
      161 |       .set('Authorization', 'Bearer valid-token')
    > 162 |       .expect(HttpStatus.OK)
          |        ^
      163 |       .expect((res) => {
      164 |         expect(res.body).toEqual(true);
      165 |       });
 
      at Object.<anonymous> (competition.e2e-spec.ts:162:8)
      ----
      at Test._assertStatus (../../../node_modules/supertest/lib/test.js:252:14)
      at ../../../node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (../../../node_modules/supertest/lib/test.js:285:13)
      at Test.assert (../../../node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (../../../node_modules/supertest/lib/test.js:120:14)
 
  ● CompetitionController (e2e) › /comp/:slug/task/:taskID (DELETE) - Delete Task
 
    expected 200 "OK", got 403 "Forbidden"
 
      170 |       .delete('/comp/test-slug/task/12345')
      171 |       .set('Authorization', 'Bearer valid-token')
    > 172 |       .expect(HttpStatus.OK)
          |        ^
      173 |       .expect((res) => {
      174 |         expect(res.body).toEqual(true);
      175 |       });
 
      at Object.<anonymous> (competition.e2e-spec.ts:172:8)
      ----
      at Test._assertStatus (../../../node_modules/supertest/lib/test.js:252:14)
      at ../../../node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (../../../node_modules/supertest/lib/test.js:285:13)
      at Test.assert (../../../node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (../../../node_modules/supertest/lib/test.js:120:14)
 
  ● CompetitionController (e2e) › /comp/:slug/task/:taskID/submit (POST) - Submit Task
 
    expected 200 "OK", got 403 "Forbidden"
 
      183 |       .set('Authorization', 'Bearer valid-token')
      184 |       .send(submissionDTO)
    > 185 |       .expect(HttpStatus.OK)
          |        ^
      186 |       .expect((res) => {
      187 |         expect(res.body).toEqual(true);
      188 |       });
 
      at Object.<anonymous> (competition.e2e-spec.ts:185:8)
      ----
      at Test._assertStatus (../../../node_modules/supertest/lib/test.js:252:14)
      at ../../../node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (../../../node_modules/supertest/lib/test.js:285:13)
      at Test.assert (../../../node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (../../../node_modules/supertest/lib/test.js:120:14)
 
[Nest] 55230  - 11/14/2024, 6:14:54 PM   ERROR [Testing] Role name unavailable
[Nest] 55230  - 11/14/2024, 6:14:54 PM   ERROR [Testing] SqliteError: UNIQUE constraint failed: permissions.resourceType, permissions.accessOperation, permissions.resourceRelationship
[Nest] 55230  - 11/14/2024, 6:14:55 PM   ERROR [Testing] Unable to assign permission to role
[Nest] 55230  - 11/14/2024, 6:14:55 PM   ERROR [Testing] Unable to assign permission to role
[Nest] 55230  - 11/14/2024, 6:14:55 PM   ERROR [Testing] Could not find any entity of type "RoleEntity" matching: {
    "relations": {
        "permissions": true
    },
    "where": {
        "name": "New Role"
    }
}
[Nest] 55230  - 11/14/2024, 6:14:55 PM   ERROR [Testing] Unable to assign permission to role

How to prevent Firefox from launching Reader mode when using a USB Barcode scanner to populate form data?

I have a contact form that I want to populate using a USB code scanning device that reads the PDF417 code on the back of Canadian drivers licences. Script works fine in Chrome, but not so much in Firefox. The scanner seems to trigger an “F9” call, which launches Firefox into this Reader mode.
I know I can disable Reader mode in about:config, or use “preventDefault” for an “F9” call, but this seems to skips past the code that actually gives me the scan data.

Here’s the code that works in Chrome (slightly modified):

barcode-scanner.js

function scanListener(e) {
    
    // add scan property to window if it does not exist
    if(!window.hasOwnProperty('scan')) {
        window.scan = []
    }
    
    // if key stroke appears after 10 ms, empty scan array
    if(window.scan.length > 0 && (e.timeStamp - window.scan.slice(-1)[0].timeStamp) > 10) {
        window.scan = []
    }   
    
    //console.log(e.key);

    if(e.key === "F9") {
        e.preventDefault(true);
        e.stopPropagation(true);

    }
    
    // if key store is enter and scan array contains keystrokes
    if(e.key === "ArrowDown" && window.scan.length > 0) {
        let scannedString = window.scan.reduce(function(scannedString, entry) {
            return scannedString + entry.key
        }, "")
        // empty scan array after dispatching event
        window.scan = []
        // dispatch `scanComplete` with keystrokes in detail property
        return document.dispatchEvent(new CustomEvent('scanComplete', {detail: scannedString}))
    }
    
    // do not listen to shift event, since key for next keystroke already contains a capital letter
    // or to be specific the letter that appears when that key is pressed with shift key
    if(e.key !== "Shift") {
        // push `key`, `timeStamp` and calculated `timeStampDiff` to scan array
        let data = JSON.parse(JSON.stringify(e, ['key', 'timeStamp']))
        data.timeStampDiff = window.scan.length > 0 ? data.timeStamp - window.scan.slice(-1)[0].timeStamp : 0;

        window.scan.push(data)
    }

}

// document.addEventListener('scanComplete', function(e) { console.log(":)" + e.detail) })

test.html

<script>
            document.addEventListener('keydown', scanListener);
            document.addEventListener('scanComplete', function(e) { 
                // stuff data into object and populate form fields
            });
</script>

Any help would be appreciated. I’ve been at this a day and a half already.

Thanks

I need to use underline tool option for my ckeditor used in .net mvc project

I used this ckeditor cdn

<script src="https://cdn.ckeditor.com/ckeditor5/39.0.1/classic/ckeditor.js"></script>

When I use this code in js for ckeditor creation with toolbar options, I get all the options listed but not underline tool.

    ClassicEditor.create(this, {
        toolbar: ['bold', 'italic', 'Underline', 'bulletedList', 'numberedList', 'link'],
    }).catch(error => {
        console.error(error);
    });

And I get this error in console when loaded:

 toolbarview-item-unavailable Objectitem: "Underline"[[Prototype]]: Object 
Read more: https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html#error-toolbarview-item-unavailable

I need to use underline tool in my editor

React DOM crashes when rendering Component

I have a parent component:

function CCList(props) {
  const [panelDisabled, setPanelDisabled] = React.useState(false);
  const [responseData, setResponseData] = React.useState("");
  const [selectedCountry, setSelectedCountry] = React.useState("US");
  const [selectedOperation, setSelectedOperation] = React.useState("Create");
  const [createdObject, setCreatedObject] = React.useState(null);
  const [searchResults, setSearchResults] = React.useState([]);
  const [selectedResult, setSelectedResult] = React.useState({})
  const [commitMessage, setCommitMessage] = React.useState("");
  const [showConfirmationModal, setShowConfirmationModal] = React.useState(false);
  const [showViewChangesModal, setShowViewChangesModal] = React.useState(false);
  const [clonedObject, setClonedObject] = React.useState(null);
  const handleOperationChange = (event) => {
    setSelectedOperation(event.target.value);
    setResponseData(""); // Clear any previous response data
    setCreatedObject(null);
    setSearchResults([]);
    setSelectedResult({});
    setClonedObject(null);
  };
  // Handle dropdown selection change
  const handleOptionChange = (event) => {
    setSelectedCountry(event.target.value);
    setResponseData(""); // Clear any previous response data
    setShowConfirmationModal(false);
    setShowViewChangesModal(false);
  };
  const handleClose = () => {
    setCreatedObject(null);
    setResponseData("");
  }
  const openConfirmationModal = () => setShowConfirmationModal(true);
  const openViewChangesModal = () => setShowViewChangesModal(true);
  // Handle button click
  const handleSearch = (event) => {
    let urlParams = new URLSearchParams({"countryCode": selectedCountry })

    let url = props.uris['trial-plans-fetch'] + "?" + urlParams.toString();

    setResponseData("Searching...");
    props.handlePanelDisabledChanged(true);
    event.preventDefault();

    fetch(url)
        .then(response => response.json())
        .then(bodyJson => {
          if (bodyJson.success) {
            const numberOfEntries = Array.isArray(bodyJson.data) ? bodyJson.data.length : 0;
            setResponseData("Successfully found " + numberOfEntries + " results.");
            setSearchResults(bodyJson.data);
          } else {
            let errorMsg = ("data" in bodyJson && "message" in bodyJson["data"]) ?
                bodyJson.data.message + (bodyJson.data.code ? ' (code: ' + bodyJson.data.code + ')' : null) : bodyJson.error;
            setResponseData(errorMsg);
          }
        })
        .catch((error) => {
          console.error('Error:', error);
        })
        .finally(() => {
        console.log("done");
        props.handlePanelDisabledChanged(false);
        });
  }
  // Handle upload CSV button click
  const handleUploadCSV = () => {
    // Example message display logic
    setResponseData(`Upload CSV Button clicked`);
  };

  return (
      <div>
        <fieldset className="p-1 my-2">
          <table width={"100%"}>
            <tbody>
            <tr>
              <td>
                <label htmlFor="selectOperationBox">Select Operation</label>
              </td>
              <td>
                <select
                    id="selectOperationBox"
                    value={selectedOperation}
                    style={{width: "80%"}}
                    className="p-1 my-2"
                    onChange={handleOperationChange}
                >
                  <option value="Create">Create</option>
                  <option value="Clone">Clone</option>
                </select>
              </td>
            </tr>
            {selectedOperation === "Create" && (
                <tr>
                  <td>
                    <ObjectCreationForm fields={modelSpecificPlanMetadataFields} validations={validations}
                                        setResponseData={setResponseData} setCreatedObject={setCreatedObject}/>
                  </td>
                </tr>
            )}
            {selectedOperation === "Clone" && (
                <tr>
                  <td>
                    <label htmlFor="selectCountryBox">Select Country Code</label>
                  </td>
                  <td>
                    <select
                        id="selectCountryBox"
                        value={selectedCountry}
                        style={{width: "80%"}}
                        className="p-1 my-2"
                        onChange={handleOptionChange}
                    >
                      {props.availableCC.map((code) => (
                          <option key={code} value={code}>
                            {code}
                          </option>
                      ))}
                    </select>
                  </td>
                  <td style={{width: "20%"}}>
                    <button
                        className={"btn btn-primary"}
                        style={{width: "100%"}}
                        onClick={handleSearch}
                    >
                      Fetch
                    </button>
                  </td>
                </tr>
            )}
            </tbody>
          </table>
        </fieldset>
        {responseData ? (
            <div className="p-1 alert alert-info" style={{
              maxHeight: '200px',
              overflowY: 'auto',
              backgroundColor: '#e0f7fa',
              padding: '10px',
              marginTop: '20px',
              border: '1px solid #b0bec5',
              borderRadius: '4px',
              fontSize: '0.85em',
              whiteSpace: 'pre-wrap',
              wordWrap: 'break-word'
            }}>{responseData}</div>
        ) : null}
        <div>
          {createdObject && (
              <div style={{
                display: 'flex',
                gap: '5px',
                overflowX: 'auto',
                whiteSpace: 'nowrap',
                padding: '5px 0',
              }}>
                {Object.entries(createdObject).map(([fieldName, value]) => (
                    <div key={fieldName}
                         style={{display: 'inline-flex', flexDirection: 'column', alignItems: 'center'}}>
          <span style={{
            fontSize: '12px',
            color: '#555',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            width: '100px',
            whiteSpace: 'nowrap',
            textAlign: 'center'
          }}>
            {fieldName}
          </span>
                      <div style={{
                        backgroundColor: '#e0e0e0',
                        padding: '8px',
                        borderRadius: '4px',
                        width: '100px',
                        textAlign: 'center',
                        color: '#333',
                        overflow: 'hidden',
                        textOverflow: 'ellipsis',
                        whiteSpace: 'nowrap'
                      }}>
                        {value || "N/A"}
                      </div>
                    </div>
                ))}
              </div>
          )}

          {/* Compact second container */}
          {createdObject && (
              <div style={{
                position: 'fixed',
                bottom: '0',
                left: '0',
                right: '0',
                height: '40px',          // Thin banner height
                backgroundColor: '#f9f9f9',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'right',
                borderTop: '1px solid #ddd',
                padding: '10px 20px',
                gap: '10px',
                boxShadow: '0 -1px 5px rgba(0, 0, 0, 0.1)',  // Subtle shadow at the top
                zIndex: '1000'
              }}>
                {/* Status text: "Ready for Pull Request" */}
                <div style={{
                  fontSize: '16px',
                  color: '#5e6c84',
                  marginRight: '30px',  // Space between text and buttons
                }}>
                  Create Pull Request
                </div>
                {/* "What did you change?" input box */}
                <input
                    type="text"
                    placeholder="What did you change?"
                    style={{
                      padding: '6px',
                      fontSize: '14px',
                      borderRadius: '4px',
                      border: '3px solid #ccc',
                      backgroundColor: '#f5f5f5',
                      width: '180px',        // Compact width for the banner style
                      marginRight: '10px'
                    }}
                />
                {/* Update button */}
                <button
                    type="button"
                    onClick={openConfirmationModal}
                    className="btn btn-primary"
                    style={{
                      backgroundColor: '#0052cc',
                      color: '#fff',
                      border: 'none',
                      padding: '6px 12px',
                      borderRadius: '4px',
                      cursor: 'pointer',
                      marginLeft: '10px'
                    }}
                >
                  Update
                </button>

                {/* Close button */}
                <button
                    type="button"
                    className={"btn btn-danger"}
                    onClick={handleClose}
                    style={{
                      border: '1px solid #ccc',
                      padding: '6px 12px',
                      borderRadius: '4px',
                      cursor: 'pointer',
                      marginLeft: '5px',
                    }}
                >
                  Cancel
                </button>

                <button
                    onClick={openViewChangesModal}
                    style={{
                      backgroundColor: '#f4f5f7',
                      border: '1px solid #dfe1e6',
                      borderRadius: '4px',
                      width: '32px',
                      height: '32px',
                      cursor: 'pointer',
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                      padding: '0',
                      fontSize: '18px',
                      color: '#5e6c84',
                    }}
                    aria-label="Options"
                >
                  &#x2022;&#x2022;&#x2022;
                </button>

              </div>
          )}
        </div>
        {searchResults.length > 0 && (
            <PaginatedResults searchResults={searchResults} setResponseData={setResponseData}
                              setCreatedObject={setCreatedObject} clonedObject={clonedObject}
                              setClonedObject={setClonedObject}/>
        )}
        {showConfirmationModal && (
            <ConfirmationModal showModal={showConfirmationModal} setShowModal={setShowConfirmationModal}
                               csvName={"CountrySpecificPlanMetadata"} createdObject={JSON.parse(JSON.stringify(createdObject))}/>
        )}
        {showViewChangesModal && (
            <ViewChangesModal showModal={showViewChangesModal} setShowModal={setShowViewChangesModal}
                                createdObject={JSON.parse(JSON.stringify(createdObject))} clonedObject={JSON.parse(JSON.stringify(clonedObject))}/>
        )}
      </div>

  );
}

The Child component “ObjectCreationForm” Sets createdObject on button click:

function ObjectCreationForm({fields, validations, setResponseData, setCreatedObject, clonedObject}) {
  // Maintain a state for expanded categories
  const [expandedSections, setExpandedSections] = React.useState({});
  const [formValues, setFormValues] = React.useState({});
  const [validationErrors, setValidationErrors] = React.useState({});

  React.useEffect(() => {
    setExpandedSections({});
    setFormValues({});
    setValidationErrors({});
    const initializeFormValues = () => {
      const initialValues = {};
      const initialErrors = {};

      const getFieldsFromObject = (field) => {
        for (const [key, value] of Object.entries(clonedObject || {})) {
          if (value && typeof value === 'object' && value[field] !== undefined) {
            return value[field]; // Return the field value if found in a non-null model
          }
        }
        return ''; // Default to an empty string if not found
      };

      Object.entries(fields).forEach(([category, categoryFields]) => {
        initialValues[category] = {};
        Object.keys(categoryFields).forEach((field) => {
          const initialValue = clonedObject ? getFieldsFromObject(field) : '';
          initialValues[category][field] = initialValue;
          initialErrors[field] = validateInput(field, initialValue) ? null : `Invalid value for ${field}`;
        });
      });
      setFormValues(initialValues);
      setValidationErrors(initialErrors);
    };
    initializeFormValues();
  }, [clonedObject, fields]); // Re-run this effect whenever clonedObject or fields change

  const validateInput = (fieldName, value) => {
    if (validations[fieldName]) {
      return (new RegExp(validations[fieldName].slice(1, -1))).test(value);
    }

    return true;  // Return true if no validation rule is defined
  };

  const toggleSection = (category) => {
    setExpandedSections((prevSections) => ({
      ...prevSections,
      [category]: !prevSections[category], // Toggle the visibility
    }));
  };
  // Handle field value changes
  const handleInputChange = (category, field, value) => {
    // Validate the field
    const isValid = validateInput(field, value);
    // Update the validation errors state
    setValidationErrors((prevErrors) => ({
      ...prevErrors,
      [field]: isValid ? null : `Invalid value for ${field}`,
    }));
    setFormValues((prevValues) => ({
      ...prevValues,
      [category]: {
        ...prevValues[category],
        [field]: value,
      },
    }));
  };

  const handleButtonClick = () => {
    // Set the response data in the parent component when the button is clicked
    const flatObject = {};

    Object.entries(formValues).forEach(([category, fields]) => {
      Object.entries(fields).forEach(([field, value]) => {
        flatObject[field] = value;  // Store field name as key and input value as value
      });
    });

    setResponseData(`Object Successfully Created!`);
    setCreatedObject(flatObject);
    window.scrollTo({ top: 0, behavior: 'smooth' });
  };

At this point there are no issues in the application. However, if I change something in theObjectCreationForm, and then click the button to setCreateObject again with different field values, the entire app crashes when I try to open ViewChangesModal:

const ViewChangesModal = ({ showModal, setShowModal, clonedObject, createdObject }) => {
  if (!showModal) return null;
  const closeModal = () => setShowModal(false);

  const flattenObject = (obj) => {
    const flatObject = {};
    for (const [key, value] of Object.entries(obj)) {
      if (value && typeof value === 'object') {
        // Recursively flatten nested objects
        const nestedObject = flattenObject(value);
        for (const [nestedKey, nestedValue] of Object.entries(nestedObject)) {
          flatObject[nestedKey] = nestedValue;
        }
      } else {
        flatObject[key] = value;
      }
    }
    return flatObject;
  };
  const flatClonedObject = flattenObject(clonedObject);
  const flatCreatedObject = flattenObject(createdObject);
  console.log("Flattened Cloned Object:", flatClonedObject);
  console.log("Flattened Created Object:", flatCreatedObject);


  // Function to determine style based on differences
  const getFieldStyle = (objectValue, compareValue, highlightColor) => {
    // Treat both "null" (string) and undefined/empty strings as equivalent
    const normalizedObjectValue = objectValue === undefined || objectValue === "" ? "null" : objectValue;
    const normalizedCompareValue = compareValue === undefined || compareValue === "" ? "null" : compareValue;

    return {
      backgroundColor: normalizedObjectValue !== normalizedCompareValue ? highlightColor : 'transparent',
      fontSize: '12px',
      color: '#555',
      textAlign: 'center',
      padding: '3px 5px',
      borderRadius: '3px',
    };
  };


  const scrollbarCSS = `
  .scrollable-banner::-webkit-scrollbar {
    height: 3px; /* Make the scrollbar thinner */
  }
  .scrollable-banner::-webkit-scrollbar-thumb {
    background-color: rgba(0, 0, 0, 0.3);
    border-radius: 4px;
  }
  .scrollable-banner::-webkit-scrollbar-track {
    background-color: transparent;
  }
`;
  const scrollableBannerStyle = {
    display: 'flex',
    gap: '5px',
    overflowX: 'auto',
    whiteSpace: 'nowrap',
    padding: '5px 0',
    border: '1px solid #ddd',
    borderRadius: '4px',
    maxHeight: '60px',
    overflowY: 'hidden',
    marginBottom: '10px',
    paddingRight: '10px',
  };
  return (
      <div style={{
        position: 'fixed',
        top: '0',
        left: '0',
        width: '100%',
        height: '100%',
        backgroundColor: 'rgba(0, 0, 0, 0.5)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        zIndex: '2000'
      }}>
        <div style={{
          backgroundColor: 'white',
          padding: '30px',
          borderRadius: '8px',
          width: '600px',
          maxWidth: '90%',
          boxShadow: '0 4px 10px rgba(0, 0, 0, 0.2)',
          overflowY: 'auto'
        }}>
          <p>You are viewing the changes between the object you <strong>Cloned</strong> and the object you <strong>Created</strong>.</p>

          {/* Cloned Object */}
          <div style={{ marginTop: '10px' }}>
            <label style={{ fontWeight: 'bold' }}>Cloned Object:</label>
            <div className="scrollable-banner" style={scrollableBannerStyle}>
              {Object.keys(flatCreatedObject).map((field) => (
                  <span
                      key={field}
                      style={getFieldStyle(flatClonedObject[field], flatCreatedObject[field], 'rgba(255, 0, 0, 0.1)')} // Red for differences
                  >
                {flatClonedObject[field] !== undefined && flatClonedObject[field] !== "" ? flatClonedObject[field] : "null"}
              </span>
              ))}
            </div>
          </div>

          {/* Created Object */}
          <div style={{ marginTop: '10px' }}>
            <label style={{ fontWeight: 'bold' }}>Created Object:</label>
            <div className="scrollable-banner" style={scrollableBannerStyle}>
              {Object.keys(flatCreatedObject).map((field) => {
                const createdValue = flatCreatedObject[field] !== undefined && flatCreatedObject[field] !== "" ? flatCreatedObject[field] : "null";
                const clonedValue = flatClonedObject[field] !== undefined && flatClonedObject[field] !== "" ? flatClonedObject[field] : "null";

                return (
                    <span
                        key={`created-${field}`}
                        style={getFieldStyle(createdValue, clonedValue, 'rgba(0, 255, 0, 0.1)')}
                    >
                    {createdValue}
                    </span>
                );
              })}
            </div>
          </div>

          {/* Close Button */}
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px', marginTop: '20px' }}>
            <button
                type="button"
                onClick={closeModal}
                style={{
                  backgroundColor: '#f0f0f0',
                  color: '#333',
                  border: '1px solid #ccc',
                  padding: '6px 12px',
                  borderRadius: '4px',
                  cursor: 'pointer'
                }}
            >
              Close
            </button>
          </div>
        </div>
      </div>
  );
}

I don’t get any errors pretty much. The DOM seems to crash and reset, as well as the browser dev tools. Im unsure of where to begin, but Im 99% Sure the issue is in the ViewChangesModal component, since i’m unable to replicate the issue when accessing any other component. The issue only occurs when createdObject is set a second time with DIFFERENT values than the first time. If i click the button to setCreatedObject without changing any of the field values, no issues occur in ViewChangesModal. Even ConfirmationModal works with no issues. Any help is appreciated.

React, SocketIO – Handling fetch request whilst receving real time data

When the user navigates to /chats, I fetch the users most recent chats. (When app mounts on / route, the chat list is not fetched):

useEffect(() => {
    const fetchChatList = async () => {
        try {
            const response = await axiosPrivate.get(`${BASE_URL}/api/chats/`)
            dispatch(setChats({ chats: response.data }))
        } catch (err) {
            console.log(err)
        }
    }

    fetchChatList()
}, [axiosPrivate, dispatch])

I have a global listener, which uses socketio to receive and handle new chats in real time. My issue is what if I receive realtime chats whilst fetching the chats at the same time?

The problem is that the fetched chats would override the newly received chat. This is because setChats reducer simply sets the chats array equal to the data it is passed. There is also no guarantee that the new chat/chats received in realtime will be included in the fetch result.

One potential solution is to temporarily store realtime chats when the chat list hasn’t been fetched yet, and then merge the results, however this would include ensuring that the fetched chat list doesn’t include the newly received chats as this would be a duplication issue. This isn’t a good solution in my opinion. Is there any other potential strategies to overcome this issue?

Tool to determine what lowest version of Browser is compatible? [closed]

Now in my application, whether Vue or Nuxt or others, is there a package or library that checks the (output) files, whether JS or CSS, and tells me the lowest version of browsers compatible with my site, meaning the lowest version of browsers that can work without problems.

I would like an explanation of how to use it if possible. Thank you.

For example :

C:appmynuxtapp> showme
> Chrmome : 42
> Opera : 22
> Sfary : 10

PluginError: Failed to resolve plugin for module “@sentry/react-native/expo” relative to “root to project”

I recently upgraded my project from Expo SDK 51 to SDK 52. After the upgrade, I encountered an issue where @sentry/react-native/expo stopped working. If I remove @sentry/react-native/expo from my app config, all other plugins work as expected. However, with it included, Sentry fails to initialize properly.

I’ve tried a few troubleshooting steps but can’t seem to find a solution. Has anyone else faced similar issues with Sentry on Expo SDK 52, or are there any known compatibility issues or workarounds?

app config(without ‘@sentry/react-native/expo’ works):

plugins: [
    [
      '@sentry/react-native/expo',
    ],
    'expo-localization',
    [
      'expo-updates',
    ],
    [
      'expo-camera',
    ],
  ],

metro.config.js:

const { getSentryExpoConfig } = require('@sentry/react-native/metro');

const config = getSentryExpoConfig(__dirname);

module.exports = config;

tsconfig.json:

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true
  },
  "types": ["react", "react-native"],
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    ".eslintrc.js", // Include ESLint configuration file
    "jest.config.js", // Include Jest configuration file
    "metro.config.js", // Include Metro configuration file
    "babel.config.js", // Include Babel configuration file
    "commitlint.config.js", // Include Commitlint configuration file
    "App.tsx" // Include Expo entry point
  ],
  "exclude": ["node_modules"]
}

package json:

"@sentry/react-native": "~6.1.0",

Outlook Crash in Legacy Mac UI with inline attachments and AttachmentsChanged event handler in taskpane

Issue: Outlook client crashes when sending a mail with inline attachment while task pane add-in has Office.EventType.AttachmentsChanged event handler attached to it.

Repro steps (Step by Step):

Deploy an xml outlook officejs add-in which has an event handler for attachments changed in taskpane.
Draft a mail with at least 1 inline attachment.
Invoke taskpane add-in. Make sure Attachments Changed event handler is running in background.
Click on the send button.

Expected Behavior: Mails should be sent successfully. Outlook client should not crash.

Actual Behavior: Outlook crashes when the send button is clicked.

Sample code (script lab yaml):

name: Blank snippet
description: Create a new snippet from a blank template.
host: OUTLOOK
api_set: {}
script:
  content: |
    $("#run").on("click", run);
 
    function run() {
      Office.context.mailbox.item.addHandlerAsync(Office.EventType.AttachmentsChanged, (val) => { console.log("recieved event::", JSON.stringify(val)); }, () => { console.log("added handler successfully");});
    }
  language: typescript
template:
  content: |
    <button id="run" class="ms-Button"><span class="ms-Button-label">Run</span></button>
  language: html
style:
  content: |-
    section.samples {
        margin-top: 20px;
    }
 
    section.samples .ms-Button, section.setup .ms-Button {
        display: block;
        margin-bottom: 5px;
        margin-left: 20px;
        min-width: 80px;
    }
  language: css
libraries: |
  https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js
  @types/office-js
  [email protected]/dist/css/fabric.min.css
  [email protected]/dist/css/fabric.components.min.css
  [email protected]/client/core.min.js
  @types/core-js
  [email protected]
  @types/[email protected]

We tried to send a mail with taskpane add-in running with AttachmentsChanged event handler of OfficeJS API. We were hoping to send the email without crash and keep log of attachments in console.

my form isn’t processing the my images am trying to upload

so this block of accepts multiple files , both drag and drop and uploading of multiples files , after uploading the html file input sees it as empty

  <div class="row">
                        <div class="col-sm-12">
                            <div class="file_uploader bottom20" id="dropZone">
                            <input type="file" id="imageUpload" name="uploaded_images[]" multiple accept="image/*" style="display: none ;">
                                <label for="imageUpload" class="dz-default dz-message">
                                    <span id="uploadPrompt">
                                        <i class="fa fa-picture-o" aria-hidden="true"></i>
                                        Drag & drop images here or click to upload (Max 5 files)
                                    </span>
                                </label>
                                <div id="previewContainer" class="preview-container"></div>
                                <p id="uploadMessage" style="color: white;"></p>
                            </div>
                        </div>
                    </div>

wheh i tried submiting a returns an error code of 4 , meaning it’s empty, this is the javascript that processs the files, i’ve tried many things the solution isn’t forthcoming ,

  <script>
    let selectedFiles = []; // Array to store selected files
    const maxSizeMB = 2;    // Maximum file size (2 MB)
    const maxFiles = 5;     // Maximum number of files allowed
    const previewContainer = document.getElementById("previewContainer");
    const uploadMessage = document.getElementById("uploadMessage");
    const dropZone = document.getElementById("dropZone");

    // Update the file selection prompt
    function updateFilePrompt() {
        const prompt = document.getElementById("uploadPrompt");
        prompt.innerText = selectedFiles.length > 0 
            ? `${selectedFiles.length} file(s) selected` 
            : "Drag & drop images here or click to upload (Max 5 files)";
    }

    // Handle file selection from input
    document.getElementById("imageUpload").addEventListener("change", function(event) {
        handleFiles(event.target.files);
        event.target.value = ""; // Clear input to allow re-selection
    });

    // Handle drag and drop files
    dropZone.addEventListener("dragover", (event) => {
        event.preventDefault();
        dropZone.classList.add("drag-over"); // Optional: Add CSS to indicate active drag
    });

    dropZone.addEventListener("dragleave", () => {
        dropZone.classList.remove("drag-over");
    });

    dropZone.addEventListener("drop", (event) => {
        event.preventDefault();
        dropZone.classList.remove("drag-over");
        handleFiles(event.dataTransfer.files);
    });

    // Function to handle files
    function handleFiles(files) {
        const newFiles = Array.from(files);
        uploadMessage.innerText = ""; // Clear previous messages

        // Check if adding the new files will exceed the max file limit
        if (selectedFiles.length + newFiles.length > maxFiles) {
            uploadMessage.innerText = `Please select up to ${maxFiles} files in total.`;
            return;
        }

        newFiles.forEach(file => {
            if (file.size > maxSizeMB * 1024 * 1024) {
                uploadMessage.innerText = `Each file must be smaller than ${maxSizeMB} MB.`;
            } else {
                // Add the file to the selected files array and preview it
                selectedFiles.push(file);
                previewImage(file);
            }
        });
        
        updateFilePrompt(); // Update file count display
    }

    // Function to display image preview with remove option
    function previewImage(file) {
        const reader = new FileReader();
        reader.onload = function(e) {
            const imgContainer = document.createElement("div");
            imgContainer.className = "image-preview";
            
            const img = document.createElement("img");
            img.src = e.target.result;
            img.style.width = "100px"; // Thumbnail size
            img.style.margin = "5px";
            
            const removeButton = document.createElement("button");
            removeButton.innerText = "Remove";
            removeButton.className = "remove-button";
            removeButton.addEventListener("click", function() {
                removeImage(file);
                imgContainer.remove(); // Remove the preview image
            });
            
            imgContainer.appendChild(img);
            imgContainer.appendChild(removeButton);
            previewContainer.appendChild(imgContainer);
        };
        reader.readAsDataURL(file);
    }

    // Function to remove an image
    function removeImage(file) {
        selectedFiles = selectedFiles.filter(f => f !== file); // Remove file from array
        updateFilePrompt(); // Update file count display
    }
</script>
```, the html tag is see it as empty even after uploading several files.

Sort order mismatch between SQLite and Javascript

I have this better-sqlite-3 statement that returns a list of products, first sorted by relevance (for search feature, it’s 0 for all in this case), then by favorite and then by name A-Z.

    ORDER BY
        relevance DESC,
        preferito DESC,
        des_art   ASC

The products are displayed like so:

"ACCIUGA RIPIENA CREUZA"

"ACCIUGA RIPIENA baccalà"

I have a javascript function that I use to restore the list in case the favorite status changes, but then the products are displayed in alphabetical order ignoring the uppercase.

allProducts.sort((a, b) => {
    
    if (b.relevance !== a.relevance) {
        return b.relevance - a.relevance;
    }
    
    if (b.preferito !== a.preferito) {
        return b.preferito - a.preferito;
    }
    
    return a.des_art.localeCompare(b.des_art);
});
"ACCIUGA RIPIENA baccalà"

"ACCIUGA RIPIENA CREUZA"

How do I keep the same SQL behavior? I tried using
return a.des_art.localeCompare(b.des_art, undefined, { sensitivity: 'case', caseFirst: 'upper' }); but without changes.

Configure Deno / Vite / React project for unit tests

I have a React project running on Deno and Vite. In the project scaffold, there’s no testing included.

I’d like to be able to do tests on my React components just like how I’d be able to in Node like so:

expect(getByText('Hello, world!')).toBeInTheDocument()
  expect(asFragment()).toMatchInlineSnapshot(`
    <h1>Hello, World!</h1>
  `)

Deno has its own testing library but does not support much-needed functions to test React components like toBeInTheDocument and other functions from the Testing Library.

How do I configure testing in Deno for the React project?

No behavior available in Primefaces widget

I want to fill an Primefaces inputText field by Javascript and trigger then the change event. In my understanding of the documentation of the inputText widget the callBehavior function would be the right way.

So my field looks like this:

<p:inputText id="myInput" value="#{myBean.value}">
   <p:ajax event="change" update="@this" onstart="console.log('Change triggered')" />
</p:inputText>

My JS function looks like this

function myFun() {
   const theId = // Have to get the id dynamically
   const inputWdgt = PrimeFaces.getWidgetById(theId);
   inputWdgt.jq.val('My new value');
   inputWdgt.callBehavior('change');
}

But in result neither the log in the onstart is printed nor the update is executed.

In my further investigation I saw, that the cfg object in the browser doesn’t contain any behaviors. Hence it is obvious, that the callBehavior has no effect (since the function relates to these).
So the question would be, why are there no behaviors defined for the widget?

Evaluate the security of the message encryption extension

I created exrension for Firefox, but i’m not really sure about it’s security. Can somebody help, how my program should store permanent keys? Now i keep them in local storage encrypted, but this extension will be opensource, so It would be better to understand all problems before release

Html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="popup.css">
    <title>Main menu</title>
</head>
<body>
    <header>
        <div class="new_window">
            <img src="materials/open_in_new_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="open"/>
        </div>
        <div class="setting">
            <img src="materials/settings_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="settings"/>
        </div>
    </header>
    <main>
        <div class="choose__action">
            <div class="choose__action__encrypt">
                encrypt
            </div>
            <div class="choose__action__decrypt">
                decrypt
            </div>
        </div>
        <label>
            <textarea class="text_input"></textarea>
        </label>
        <div class="key_input_box">
            <label class="switch">
                one-time key
                <input type="checkbox" class="checkbox__switcher"/>
                permanent key
            </label>
            <label>
                <input type="text" class="key_input"/>
            </label>
        </div>
        <label class="output__box">
            <div class="output_text">Output</div>
            <textarea class="output" disabled></textarea>
        </label>
        <div class="send_button_box">
            <button class="operation__button">
               Convert
            </button>
        </div>
    </main>
    <div class="settings__window">
        <div class="settings__flex_block">
            <div class="settings__title">Settings</div>
            <div class="settings__exit__button">
                <img src="materials/close_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="close">
            </div>
        </div>
        <div class="settings_description">
            Warning! To avoid password brute force, we strongly recommend using a 10-character password that contains at least one digit and a special characters.
            Permanent keys are stored encrypted locally.
        </div>
        <div class="change__block">
            <div>
                <span class="settings__encryption_errors"></span>
                <input type="text" class="settings__encryption__key" placeholder="Set encryption key"/>
                <button class="encryption__key__button">Save</button>
            </div>
            <div>
                <span class="settings__decryption_errors"></span>
                <input type="text" class="settings__decryption__key" placeholder="Set decryption key"/>
                <button class="decryption__key__button">Save</button>
            </div>
        </div>
    </div>
    <script src="popup.js"></script>
</body>
</html>

css:

*{
    box-sizing: border-box;
}
input, textarea{
    outline: none;
}
header{
    display: flex;
    justify-content: space-between;
}
.new_window, .setting{
    cursor: pointer;
}
main{
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 10px 70px 50px;
}
.choose__action{
    display: flex;
    justify-content: center;
    cursor: pointer;
    margin-top: 30px;
}
.choose__action{
    border: 1px solid black;
}
.choose__action__encrypt{
    border-right: 1px solid black;
    padding: 5px 10px;
}
.choose__action__decrypt{
    padding: 5px 10px;
}
.choose__action__encrypt.active, .choose__action__decrypt.active{
    background-color: #000;
    color: #fff;
}
.text_input{
    margin-top: 50px;
    resize: none;
    width: 235px;
    height: 100px;
}
.key_input_box{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    margin-top: 20px;
}
.key_input{
    width: 235px;
    height: 25px;
    margin-top: 20px;
}
.switch{
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 5px;
    /*transform: translate(-50%, -50%);*/
}
.checkbox__switcher{
    position: relative;
    width: 40px;
    height: 20px;
    -webkit-appearance: none;
    background: #c6c6c6;
    border-radius: 20px;
    transition: .5s;
    box-shadow: inset 0 0 5px rgba(135, 97, 97, 0.5);
    cursor: pointer;
}
.checkbox__switcher:checked{
    background-color: #171616;
}
.checkbox__switcher::before{
    content: "";
    position: absolute;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    top: 0;
    left: 0;
    background-color: #fff;
    transition: .5s;
    transform: scale(1.1);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.69);
}
.checkbox__switcher:checked::before{
    left: 20px;
}
.output__box{
    display: flex;
    justify-content: center;
    flex-direction: column;
    align-items: center;
    margin-top: 20px;
}
.output{
    resize: none;
    width: 235px;
    height: 100px;
    margin-top: 10px;
}
.operation__button{
    margin-top: 15px;
    background-color: transparent;
    color: black;
    border: 1px solid black;
    transition: .3s ease-in-out;
    cursor: pointer;
    padding: 5px 20px;
}
.operation__button:hover{
    background-color: #000;
    color: #fff;
}
.modal{
    display: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /*display: flex;*/
    flex-direction: column;
    gap: 5px;
    z-index: 2;
    background-color: #fff;
    padding: 10px;
    border-radius: 15px;
}
.saveKeyButton{
    width: max-content;
    height: max-content;
    padding: 3px 15px;
    background-color: transparent;
    border: 1px solid black;
    cursor: pointer;
    transition: .3s ease-in-out;
}
.saveKeyButton:hover{
    background-color: black;
    color: white;
}
.modal_background{
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    background-color: rgba(0, 0, 0, 0.49);
    width: 100%;
    height: 100%;
}
.modal_warning{
    font-size: 15px;
}
.settings__window{
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    background-color: rgb(255, 255, 255);
    width: 100%;
    height: 100%;
}
.settings__exit__button{
    position: absolute;
    top: 7px;
    right: 7px;
    cursor: pointer;
}
.settings__flex_block{
    display: flex;
    justify-content: center;
    margin-top: 10px;
}
.settings__title{
    font-size: 20px;
    font-family: Verdana, serif;
}
.change__block{
    margin-top: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-left: 10px;
}
.settings__encryption__key, .settings__decryption__key{
    padding: 5px 20px 5px 5px;
}
.encryption__key__button, .decryption__key__button{
    background-color: transparent;
    color: #000000;
    transition: .3s ease-in-out;
    border: 1px solid #000;
    cursor: pointer;
    padding: 5px 20px;
    margin-left: 5px;
}
.encryption__key__button:hover, .decryption__key__button:hover{
    background-color: #000;
    color: #fff;
}
.settings_description{
    padding: 10px;
    font-family: Verdana, serif;
    font-size: 15px;
}

js:

let encrypt__button = document.querySelector('.choose__action__encrypt')
let decrypt__button = document.querySelector('.choose__action__decrypt')
let key_input = document.querySelector('.key_input')
let switcher = document.querySelector('.checkbox__switcher')
let new_window = document.querySelector('.new_window')
let send_button = document.querySelector('.operation__button')
let input_text = document.querySelector('.text_input')
let output_text = document.querySelector('.output')
let output_warning = document.querySelector('.output_text')
let setting__button = document.querySelector('.setting')
let settings_window = document.querySelector('.settings__window')
let settings__exit = document.querySelector('.settings__exit__button')
let settings__encryption__key = document.querySelector('.settings__encryption__key')
let settings__decryption__key = document.querySelector('.settings__decryption__key')
let encryption__key__button = document.querySelector('.encryption__key__button')
let decryption__key__button = document.querySelector('.decryption__key__button')


// Удобство и сохранение методов в локальном хранилище
if (localStorage.getItem('method')){
    if (localStorage.getItem('method') === 'decrypt'){
        decrypt__button.classList.add('active')
    } else{
        encrypt__button.classList.add('active')
    }
} else{
    encrypt__button.classList.add('active')
}
if (localStorage.getItem('key_input')){
    if (localStorage.getItem('key_input') === 'true'){
        switcher.checked = true
        key_input.disabled = true
    } else{
        switcher.checked = false
        key_input.disabled = false
    }
}

//открытие нового окна
new_window.addEventListener('click', () =>{
        browser.windows.create({
        url: "popup.html",
        type: "popup",
        width: 450,
        height: 700
    });
})

//смена методов
decrypt__button.addEventListener('click', () =>{
    if (encrypt__button.classList.contains('active')){
        encrypt__button.classList.remove('active');
    }
    decrypt__button.classList.add('active')
    localStorage.setItem("method", "decrypt")
})
encrypt__button.addEventListener('click', () =>{
    if (decrypt__button.classList.contains('active')){
        decrypt__button.classList.remove('active');
    }
    encrypt__button.classList.add('active')
    localStorage.setItem("method", "encrypt");
})
//выбор пароля
switcher.addEventListener('click', () =>{
    if (switcher.checked){
        key_input.disabled = true
        localStorage.setItem('key_input', 'true')
    } else{
        key_input.disabled = false
        localStorage.setItem('key_input', 'false')
    }
})
//Кнопка настроек
setting__button.addEventListener('click', ()=>{
    if (!(settings_window.style.display === 'block')){
        settings_window.style.display = 'block'
    }
})
//Окно настроек
settings__exit.addEventListener('click', ()=>{
    if (!(settings_window.style.display === 'none')){
        settings_window.style.display = 'none'
    }
})
//Смена постоянных ключей
encryption__key__button.addEventListener('click', ()=>{
    if (settings__encryption__key.value){
        let check = password_check(settings__encryption__key.value)
        if (check){
            saveEncryptionKeys(settings__encryption__key.value)
            settings__encryption__key.value = ''
            document.querySelector('.settings__encryption_errors').textContent = ''
        }else{
            document.querySelector('.settings__encryption_errors').textContent = 'Password contains unacceptable simbols'
        }
    }
})
decryption__key__button.addEventListener('click', ()=>{
    if (settings__decryption__key.value){
        let check = password_check(settings__decryption__key.value)
        if (check){
            saveDecryptionKeys(settings__decryption__key.value)
            settings__decryption__key.value = ''
            document.querySelector('.settings__decryption_errors').textContent = ''
        }else{
            document.querySelector('.settings__decryption_errors').textContent = 'Password contains unacceptable simbols'
        }
    }
})
//отправка данных для шифрования/дешифрования
send_button.addEventListener('click', async () => {
    let method = localStorage.getItem('method') || 'encrypt';
    let key;

    if (switcher.checked === true && method === 'encrypt') {
        key = await getEncryptionKey();
        if (!key) {
            if (!(settings_window.style.display === 'block')){
                settings_window.style.display = 'block'
            }
            return;
        }
    } else if (switcher.checked === true && method === 'decrypt') {
        key = await getDecryptionKey();
        if (!key) {
            if (!(settings_window.style.display === 'block')){
                settings_window.style.display = 'block'
            }
            return;
        }
    } else {
        key = key_input.value;
    }

    let inText = input_text.value
    if (method === 'encrypt'){
        output_text.value = await encryptWithPassword(key, inText)
        output_warning.textContent = 'Output'
    } else{
        const decryptedText = await decryptWithPassword(key, inText)
        if (decryptedText === null) {
            output_text.textContent = ""
            output_warning.textContent = "Incorrect decryption key"
        } else {
            output_text.value = decryptedText
            output_warning.textContent = "Output"
        }
    }
})
//сохранение паролей в локальном хранилище
async function saveEncryptionKeys(encryptionKey) {
    const { encryptedText, iv } = await encryptText(encryptionKey);
    localStorage.setItem('encryptionKey', encryptedText);
    localStorage.setItem('encryptionIV', iv);
}
async function saveDecryptionKeys(decryptionKey) {
    const { encryptedText, iv } = await encryptText(decryptionKey);
    localStorage.setItem('decryptionKey', encryptedText);
    localStorage.setItem('decryptionIV', iv);
}
//получение паролей из локального хранилища
async function getEncryptionKey() {
    const encryptedText = localStorage.getItem('encryptionKey')
    const iv = localStorage.getItem('encryptionIV')
    if (encryptedText && iv) {
        try {
            return await decryptText(encryptedText, iv)
        } catch (e) {
            console.error("Ошибка расшифровки ключа:", e)
            return false;
        }
    }
    return false;
}

async function getDecryptionKey() {
    const encryptedText = localStorage.getItem('decryptionKey');
    const iv = localStorage.getItem('decryptionIV');
    if (encryptedText && iv) {
        try {
            return await decryptText(encryptedText, iv);
        } catch (e) {
            console.error("Ошибка расшифровки ключа:", e);
            return false;
        }
    }
    return false;
}
//функция получения уникального ключа
async function deriveKeyFromPassword(password, salt = null) {
    const encoder = new TextEncoder();
    salt = salt || crypto.getRandomValues(new Uint8Array(16))

    const keyMaterial = await crypto.subtle.importKey(
        "raw", encoder.encode(password), "PBKDF2", false, ["deriveKey"]
    );

    const cryptoKey = await crypto.subtle.deriveKey(
        { name: "PBKDF2", salt: salt, iterations: 200000, hash: "SHA-256" },
        keyMaterial,
        { name: "AES-GCM", length: 256 },
        false,
        ["encrypt", "decrypt"]
    );

    return { cryptoKey, salt }
}
//функция проверки пароля
function password_check(password) {
    return /^[A-Za-zА-Яа-яЁё0-9!@#$%^&*()_+{}[]:;"'<>?,./\|~`-]+$/.test(password)
}
// функции шифрования и дешифрования на основе пароля
async function encryptWithPassword(password, plaintext) {
    const encoder = new TextEncoder();

    const { cryptoKey, salt } = await deriveKeyFromPassword(password);

    const iv = crypto.getRandomValues(new Uint8Array(12));

    const ciphertext = await crypto.subtle.encrypt(
        { name: "AES-GCM", iv: iv },
        cryptoKey,
        encoder.encode(plaintext)
    );

    const encryptedData = new Uint8Array(salt.length + iv.length + ciphertext.byteLength);
    encryptedData.set(salt);
    encryptedData.set(iv, salt.length);
    encryptedData.set(new Uint8Array(ciphertext), salt.length + iv.length);

    return btoa(String.fromCharCode(...encryptedData)); // Конвертируем в Base64 строку
}
async function decryptWithPassword(password, encryptedText) {
    try {
        const encryptedData = Uint8Array.from(atob(encryptedText), c => c.charCodeAt(0));

        const salt = encryptedData.slice(0, 16);
        const iv = encryptedData.slice(16, 28);
        const ciphertext = encryptedData.slice(28);

        const { cryptoKey } = await deriveKeyFromPassword(password, salt);

        const decrypted = await crypto.subtle.decrypt(
            { name: "AES-GCM", iv: iv },
            cryptoKey,
            ciphertext
        );

        const decoder = new TextDecoder();
        return decoder.decode(decrypted);

    } catch (error) {
        console.error("Ошибка расшифровки: Неверный ключ или поврежденные данные.");
        return null;
    }
}

//уникальная строка для хранения паролей
async function generateFixedKey() {
    const encoder = new TextEncoder();
    const uniqueData = `${navigator.userAgent}-fixedSalt`; // создаём уникальную строку
    const keyMaterial = await crypto.subtle.importKey(
        "raw", encoder.encode(uniqueData), "PBKDF2", false, ["deriveKey"]
    );
    return await crypto.subtle.deriveKey(
        { name: "PBKDF2", salt: encoder.encode("fixedSalt"), iterations: 200000, hash: "SHA-256" },
        keyMaterial,
        { name: "AES-GCM", length: 256 },
        false,
        ["encrypt", "decrypt"]
    );
}
//шифрование текста перед отправкой в хранилище
async function encryptText(text) {
    const cryptoKey = await generateFixedKey();
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encryptedData = await crypto.subtle.encrypt(
        { name: "AES-GCM", iv: iv },
        cryptoKey,
        new TextEncoder().encode(text)
    );
    return {
        encryptedText: btoa(String.fromCharCode(...new Uint8Array(encryptedData))),
        iv: btoa(String.fromCharCode(...iv))
    };
}
async function decryptText(encryptedText, iv) {
    const cryptoKey = await generateFixedKey();
    const decryptedData = await crypto.subtle.decrypt(
        { name: "AES-GCM", iv: Uint8Array.from(atob(iv), c => c.charCodeAt(0)) },
        cryptoKey,
        Uint8Array.from(atob(encryptedText), c => c.charCodeAt(0))
    );
    return new TextDecoder().decode(decryptedData);
}

I’d like to know, how keep permanet passwords and and have access without additional passwords

JavaScript Error: Specified argument was out of the range of valid values

I am not very JS savvy so trying to figure out this error. Any help would be greatly appreciated.

Error:
Specified argument was out of the range of valid values. Parameter name: Class of type System.Object[] does not have a property named AL

Here’s my code:

var tempPolicyNum = "XXXXXXXX";
var record1 = ctx.Db.ExecuteSQL("Select pol.Premium as Premium,pol.PolicyNumber,pol.PolicyLines from Custom__IncentiveProgram CIP LEFT JOIN Members mem ON CIP.MemberID = mem.MemberID LEFT JOIN Policies pol ON mem.MemberID = pol.MemberID and pol.CustomCode13ID = CIP.FundYearID where pol.PolicyNumber="+ "'" + tempPolicyNum + "'") 
var GL_NP = 0, AL_NP=0, PR_NP=0, WC_NP=0;
var premiumTotal = 0;
var tableArr = [];

if(record1.Length>0){
  for(var i in record1){
    
var premiumValue = parseFloat(record1[i].Premium ? String(record1[i].Premium).replace(/[$,]/g, '') : '0');
     premiumTotal += premiumValue;
    if(record1[i].PolicyLines == "|8|"){
      WC_NP=record1[i].Premium;
    }
    if(record1[i].PolicyLines == "|9|"){
      GL_NP=record1[i].Premium;
    }
     if(record1[i].PolicyLines == "|1|"){
      AL_NP=record1[i].Premium;
    }
     if(record1[i].PolicyLines.includes("|2|")){
      PR_NP=record1[i].Premium;
    }
  }
}
tableArr.push({
            GL: GL_NP,
            AL: AL_NP,
            PR: PR_NP,
            WC: WC_NP,
           Total: '$'+ (Math.round(premiumTotal).toLocaleString('en-US')).toString().split('.')[0]
        });
        
return tableArr;