How can i re-authenticate on keycloak-js?

I’m using Keycloak in a React app and trying to implement reauthentication when a user clicks a button. The idea is to reauthenticate without redirecting the user to the Keycloak login page if they’re already logged in via SSO. Here’s my current function:

const handleReauthClick = useCallback(() => {
    if (!keycloak) {
        console.error("Keycloak is not initialized.");
        return;
    }

    const idpHint = storedTokenData?.identity_provider; // getting the identity_provider from localstorage

    console.log("Starting silent reauthentication...");

    keycloak
        .login({
            redirectUri: window.location.href,
            prompt: "none",
            idpHint,
        })
        .then(() => {
            if (keycloak.authenticated) {
                console.log("Silent reauthentication successful.");
            } else {
                console.warn("Silent reauthentication failed; redirecting to Keycloak login.");
                redirectToKeycloak(idpHint);
            }
        })
        .catch((error) => {
            console.error("SSO reauthentication error:", error);
        })
        .finally(() => {
            console.log("Reauthentication attempt completed.");
            doSomethingButton("needToTriggerSomething");
        });
}, [keycloak, redirectToKeycloak, doSomethingButton]);

The doSomethingButton function should only run if reauthentication is successful. However, I’m facing these issues:

  1. No Logs on Silent Reauth: I don’t see logs like Silent reauthentication successful. even though I see an authentication event in Keycloak. It seems like keycloak.login({ prompt: “none” }) isn’t resolving as expected.
  2. Page Reloads without doSomethingButton: The page reloads without triggering doSomethingButton.

At this point im not even sure if im actually really re-authenticating properly. Any insights will be wonderful

FATAL ERROR: Reached heap limit Allocation failed – JavaScript heap out of memory Ec2 nodejs pm2

I am using ec2 machine and running nodejs application with pm2 and i am facing this issue

0|backend  | <--- Last few GCs --->
0|backend  |
0|backend  | [432704:0x7034870]  3263111 ms: Mark-sweep (reduce) 1918.1 (1953.3) -> 1917.7 (1954.0) MB, 801.2 / 0.0 ms  (+ 295.2 ms in 86 steps since start of marking, biggest step 22.1 ms, walltime since start of marking 1334 ms) (average mu = 0.347, current mu = 0.4[432704:0x7034870]  3264816 ms: Mark-sweep (reduce) 1918.7 (1954.0) -> 1917.9 (1954.5) MB, 1697.6 / 0.0 ms  (average mu = 0.190, current mu = 0.004) allocation failure; scavenge might not succeed
0|backend  |
0|backend  |
0|backend  | <--- JS stacktrace --->
0|backend  |
0|backend  | FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
0|backend  |  1: 0xb95be0 node::Abort() [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  2: 0xa9a7f8  [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  3: 0xd6f5b0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  4: 0xd6f957 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  5: 0xf4ceb5  [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  6: 0xf5f38d v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  7: 0xf39a7e v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  8: 0xf3ae47 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  |  9: 0xf1c04a v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  | 10: 0x12e139f v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node /home/ubuntu/project/backend/dist/main.js]
0|backend  | 11: 0x170e079  [node /home/ubuntu/project/backend/dist/main.js]
0|backend  | (node:432915) NOTE: We are formalizing our plans to enter AWS SDK for JavaScript (v2) into maintenance mode in 2023.
0|backend  |
0|backend  | Please migrate your code to use AWS SDK for JavaScript (v3).
0|backend  | For more information, check the migration guide at https://a.co/7PzMCcy

on saying

pm2 monit

it is showing

enter image description here

is this total heap size ?

i have tried passing this as well

export NODE_OPTIONS=--max_old_space_size=1048;

and even

pm2 restart 0 --node-args="--max-old-space-size=1048"

so what can be the issue and how can i check my heap size assigned and used?

Asynchronous sequential API calls

I’m trying to only have two API calls running at any given time. I’m also trying to recursively call this API immediately after either finishes up until some certain condition (in this example I’ve used i < FOO). I’m also trying to wait until ALL API calls are finished. So I’m trying to have 2 concurrent API calls at all times but once either finishes then it immediately starts another.

const API_URL = "DUMMY";
const FOO = 10;

async function api(n: number) {
    let response = await fetch(API_URL.concat(`/${n}`));
    let json = await response.json();
    console.log(`${n} Finished!`);

    ++i;
    if (i < FOO) {
        return await api(n + 1);
    }
    return response;
}

var i = 0;
var promises = new Array();
for (let j = 0; j < 2; j++) {
    promises[j] = new Promise(async (resolve, reject) => {
        const response = await api(j);
        resolve(response);
    })
}

Promise.all(promises);

I’ve abstracted away some of the details but essentially this is the idea. I create two Promises that resolve once the initial api call resolves. Since it’s recursive, it should resolve after i > 10. The code compiles and it calls all API correctly, but the Promise.all returns immediately.

Thanks in advance!

Angular 18 custom input: FormControl has strange behavior

I’m struggling with some strange behavior while using my custom input component.

First of all, I built a simple abstract class that has the main “features” and methods of the component, then, the input-component which has very few code:

// Abstract class

export abstract class BaseFormInput<T> implements ControlValueAccessor, Validator, AfterViewInit, OnDestroy {
    @Input() label: string
    @Output() onChange: EventEmitter<T> = new EventEmitter<T>()

    private changeInternal: (obj: T) => void
    private changeSub: Subscription
    private disabled$ = new BehaviorSubject(false)
    private required$ = new BehaviorSubject(false)

    public input = new FormControl(null)

    ngOnDestroy() {
        this.changeSub.unsubscribe()
    }

    ngAfterViewInit() {
        this.changeSub = this.input.valueChanges.subscribe(v => {
            if (!this.disabled$.getValue()) {
                this.onChange.emit(v)
                this.changeInternal(v)
            }
        })
    }

    writeValue = (obj: T) => this.input.setValue(obj)

    registerOnChange = (fn: (obj: T) => void) => this.changeInternal = fn

    registerOnTouched = (_fn: (obj: any) => void) => {}

    setDisabledState = (isDisabled: boolean) => this.disabled$.next(isDisabled)

    validate(control: AbstractControl): ValidationErrors {
        this.required$.next(control.hasValidator(Validators.required))

        // THIS LINE HAS WEIRD BEHAVIOR
        console.log(control, control.errors)

        return null
    }

    public get isDisabled$(){
        return this.disabled$.asObservable()
    }

    public get isRequired$(){
        return this.required$.asObservable()
    }
}

The input component is simply designed like this:

@Component({
    selector: "ec-input-text",
    template: `<div class="form-control">
            <label *ngIf="label">
                {{ label }}
                <span *ngIf="isRequired$ | async">*</span>
            </label>
            <input *ngIf="type !== 'textarea'" [type]="type" [formControl]="input" [attr.disabled]="isDisabled$ | async" />
            <textarea *ngIf="type === 'textarea'" [formControl]="input" [attr.disabled]="isDisabled$ | async"></textarea>
            <ng-template></ng-template>
        </div>`,
    changeDetection: ChangeDetectionStrategy.OnPush,
    providers: [
        { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputTextComponent), multi: true },
        { provide: NG_VALIDATORS, useExisting: forwardRef(() => InputTextComponent), multi: true }
    ]
})
export class InputTextComponent extends BaseFormInput<string> {
    @Input() type: "text" | "password" | "email" | "textarea" = "text"
    @Input() maxLength: number
}

Finally, I created a register-component, which uses the input.

HTML:

<form [formGroup]="form">
    <ec-input-text label="First name" formControlName="firstName" />
    <ec-input-text label="Last name" formControlName="lastName" />
    <ec-input-text label="E-mail" formControlName="email" type="email" />
    <ec-input-text label="Password" formControlName="password" type="password" />
</form>

The TS of the register-component has a public property like this:

public form = new FormGroup({
        firstName: new FormControl(null, [Validators.required, Validators.maxLength(50)]),
        lastName: new FormControl(null, [Validators.required, Validators.maxLength(50)]),
        email: new FormControl(null, [Validators.required, Validators.maxLength(100)]),
        password: new FormControl(null, Validators.required)
    })

Now, the issue is the following: in the validate method of the abstract class (where I put a comment), I tried to log the control errors, and I get a strange behavior: when logging the formControl, I can see in the console that the property errors is null, but if I log control.errors it logs:

{ required: true }

Even though the control is valid and I typed the value (in fact, control.value has a value and results valid).
So if i do:

console.log(control)

And I expand it, errors is null (expected behavior, correct!)

But if I do:

console.log(control.errors)

It is valorized (not correct, the control is valid!)

How can I figure this out? Thanks in advance!

Select2 Shows Twice in Modal the First Time It Opens

image dropdown issue

I have the modal where this modal show the content dynamically by using js. the modal html is like this:

<div id="modal" draggable="true" class="modal fade bd-example-modal-lg hide" role="dialog">
    <div class="modal-dialog modal-dialog-centered modal-lg">
        <div class="modal-content">

            <div class="modal-header" style="margin-bottom: 10px;">
                <b class="modal-title" id="modalTitle">Modal Title</b>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>

            <div id="modalBody" class="modal-body pt-0">
                @* Modal content will be injected here *@
            </div>
        </div>
    </div>
</div>

The modal then appends content inside the “modalBody” using the template I provided. One example of a template for the content body is:

<div id="templateModalEducation">
    <div class="row">
        <div class="col-md-6 col-sm-6">
            <div class="form-group">
                <label class="Requiredinput">Year</label>
                <input type="text" class="form-control edu-year">
                <span id="edu-year-validation" class="text-validation"></span>
            </div>
        </div>
        <div class="col-md-6 col-sm-6">
            <div class="form-group">
                <label class="Requiredinput">Level</label>
                <select class="form-control select2normalWithouClear edu-level"
                    data-placeholder="Select Education Level">
                    <option></option>
                    @if (Model.EduLevelDropdownList != null)
                    {
                        foreach (var level in Model.EduLevelDropdownList)
                        {
                            <option>@level.CodeName</option>
                        }
                    }
                </select>
                <span id="edu-level-validation" class="text-validation"></span>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-12 col-sm-12">
            <div class="form-group">
                <label class="Requiredinput">Institution Name</label>
                <select class="form-control select2normalWithouClear edu-instituteName"
                    data-placeholder="Select Institution Name">
                    <option></option>
                    <option value="0">Other</option>
                    @if (Model.EduInstitutionDropdownList != null)
                    {
                        foreach (var institute in Model.EduInstitutionDropdownList)
                        {
                            <option>@institute.CodeName</option>
                        }
                    }
                </select>
                <span id="edu-instituteName-validation" class="text-validation"></span>
            </div>
            <div class="form-group">
                <input type="text" class="form-control edu-instituteNameOther">
            </div>
            <span id="edu-instituteNameOther-validation" class="text-validation"></span>
        </div>
    </div>

    <div class="modal-footer d-flex justify-content-end m-0 p-0">
        <button id="addEducationRowToMainForm" data-target-table="#tableEducation" type="button"
            class="btn btn-outline btn-primary btn-sm mb-1">
            <i class="fas fa-plus"></i> Add
        </button>
        <button id="updateEducationRow" type="button" class="btn btn-outline btn-primary btn-sm mb-1"
            style="display: none;">
            <i class="fas fa-save"></i> Update
        </button>
    </div>
</div>

This is my JavaScript code that dynamically changes the content of the modal:

<script>
    // Function to load different template content into the modal
    function loadModalContent(templateId, title) {

        // Clear previous content to prevent duplication
        $('#modalBody').empty(); 
        $('#modalBody .select2normalWithouClear').select2('destroy');

        // Load new content and set the title
        var templateContent = $('#' + templateId).html();  // Get the content of the template
        $('#modalBody').html(templateContent);             // Inject content into the modal body
        $('#modalTitle').text(title);                      // Set the modal title

        $('#modalBody .select2normalWithouClear').select2({
            placeholder: $(this).data('placeholder')
        });

        $('.edu-instituteNameOther').hide();

        $('#modal').modal('show');                         // Show the modal
    }

    // Event listener for buttons that trigger the modal with different templates
    $(document).on('click', '.openModalBtn', function () {
        var templateId = $(this).data('template');         // Get the template to load
        var modalTitle = $(this).data('title');            // Get the title for the modal

        loadModalContent(templateId, modalTitle);          // Call the function to load the content

        TMCEWoFileUpl("250", "");
    });
</script>

The problem is that when I open the modal after the page loads, it displays two dropdowns: one functions correctly, while the other seems to be a duplicate and does not work. However, when I open the modal a second time, the dropdown displays correctly and is not duplicated. Can someone help me fix this?

I’ve tried initializing it outside the modal, but it still doesn’t work.

Tabulator with multiple tables in a single page is sharing column calculations from one table to all tables, and more

I have a page where I have 18 tabulator tables in a single page. Ideally, only 2 visible at a time, but for this example I have them all visible due to trying to see if hidden tables was the issue… which it does not seem to be.

Anyway, the way this would be used is that users add materials or labor items to the tables, and when they set the quantity and cost per cells, I use the tabulator cellEdited event to multiply the values and it updates the total cell for that row. Users can add multiple rows of items with different total costs, and so each table has a columnCalc of SUM set to show the tables total amount below the total column in the table. I am creating this for a tile project estimator.

The issue shows up when I make a change to a table’s total column, it will show the column calculation on other tables that have not been used yet. If I change or make a change to the total column on a different table, it will modify the columnCalc to match the new value on all of the other tables.

Im not sure what I am doing wrong or if this is an issue with Tabulator.

Here is a live link to try. You can just add a value into a qty and cost per cell, then click to a new cell to update the total cell, then click the green add a row button. Notice how it shares the columnCalc SUM value with the other tables. It’s weird.

https://quakquakquak.com/estimator/table4b.html

I have all the code in the single page, so it was too much to put into this text box… but if you view the page source you can see the javascript I have used.

EDIT – Each table has a unique ID in the page and below I provided a block of code that is basically repeated (using different ID’s) for creating each 2 table set of Tabulators, and the add row button code.

   var tableData = [
        {id:1, item:"Select", description:"", qty:0, type:"", costper:0, total:0},
    ]

//room 1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    var room1MaterialsTable = new Tabulator("#room1MaterialsTable", {
        data:tableData,
        layout:"fitColumns",
        
        selectableRows:true,
        
        movableRows:true,
        rowHeader:{headerSort:false, resizable: false, minWidth:30, width:30, rowHandle:true, formatter:"handle"},
        columns:[
            {title:"Id", field:"id", headerSort:false, visible:false},
            {title:"Item", field:"item", headerSort:false, editor:"list", resizable:false, editorParams:{
                defaultValue:"Select",
                valuesURL:"./materials-list.php",  //get value list from URL
            }},
            {title:"Description", widthGrow:3, field:"description", editor:"input", headerSort:false, resizable:false},
            {title:"Qty", width:50, headerHozAlign:"center", field:"qty", editor:"number", hozAlign:"center", headerSort:false, editorEmptyValue:0, validator:"min:0", resizable:false, editorParams:{
            selectContents:true}},
            {title:"Type", field:"type", headerHozAlign:"center", editor:"input", hozAlign:"center", headerSort:false, resizable:false},
            {title:"Cost Per", field:"costper", headerHozAlign:"right", editor:"number", hozAlign:"right", formatter:"money", headerSort:false, resizable:false, editorEmptyValue:0, validator:"min:0", editorParams:{
            selectContents:true}},
            {title:"Total", field:"total", headerHozAlign:"right", hozAlign:"right", formatter: "money",formatterParams: { decimal: ".", thousand: ",", symbol: "$"}, bottomCalc:"sum", bottomCalcFormatter:"money", headerSort:false, resizable:false},

        ],
    });

    var room1LaborTable = new Tabulator("#room1LaborTable", {
        data:tableData,
        layout:"fitColumns",
        
        selectableRows:true,
        
        movableRows:true,
        rowHeader:{headerSort:false, resizable: false, minWidth:30, width:30, rowHandle:true, formatter:"handle"},
        columns:[
            {title:"Id", field:"id", headerSort:false, visible:false},
            {title:"Item", field:"item", headerSort:false, editor:"list", resizable:false, editorParams:{
                defaultValue:"Select",
                valuesURL:"./labor-list.php",  //get value list from URL
            }},
            {title:"Description", widthGrow:3, field:"description", editor:"input", headerSort:false, resizable:false},
            {title:"Qty", width:50, headerHozAlign:"center", field:"qty", editor:"number", hozAlign:"center", headerSort:false, editorEmptyValue:0, resizable:false, validator:"min:0", editorParams:{
            selectContents:true}},
            {title:"Type", field:"type", headerHozAlign:"center", editor:"input", hozAlign:"center", headerSort:false},
            {title:"Cost Per", field:"costper", headerHozAlign:"right", editor:"number", hozAlign:"right", formatter:"money", headerSort:false, resizable:false, editorEmptyValue:0, validator:"min:0", editorParams:{
            selectContents:true}},
            {title:"Total", field:"total", headerHozAlign:"right", hozAlign:"right", formatter:"money", bottomCalc:"sum", bottomCalcFormatter:"money", headerSort:false, resizable:false},

        ],
    });

    room1MaterialsTable.on("cellEdited", function(cell){
            
            //cell - cell component
            let fieldname = cell.getField();

            if (fieldname === "costper" || fieldname === "qty"){

                let qty = cell.getRow().getCell("qty").getValue();
                let costper = cell.getRow().getCell("costper").getValue();
                
                cell.getRow().update({"total":Number(qty * costper)});
            }

    });



    room1LaborTable.on("cellEdited", function(cell){
            
        //cell - cell component
        let fieldname = cell.getField();

        if (fieldname === "costper" || fieldname === "qty"){

            let qty = cell.getRow().getCell("qty").getValue();
            let costper = cell.getRow().getCell("costper").getValue();
            
            cell.getRow().update({"total":Number(qty * costper)});
        }
            

    });

    

    let r1mbtn = document.querySelector('#rm1addmatrow');

    let r1lbtn = document.querySelector('#rm1addlabrow');

    r1mbtn.addEventListener('click', ()=>{
        var rowCount = room1MaterialsTable.getDataCount();
        rowCount ++;
        room1MaterialsTable.addRow({id:rowCount, item:"Select", description:"", qty:0, type:"", costper:0, total:0}, false);
    })

    r1lbtn.addEventListener('click', ()=>{
        var rowCount = room1LaborTable.getDataCount();
        rowCount ++;
        room1LaborTable.addRow({id:rowCount, item:"Select", description:"", qty:0, type:"", costper:0, total:0}, false);
    })

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Thanks in advance.

Why does touchstart event freeze my program?

I’ve been working on developing a video game and have recently been working on incorporating touchscreen capabilities into the program. However, I noticed that the touch event tends to cause movement in the canvas to freeze.

Here is some code that I wrote up to demonstrate this:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <canvas id="cnvs" width="400" height="400"></canvas>

        <script>
            var canvas, canvasContext;
            var x = 0, y = 30;
            var incrementor = 1;

            window.onload = function() {
                canvas = document.getElementById('cnvs');
                canvasContext = canvas.getContext('2d');

                colorRect(0, 0, canvas.width, canvas.height, 'white');

                setInterval(updateAll, 1);

                canvas.addEventListener('touchstart', touchStart);
                canvas.addEventListener('touchend', touchEnd);
            }

            function touchStart(e) {
                console.log(e);
            }

            function touchEnd(e) {
                console.log(e);
            }

            function colorRect(p,a,i,n,t) {
                canvasContext.fillStyle = t;
                canvasContext.fillRect(p,a,i,n);
            }

            function updateAll() {
                moveAll();
                drawAll();
            }

            function moveAll() {
                x += incrementor;

                if(x > canvas.width - 20 || x < 0) {
                    incrementor = incrementor * -1;
                }
            }

            function drawAll() {
                colorRect(0, 0, canvas.width, canvas.height, 'black');
                colorRect(x, y, 20, 20, 'red');
            }
        </script>
    </body>
</html>

Notice that when you touch the canvas using a device with a touchscreen that the program seems to “stutter” a little bit. It’s not necessarily that there are errors that appear. In fact, the program runs through without any errors. The only “bug” that appears is that movement on the canvas tends to freeze when the touch event is called.

The main question I have is this: how to I write a program similar to this one that performs the exact same tasks, but without freezing the program in the process? I do not have any experience with jQuery or any other fancy JS plugins, so a way to do it using plain JavaScript would be helpful to my cause.

How to get dashboard UUID from dashboard name in ThingsBoard?

I am trying to set up a function for the “on-click” action on a general dashboard (i.e. a dashboard showing all assigned devices). I want the action to navigate to a unique dashboard based on the entityId/deviceName. I would prefer to do this sort of navigation using the built in dashboard states (which I have done before), but my employer does not want that so I am trying to develop a work around.

I found the following bit of code (that I modified slightly) which lets me hard-code in a URL:

/******* NAVIGATE TO OTHER DASHBOARD ****/

var $injector = widgetContext.$scope.$injector;
$injector.get(widgetContext.servicesMap.get('deviceService')).getDevice(entityId.id).subscribe(function (device) {

    if (device.name == 'DeviceName') {
        openDashboardState('default');
    }
});

function openDashboardState(stateId) {
    var params = {
        entityId: entityId,
        entityName: entityName
    };

    var stateObject = {
        id: stateId,
        params: {}
    };

    stateObject.params = params;
    var state = objToBase64URI([stateObject]);
    var target_dashboard_id = "dashboard-UUID"; //<-- target dashboard id
    let url = "/dashboards/" + target_dashboard_id/*+"?state="+state+""*/;
    //window.location.href = url; <- This open the other dashboard in the same window.
    window.open(url, '_blank'); // <- This open the other dashboard in a new window.
}

function objToBase64URI(obj) {
    return encodeURIComponent(objToBase64(obj));
}

function objToBase64(obj) {
    const json = JSON.stringify(obj);
    return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes(match, p1) {
            return String.fromCharCode(Number('0x' + p1));
        }));
}

However, I don’t want to expand the code to include a unique case for every single device, so ideally there is a way to get the dashboard UUID if I know it’s name, since each unique dashboard will have the same name as the device. I am not sure if there is documentation covering the functions and such that are used, but reading through the API is difficult. However if there are suggestions for navigating that I will happily listen.

Lazy Loading Audio and Video Not Loading

I’ve tried everything, even my enemy (ChatGPT). I’m not sure what I’m doing wrong. The audios and video will not load. Worst off, my text describing the video is supposed to change colours and its not doing it. Im at the end of my rope here, please help.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Assignment 3</title>
    <link rel="stylesheet" href="./styles/home.css" />
  </head>
  <body>
    <div class="audioVisualContainer">
      <!-- add lazy loaded audio or visual content here -->
       <audio controls data-src="public/trimmed1.m4a"></audio><br><br>
       <audio controls data-src="public/trimmed2.m4a"></audio><br><br>
       <audio controls data-src="public/trimmed3.m4a"></audio>
    </div>

    <!-- checkboxes, DO NOT modify! -->
    <div class="theDivs">
      <input type="checkbox" />
      <label> I agree to watch the video below.</label>
    </div>
    <div class="theDivs">
      <input type="checkbox" />
      <label>
        I agree to only scroll down if I really want to watch the video.</label
      >
    </div>
    <div class="theDivs">
      <input type="checkbox" />
      <label> I agree to respect the page author's views and opinion.</label>
    </div>

    <!-- add description and video here -->
     <video width="920" height="720" controls data-src="content/Perfect Blue.mp4">
      <source src="" type="video/mp4">
     </video>
      <br><br><p class="description">Lorem ipsum, dolor sit amet consectetur adipisicing elit. 
        Hic voluptates ratione tempore blanditiis quasi ducimus accusantium, 
        sed, sunt earum nam neque quam vel accusamus asperiores iure adipisci nisi facere. Tenetur?.
      </p>
  </body>
</html>

const lazyAudio = document.querySelectorAll('audio');
const lazyVid = document.getElementById('video');
const checkboxes = document.querySelectorAll('.theDivs');
const descriptVid = document.querySelector('.description');

const arrayCheck = () => Array.from(checkboxes).map(cb => cb.checked).reduce((a, b) => a && b, true);

const audioObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach((entry) => {
        if (entry.isIntersecting) {
            const audioPart = entry.target;
            console.log('attempting to load');
            if (audioPart.getAttribute("data-src") && !audioPart) {
                audioPart.src = audioPart.getAttribute("data-src");
                audioPart.load();
                console.log('audio loaded');
            }
            observer.unobserve(audioPart);
        }
    });
}, { threshold: 0.1 });

const videoObserver = new IntersectionObserver(entries => {
    entries.forEach((entry) => {
        if (entry.isIntersecting && entry.intersectionRatio >= 0.33 && arrayCheck()) {
            if (!lazyVid.src) {
                lazyVid.src = lazyVid.dataset.src;
                lazyVid.load();
            }
            lazyVid.play();
        } else {
            lazyVid.pause();
        }
    });
}, { threshold: 0.33 });

lazyVid.addEventListener("ended", () => {
    videoObserver.unobserve(lazyVid);
})

const descriptionObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting && entry.intersectionRatio === 1) {
            descriptVid.style.color = "purple";
            observer.unobserve(descriptVid);
        }
    });
}, { threshold: 1.0 });


lazyAudio.forEach(lazyAudio => audioObserver.observe(lazyAudio));
videoObserver.observe(lazyVid);
descriptionObserver.observe(descriptVid);

It should load each audio after 1 second, load the video when it is in the viewport 1/3 and change the text color after a few seconds. (Really the priority is the audio and video).
I tried various things, like maybe my path was wrong, maybe the code has an error somewhere and i couldnt find what I’m doing wrong. I did check the network tab and the sources dont even appear which is odd, considering the path is right.

I cannot change anything the HTML file besides the sections where audios and videos are added.

I am trying to adjust sticky positioning for my header dynamically with JS. How can I achieve this?

This is my first personal project and I am having trouble dynamically altering the sticky positioning on my header with JS. All I want to do is to remove the sticky positioning I have set on the head class after I have scrolled past the bottom of the intro class. How do I achieve this using JavaScript?

This is the html code I have for the header and intro sections.

<header id="head">
        <div class="title">
            <h1 style="font-size: 2.5rem; margin-left: 10px;">
                Health & Wellness
            </h1>
            <img src="C:Tech StackWebsite DesignCSS PracticeeImagesdownload (2).png" style="height: 60px; width: 100px; border: none; object-fit: cover;">
        </div>
        <nav class="navigation">
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
                aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarNav"></div>
            <button type="button" class="btn btn-light" id="search"><i class="bi bi-search" style="font-size: 15px;"></i>Search</button>
            <button class="btn btn-primary" id="home">Home</button>
            <button class="btn btn-success" id="about">About</button>
            <button class="btn btn-info" id="services" style="color: white">Services</button>
        </div>
        </nav>
    </header>

    <div class="intro" id="intro">
        <h2>Health & Wellness Blog Articles</h2>
        <p>Read posts from experts at Harvard Health Publishing covering a variety of health topics and perspectives on medical news.
        </p>
    </div>

Here is the CSS

#head{
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin: 10px;
    background-color: #CDFFFC;
    min-height: 150px;
    padding: 15px;
    
}
.sticky {
    position: sticky;
    top: 0;
    width: 100%;
    z-index: 1000;
    background-color: #CDFFFC;

And here is the Javascript



window.addEventListener('scroll', function() {
    
    const header = document.getElementById('head');
    const introSection = document.getElementById ('intro');

    // Calculate the bottom position of the intro section

    const introBottom = introSection.offsetTop + introSection.offsetHeight;

    if(window.scrollY >= introBottom) {
        header.classList.remove('sticky');
    }else {
        header.classList.add('sticky');
    }
});

Google Map Api (Places api) diffrent result?

my problem is im trying to make Searching service.
so, im trying to find some market like “공주 행복자전거” but, i can’t find by places api.
and i try google map service. (not api) and i find it.

i dont know why my script cant found. and why google map can found.

<style>
  #map {height: 400px; width: 100%; }
</style>

<script src="https://maps.googleapis.com/maps/api/js?key=<?=$apiKey?>&libraries=places"></script>
   
<body>
  <h1>장소 검색</h1>
  <input id="location" type="text" placeholder="검색할 장소 입력" />
  <button id="search">검색</button>
  <div id="map"></div>
  <script src="script.js"></script>
</body>
  
 <script>
let map;
let service;

function initMap() {
    const location = new google.maps.LatLng(-33.867, 151.195); // 초기 위치 설정

    map = new google.maps.Map(document.getElementById("map"), {
        center: location,
        zoom: 15,
    });

    service = new google.maps.places.PlacesService(map);

    document.getElementById("search").addEventListener("click", searchPlaces);
}

function searchPlaces() {
    const locationInput = document.getElementById("location").value;
    
    const request = {
        query: locationInput,
        fields: ["name", "geometry"],
    };

    service.findPlaceFromQuery(request, (results, status) => {
        if (status === google.maps.places.PlacesServiceStatus.OK) {
            for (let i = 0; i < results.length; i++) {
                createMarker(results[i]);
            }
            map.setCenter(results[0].geometry.location);
        } else {
            alert("장소를 찾을 수 없습니다: " + status);
        }
    });
}

function createMarker(place) {
    const marker = new google.maps.Marker({
        map: map,
        position: place.geometry.location,
        title: place.name,
    });

    const infowindow = new google.maps.InfoWindow({
        content: place.name,
    });

    marker.addListener("click", () => {
        infowindow.open(map, marker);
    });
}

google.maps.event.addDomListener(window, "load", initMap);


   </script>

im trying to many type of search on places api.

findPlaceFromQuery, nearbySearch, textSearch..
but i cant find..

Preventing Extra Line Breaks When Pasting HTML Links in Outlook or Word via Clipboard API?

I’m using JavaScript’s Clipboard API to create a “link generator” that allows users to enter a URL, display text, and press a button to copy a link into Office suite/Outlook much the same way Edge works when copying a link from the address bar. It lets users copy HTML link data to the clipboard, so users can paste it directly into Microsoft Office apps (e.g., Outlook, Word, Teams). When pasting into Excel or Teams, the link appears correctly, but pasting into Word or Outlook results in an unwanted line break or “carriage return” after the link.

Here’s my code snippet for copying the link:

function copyLinkToClipboard() {
    const linkText = document.getElementById('linkText').value.trim();
    const url = document.getElementById('url').value.trim();
    const screenReaderText = document.getElementById('screenReaderText').value.trim();

    if (!linkText || !url) {
        flashButtonError('generateOutlookButton');
        document.getElementById('generatedLinkCode').value = 'Error: Please fill out the URL and Display Text fields.';
        return;
    }

    const html = `<a href="${url}"${screenReaderText ? ` title="${screenReaderText}"` : ''}>${linkText}</a>`;

    navigator.clipboard.write([
        new ClipboardItem({
            "text/html": new Blob([html], { type: 'text/html' }),
            "text/plain": new Blob([linkText], { type: 'text/plain' })
        })
    ]).then(() => {
        flashButtonEffect('generateOutlookButton');
        document.getElementById('generatedLinkCode').value = "Link copied. If pasting into Word or Outlook, press Backspace after pasting to remove the included carriage return.";
    }).catch(err => {
        console.error('Failed to copy: ', err);
        flashButtonError('generateOutlookButton');
        document.getElementById('generatedLinkCode').value = 'Error: Unable to copy the link. Please try again.';
    });
}

Problem: When users paste the copied link into Word or Outlook, it automatically adds a line break, as if the Enter key was pressed after the link. This behavior isn’t ideal, and I’d prefer the cursor to remain directly after the link text without an extra line break. It works fine in Excel and Teams, but not in Word and Outlook.

Question: Is there a way to modify the code so the link doesn’t add this extra line break when pasted in Word or Outlook? Any help or insight would be greatly appreciated!

I’ve tried different formats and experimented with the Blob content types, but haven’t been able to prevent this extra line break.

In angular, ng-repeat breaks the options into options again

My js array is [“standard”, “premium”]

When I click on the dropdown option for the first time, it’s showing option as standard and premium. After choosing any one of them the chosen values is not displayed in the box. If I do click again (assume I have selected standard in the first go) then it’s giving options as s,t,a,n,d,a,r,d.

My code is:

<select id = "red", ng-model="red" class= "form-select">
<option ng-repeat=" r in red">[[red]]</option>
</select>

In angular ng-repeat breakes the options into options again

my js array is [“standard”, “premium”]

When I click on the dropdown option for the first time , its showing option as standard and premium. After choosing any one of them the choosen values is not displayed in the box. If I do click again (assume I have selected standard in the first go)then its giving options as s,t,a,n,d,a,r,d .

my code is

<select id = "red", ng-model="red" class= "form-select">
<option ng-repeat=" r in red">[[red]]</option>
</select>

userId field missing when adding posts – logged-in user info not saving

I’m trying to add posts to my Firestore database, but I’m running into an issue where the userId field (which should contain the logged-in user’s ID) isn’t being saved in the document. Here’s

I’m attempting to add a new post to Firestore in my React Native app. Each post should include fields like userId (from the logged-in user), content, timestamp, etc. I have checked the following:

The user is authenticated, and user.uid appears as expected when I log it to the console.
My Firestore security rules allow authenticated users to write to the posts collection.
I’m calling addDoc on the posts collection, but no new documents are appearing in Firestore.
I expected the post to save successfully, with userId populated from the logged-in user’s uid. User id is undefined or doesn’t exist. However, the document is not being saved at all, and no errors are being thrown in my code.

cannot find userid Something went wrong with adding post to Firestore:’, error).

path: apppostsposts.jsx

import {
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableOpacity,
  Image,
  ScrollView,
  Alert
} from 'react-native';
import React, { useContext, useState } from 'react';
import { AntDesign, Feather } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import { useAuth } from '../context/authContext';
import * as ImagePicker from 'expo-image-picker';
import { getFirestore, collection, addDoc } from 'firebase/firestore';
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage';

const AddPost = () => {
  const router = useRouter();
  const { user } = useContext(useAuth);
  const handleClose = () => {
    router.back();
  };

  const [image, setImage] = useState(null);
  const [post, setPost] = useState('');

  const handlePost = async () => {
    if (!user) {
      Alert.alert('Error', 'You must be logged in to post.');
      return;
    }

    const imageUrl = await uploadImage();
    console.log('Image Url:', imageUrl);
    console.log('Post:', post);

    const db = getFirestore();
    try {
      await addDoc(collection(db, 'posts'), {
        userId: user.uid, // Ensure user.uid is defined
        post: post,
        postImg: imageUrl,
        postTime: new Date(),
        likes: [],
        comments: [],
      });
      console.log('Post Added');
      Alert.alert('Post published', 'Your Post has been published');
      setPost('');
      setImage(null);
    } catch (error) {
      console.log('Something went wrong with adding post to Firestore:', error);
      Alert.alert('Error', 'Something went wrong while adding your post.');
    }
  };

  const uploadImage = async () => {
    if (!image) {
      return null; // Return null if there's no image
    }

    const response = await fetch(image);
    const blob = await response.blob();

    const storage = getStorage();
    const storageRef = ref(storage, `photos/${Date.now()}`);

    try {
      await uploadBytes(storageRef, blob);
      const url = await getDownloadURL(storageRef);
      return url;
    } catch (e) {
      console.log('Image upload error:', e);
      return null;
    }
  };

  const handleAddMedia = async () => {
    const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (permissionResult.granted === false) {
      Alert.alert('Permission required', 'Permission to access camera roll is required!');
      return;
    }

    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.All,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
    });

    if (!result.cancelled) {
      setImage(result.uri);
    }
  };

  return (
    <ScrollView style={styles.container}>
      <View style={styles.header}>
        <TouchableOpacity onPress={handleClose}>
          <AntDesign name="arrowleft" size={24} color="black" />
        </TouchableOpacity>
        <Text style={styles.headerTitle}>Add Post</Text>
        <TouchableOpacity onPress={handlePost}>
          <Text style={styles.postButton}>Post</Text>
        </TouchableOpacity>
      </View>

      <View style={styles.content}>
        <TextInput
          style={styles.textInput}
          placeholder="What's on your mind?"
          multiline
          numberOfLines={4}
          value={post}
          onChangeText={setPost}
        />
        <TouchableOpacity onPress={handleAddMedia} style={styles.addMediaButton}>
          <Feather name="image" size={20} color="white" />
          <Text style={styles.addMediaText}>Add Media</Text>
        </TouchableOpacity>
        {image && (
          <>
            <Image source={{ uri: image }} style={styles.imagePreview} />
            <Text style={styles.imagePreviewText}>{image}</Text>
          </>
        )}
      </View>
    </ScrollView>
  );
};

export default AddPost;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    paddingHorizontal: 16,
    paddingTop: 25,
  },
  header: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingVertical: 20,
  },
  headerTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    textTransform: 'capitalize',
  },
  postButton: {
    color: '#34c759',
    fontWeight: 'bold',
    textTransform: 'uppercase',
  },
  content: {
    marginVertical: 20,
  },
  textInput: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 10,
    padding: 16,
    height: 120,
    fontSize: 16,
    marginBottom: 10,
  },
  addMediaButton: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#34c759',
    padding: 10,
    borderRadius: 10,
    justifyContent: 'center',
  },
  addMediaText: {
    color: '#fff',
    fontWeight: 'bold',
    marginLeft: 5,
  },
  imagePreview: {
    width: '100%',
    height: 200,
    marginTop: 10,
    borderRadius: 10,
    resizeMode: 'cover',
  },
  imagePreviewText: {
    color: '#888',
    fontSize: 12,
    marginTop: 5,
    textAlign: 'center',
  },
});

path: appcontextauthContext.jsx

import { createContext, useContext, useEffect, useState } from "react";
import {
  onAuthStateChanged,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut
} from "firebase/auth";
import { auth, databaseFB } from "../../FirebaseConfig";
import { doc, getDoc, setDoc } from "firebase/firestore";

// Create the context
export const AuthContext = createContext();

// AuthContextProvider component
export const AuthContextProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(undefined);

  // Monitor authentication state
  useEffect(() => {
    const unsub = onAuthStateChanged(auth, (user) => {
      if (user) {
        setIsAuthenticated(true);
        updateUserData(user.uid);
      } else {
        setIsAuthenticated(false);
        setUser(null);
      }
    });
    return unsub;
  }, []);

  const updateUserData = async (userId) => {
    const docRef = doc(databaseFB, 'users', userId);
    const docSnap = await getDoc(docRef);
    if (docSnap.exists()) {
      const data = docSnap.data();
      setUser({ ...user, username: data.username, userType: data.userType, userId: data.userId });
    } else {
      console.log('No such document in users collection!');
    }
  };

  // Login function
  const login = async (email, password) => {
    try {
      const response = await signInWithEmailAndPassword(auth, email, password);
      setUser(response.user);
      setIsAuthenticated(true);
      return { success: true, data: response.user };
    } catch (e) {
      let msg = e.message;
      if (msg.includes('auth/wrong-password')) msg = 'Wrong password';
      if (msg.includes('auth/invalid-email')) msg = 'Invalid email';
      if (msg.includes('auth/user-not-found')) msg = 'User not found';
      return { success: false, msg };
    }
  };

  // Logout function
  const logout = async () => {
    try {
      await signOut(auth);
      setUser(null);
      setIsAuthenticated(false);
      return { success: true };
    } catch (e) {
      return { success: false, msg: e.message, error: e };
    }
  };

  // Register function
  const register = async (email, password, username, userType) => {
    try {
      if (!username || !userType) {
        return { success: false, msg: 'Username and user type are required' };
      }
      const response = await createUserWithEmailAndPassword(auth, email, password);
      await setDoc(doc(databaseFB, "users", response.user.uid), {
        username,
        userType,
        userId: response.user.uid,
      });
      return { success: true, data: response.user };
    } catch (e) {
      let msg = e.message;
      if (msg.includes('auth/invalid-email')) msg = 'Invalid email';
      return { success: false, msg };
    }
  };

  return (
    <AuthContext.Provider value={{ user, isAuthenticated, login, register, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

// Custom hook for consuming auth context
export const useAuth = () => {
  const value = useContext(AuthContext);
  if (!value) {
    throw new Error("useAuth must be wrapped inside AuthContextProvider");
  }
  return value;
};
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAuth, initializeAuth, getReactNativePersistence } from 'firebase/auth';
import { getFirestore, collection } from 'firebase/firestore';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Firebase configuration
const firebaseConfig = {
  apiKey: "AIzaSyAe3TwmcwhAoSqtNT12XBsHFqFkUxnSx0g",
  authDomain: "internconnectdb.firebaseapp.com",
  projectId: "internconnectdb",
  storageBucket: "internconnectdb.appspot.com",
  messagingSenderId: "445282960631",
  appId: "1:445282960631:web:a2cf595a4f345f62933ff2"
};

FirebaseConfig.ts

// Initialize Firebase
const app = initializeApp(firebaseConfig);

export const auth = initializeAuth(app,{
  persistence: getReactNativePersistence(AsyncStorage)
});

export const databaseFB = getFirestore(app);

export const usersRef = collection(databaseFB,'users');