Angular dragover css on item in recursive calling list item component

I have an angular component in which it renders the navigation list items recursively.
I want to implement drag and drop on every list item. And on drag over it should highlight particular item on which item is hovered.

Here is the code

<a [ngStyle]="{ 'padding-left': depth * 12 + 'px' }" [ngClass]="{
        'app-drag-over': dragOverItem?.id === item?.id
    }" class="nav-list-item" (drop)="onDrop($event, item)" (dragover)="onDragOver($event, item)"
    (dragleave)="onDragLeave($event, item)">
    <div *ngIf="!collapsed">
        {{ item.displayName }}
    </div>
    <span *ngIf="item.children && item.children.length">
        <i class="material-icons" [@indicatorRotate]="expanded ? 'expanded' : 'collapsed'"> expand_more </i>
    </span>
</a>
<div *ngIf="expanded">
    <nav-list-item *ngFor="let child of item.children" [item]="child" [depth]="depth + 1">
    </nav-list-item>
</div>

and here is ts functions

onDragOver(event: DragEvent, item): void {
    this.dragOverItem = item;
    console.log("onDragOver ~ item:", item);
    event.preventDefault();
}

onDragLeave(event: DragEvent, item): void {
    this.dragOverItem = null;
    console.log("onDragLeave ~ item:", item);
    event.preventDefault();
}

i have added console logs in the drag functions but due to recursive rendering, i am getting too many calls in the events on drag due to which it is slowing down the browser and application and show tha border on item very late.

And if i move the dragged item to another, it is showing flicker and applying very late.

any solution on this?

Any way to replace the exit intent prompt from the beforeunload event with a custom modal? [duplicate]

When any user open a certain page, a request is made to change some status in a database, and I want to revert this status if the user closes the page without using the “save” button.

To that end, I declared a “window.addEventListener(‘beforeunload’, functionName)” in the page’s code within an useEffect. If I declare “event.preventDefault()” in said eventListener, a prompt is issued asking the user if they really want to close the page.

I want to execute a function only if and after the user press the “Exit” button in the prompt, but, currently, it is executed rigth after the prompt is issued, it doesn’t await the user’s response.

By the other hand, if I remove the “preventDefault”, the prompt won’t appear anymore and the function will execute at the same time the page is closed, but it only works if the user already interacted with the page contents, if they just open the page and close it, without interactions, the function is not executed, and I desired it to be.

For that end, I wanted to replace the exit intent prompt with a modal of my own, in a way I can add onClick events to the modal buttons and then proceed to close the page, there’s any way to do that or the “execute function only if the user interacted with the page” approach is the only way?

Currently, my code looks like this:

useEffect(() => {
  const handleBeforeUnload = async (event) => {
   event.preventDefault();
     try {
       await requestChangeStatus(+route.query.dataI, +route.query.dataII)
     } catch (error) {
       await errorLog(error);
     }
  };

  window.addEventListener('beforeunload', handleBeforeUnload);

  return () => {
   window.removeEventListener('beforeunload', handleBeforeUnload);
  }
}, [])

MS Outlook Addin is repeatedly failing to make requests to the right host sever

When pushing out apps via O365 admin, it takes forever to replicate outwards. Microsoft will tell you that it can take 24-72 hours. While we were waiting to see if our fixes worked, one of the changes I made took almost 2 days propagate.

The outlook addin in question has static js assets that make network requests to our host server to process emails. The issue is these static assets don’t seem to update correctly or at all when code changes are pushed which results in failed network requests to our host server due to a mangled url that has since been updated.

The host server itself is up to date and if you took the manifest file and uploaded(side-loaded) it manually to your destkop outlook, it would work fine. My POV on part of the problem here is Microsoft and how long they take to update cache.

Any tips and tricks to a better or faster dev cycle would be much appreciated.

Here is what has happened so far.

  1. I removed the add-in from O365, waited a day until is was removed from the organization. That wokred overnight.

  2. I added it back with updated manifest and waited overnight for it to show back up in outlook.

  3. In our js assets we make network request via : “xhr.open(“POST”, “/api/scan.php”, true);” but this doesn’t work because a different origin/domain is added to the beginning of the string.

  4. We changed it to (“POST”, HOST_SERVER + “/api/scan.php”, true);. This did not fix the issue.

Intercept iframe trying to redirect a parent page

I have an iFrame which contains a content that I cannot control. In some cases this iFrame redirects the top window which I want to prevent. Ideally I want to intercept this redirect, retrieve the redirect URL, and then redirect the top page to a different URL.

I’ve tried to use window beforeunload event, but it also adds a popup about “are you sure you want to leave a site” which I also want to avoid.

Also tried to use “sandbox” but it blocks redirect completely and I haven’t found a way to react on the redirection attempt in that case.

My current code as simple as that:

<iframe
  src="my embed url"
/>

Will php mb_strlen($str,‘utf8’) ever return a greater result than JavaScript .length?

I’m working on an Angular 17 reactive form where I send the form data to a PHP API on the server and store it in a database.
I would like the user to be able to input emojis to the form so I have set my database to utf8mb4_unicode_ci collation so that the emojis can be stored.

Security is very important to me so I do several checks on both the client side and the server side for various things.

One of the checks I do is to check the length of the input. I was wondering If you can help because the length results are inconsistent on client side and server side (since the string contains emojis).

On using the JavaScript .length property and also the built-in Angular Form Validators called minLength and maxLength I see that they all calculate the length in the same way (for example most of the basic smilie emojis are calculated as having a length of 2).

However when I send this data (which includes emojis) to the server side I use the PHP method called mb_strlen($subject, 'utf8') and the values are different (most of the basic smilie emojis are calculated as having a length of 1 and also they take up 1 varchar character in the database).

I’ve tested about 160 emojis to see what values they return on both client side and server side in order to try and work out a pattern (so that I can do checks for length in the right way).

As you can see from my screenshots below in most cases mb_strlen($subject,‘utf8’) returns a lower value for the length than JavaScript .length. Sometimes it returns the same value as JavaScript .length property but in all these cases mb_strlen($subject,‘utf8’) has never returned a length greater than what JavaScript .length returns.

Length of Emojis in JavaScript and PHP

Length of Emojis in JavaScript and PHP

Is it safe to assume that mb_strlen($subject,‘utf8’) will never return a value greater than JavaScript .length. for the rest of existing emojis that I have not tested?

If not could you explain a bit more about this and could you give some examples of characters where mb_strlen($subject,‘utf8’) would return a greater value than JavaScript .length?

Thank you

Re-exporting class in a namespace object loses its “type” in JSDoc

I have issue with VS Code, TypeScript (not used directly) and JSDoc where I re-export a class from a library through a namespace object to users code and type is not properly recognized in all uses:

path.js (class definition):

'use strict';

class Path {}

module.exports = Path;

paths.js (namespace):

'use strict';

module.exports = {
    'Path': require('./path')  // Re-export class
};

Actual user code:

'use strict';

const paths = require('./paths');

const p = new paths.Path();  // Works fine

/**
 * @param {paths.Path} path  <-- Error
 */
function drawPath(path) {

}

When using paths.Path as a type inside @type or @param I get TS error saying:

‘paths.Path’ refers to a value, but is being used as a type here. Did you mean ‘typeof paths.Path’? ts(2749)

I’m using regular JS code with JSDoc comments and have jsconfig.json file

How can I avoid this error so that a class in a namespace object could be used as a type in JSDoc comments?

How to write a function to plot all of the schools in Ohio using geocoding?

I’ve been working on this project for many months now, and on this problem in particular for at least four days so I figured I’d ask the experts. I’m new to web development and entirely self-taught, and I’m seeking guidance on where my code is going wrong.

I’m trying to run through my CSV file which contains school names (Name), street addresses (Address), city (City), and zip code (ZIP), and state (State).

Using Papa.parse to read through the CSV, I’m trying to pass this CSV information through the opencagedata geocoding API.

I believe there must be an issue with either the function structure or the variable names. The web developer tool says that the js and css files are loading properly.

map.js

// BENNETT DON'T TOUCH THIS !!!
var map = L.map('map').setView([39.983334, -82.983330], 9);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var baseMaps = {
    "Street View": L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'),
    "Satellite View": L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}')
};
L.control.layers(baseMaps).addTo(map);

// Test marker
L.marker([39.983334, -82.983330]).addTo(map)
    .bindPopup('Bennett Made A Popup!')
    .openPopup();

// Convert Papa.parse to return a Promise
function parseCSV(csvFile) {
    return new Promise((resolve, reject) => {
        Papa.parse(csvFile, {
            header: true,
            complete: results => resolve(results.data),
            error: error => reject(error)
        });
    });
}

async function geocodeAddress(address) {
    try {
        const encodedAddress = encodeURIComponent(address);
        const geocodingUrl = `https://api.opencagedata.com/geocode/v1/json?q=${encodedAddress}&countrycode=us&bounds=-84.99023,38.44498,-80.28809,42.56926&key=MY_API_KEY`;

        const response = await fetch(geocodingUrl);
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }    
const response = await fetch(geocodingUrl);
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    
    if (data.results?.[0]?.geometry) {
        return data.results[0].geometry;
    }
    console.warn('No results found for address:', address);
    return null;
} catch (error) {
    console.error('Error geocoding address:', error);
    return null;
}
async function plotSchools(csvFile) { 
    try {
    // Fetch and parse CSV
        const response = await fetch(csvText);
        const csvText = await response.text();
        const schools = await parseCSV(csvText);

    // Process each school
    for (const school of schools) {
        const { "Street Address": Address, City, State, ZIP, Name } = school;
        
        if (!Address || !City || !State || !ZIP) {
            console.warn(`Incomplete address for school: ${Name}`);
            continue;
        }

        const fullAddress = `${Address}, ${City}, ${State} ${ZIP}`;
        
        const coords = await geocodeAddress(fullAddress);
        if (coords) {
                console.log(`Coordinates for ${Name}:`, coords.lat, coords.lng);
            L.marker([coords.lat, coords.lng])
                .addTo(map)
                .bindPopup(`<b>${Name}</b>`);
        } else {
            console.warn(`No coordinates found for ${Name}`);
        }
    }
} catch (error) {
    console.error('Error plotting schools:', error);
    throw error;
}
}// Initialize with proper CSV file path
plotSchools('data/school-data/ohioschools.csv')
    .catch(error => console.error('Failed to plot schools:', error));

index.html

My Community Map


<!--Linking CSS-->
<link rel="stylesheet" href="./css/styles.css"/>

<!--Linking Leaflet CSS-->
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
 integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
 crossorigin=""/>

 <!--Linking Google Fonts-->
 <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap">
</head>
<body>
<div id="map" style="height: 500px;"></div>
</body>

Is that refactor of fetch function fine?

I had previous verison of my code, that looked like this

const getJSON = function (url, errorMsg = 'Something went wrong') {
  return fetch(url).then(response => {
    if (!response.ok) throw new Error(`${errorMsg} (${response.status})`);
    return response.json();
  });
};

But after all I got an idea to refactor it, to that one

const getJSON = function (url, errorMsg = 'Something went wrong') {
  return (async function () {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`${errorMsg} (${response.status})`);
    return response.json();
  })();
};

So I’m curious, if my solution of refactor fine, using IIEF in this case.

There’s no error or something.

Javascript dont enter if statement when its true

I’m trying to make online version of Windows XP, and I’m currently on making moving multiple selected apps on desktop grid and I encounter some error in my functions’ logic however I do not know why it occurs. So the problem only appears when I move at least 3 items at the same time. When I move more than 3 apps to the place when other apps exists, it just overrides one of the apps. I’ve tried to debug it and I find the moment when it fails however I don’t know why because it don’t enter one of my if statements however is I print the condition after statement it returns true. So to visualize it better I will first show this issue on the UI.

So if we have 5 apps on desktop
problem visualization 1

and I will select 3 of them and then move it to column one and rows 0,1,2 one of the apps will disappear
problem screenshot 2
problem screenshot 3

So looking at the code first thing you need to know is that all of the frontend code is written in vue, i represent grid as 2 dimensional array and store it in data. if the grid slot is empty i represent it as null and if not it contains object with informations like app title, icon, selcted and id.

<div class="desktop-row" v-for="(row, rowIndex) in desktopGridLayout" :key="rowIndex">
        <div class="desktop-slot" v-for="(slot, slotIndex) in row" :key="slotIndex" ref="appSlots">
            <div class="app-wrapper"  v-if="slot" @mousedown="registerGrab($event, slot)" @touchstart="registerGrab($event, slot)" @click="selectApp($event, slot)" :class="{
                selected: slot.selected
            }" :id="`slot${slot.id}`" @dblclick="slot.edit = true">
                <div class="image-container">
                    <img :src="slot.icon" :alt="slot.title">
                    <div class="select-overlay" :style="{
                        maskImage: `url(${slot.icon})`
                    }">

                    </div>
                </div>
                <input type="text" v-if="slot.edit" v-model="slot.title">
                <h3 v-else>{{slot.title}}</h3>
            </div>
        </div>
    </div>

I also have array of object containg apps inforamtions (title, id, icon, row, col, selected)

apps: [
                {
                    id: 0,
                    title: "Kosz bardzo długa nazwa",
                    icon: "icons/Recycle Bin (empty).png",
                    row: 0,
                    col: 0,
                    selected: false,
                    edit: false,
                },
                {
                    id: 1,
                    title: "Mój komputer",
                    icon: "icons/My Computer.png",
                    row: 1,
                    col: 0,
                    selected: false,
                    edit: false,
                }
            ],

So I can use it to re-render grid to resize to make this responsive. To move and re-render grid, I use update grid function which gets the number of columns and rows as arguments and then re-render the grid.

updateGrid(numOfRows = this.desktopGridLayout.length, numOfCols = this.desktopGridLayout[0].length) {
            // console.error("new grid update");
            const newGrid = []
            for (let i = 0; i < numOfRows; i++) {
                newGrid.push([])
                for (let j = 0; j < numOfCols; j++) {
                    newGrid[i].push(null)
                }
            }
            this.apps.sort((a,b) => {
                let rowDiff = a.row - b.row
                let colDiff = a.col - b.col
                if (colDiff === 0) {
                    return rowDiff
                }
                return colDiff
            }).sort(function (x, y) {
                return (x === y) ? 0 : x ? 1 : -1;
            }).forEach(app => {
                let row = app.row
                let col = app.col
                if (row >= newGrid.length ) {
                    row = newGrid.length - 1
                } else if (row < 0) {
                    row = 0
                }

                if (col >= newGrid[0].length ) {
                    col = newGrid[0].length - 1
                } else if (col < 0) {
                    col = 0
                }

                // console.log("Moving app: ", app.title, " from: ", app.row, app.col, " to: ", row, col)
                if (newGrid[row][col] !== null) {
                    // console.log("Slot is occupied moving occupied app down")
                    this.moveAppDown(row, col, newGrid)
                }

                console.log(`Selected app finished on (${row}, ${col}) ${newGrid[row][col]}`)


                newGrid[row][col] = {
                    title: app.title,
                    icon: app.icon,
                    id: app.id,
                    selected: app.selected
                }

                this.apps[this.apps.indexOf(app)] = {
                    ...app,
                    row: row,
                    col: col
                }
            })
            this.desktopGridLayout = newGrid
        }

If the app will be placed on the place of other app, it will trigger the move app down function. However, here is the problem because after watching consol log some apps don’t enter the if even is the slot is not empty, and I don’t have idea why.

moveAppDown(rowIndex, colIndex, grid = this.desktopGridLayout) {
            let newColIndex = colIndex;
            let newRowIndex = rowIndex + 1;

            if (newRowIndex >= grid.length) {
                newRowIndex = 0

                newColIndex = colIndex + 1
                if (newColIndex >= grid[0].length) {
                    const emptySlotRow = grid.find(row => row.some(col => col === null))

                    newRowIndex = grid.indexOf(emptySlotRow)
                    newColIndex = grid[newRowIndex].indexOf(null)
                }
            }

            // console.log("Moving", grid[rowIndex][colIndex].title, "from", rowIndex, colIndex, "occupied slot to", newRowIndex, newColIndex)
            if (grid[newRowIndex][newColIndex] !== null) {
                console.log("entering reccurention")
                this.moveAppDown(newRowIndex, newColIndex)
            }

            const app = this.apps.filter(app => app ? app.id === grid[rowIndex][colIndex].id : false)[0]

            console.log(`Moved app finished on (${newRowIndex}, ${newColIndex})`, grid[newRowIndex][newColIndex], grid[newRowIndex][newColIndex] !== null)

            grid[newRowIndex][newColIndex] = grid[rowIndex][colIndex]
            grid[rowIndex][colIndex] = null

            this.apps[this.apps.indexOf(app)] = {
                ...app,
                row: newRowIndex,
                col: newColIndex
            }
        }

I will also provide you function that handle app movement on grab, but there are still some parts of the code missing because this component is huge, and I don’t want to make this text so long. That’s why you can also see a full component as well as a project on this GitHub repo. https://github.com/kremobil/Portfolio/blob/main/resources/js/Components/Desktop.vue

Here are also handle Grab function I talked about

handleGrab(mouseX, mouseY) {
            const app = this.apps.filter(app => app.id === this.grabActive)[0]
            const appWrapper = app.element.querySelector(".app-wrapper")
            appWrapper.style.position = "absolute";
            appWrapper.style.top = mouseY + this.grabOffset.y + "px"
            appWrapper.style.left = mouseX + this.grabOffset.x + "px"
            appWrapper.style.zIndex = 500

            // also move selected objects
            this.apps.filter(app => app.selected && app.id !== this.grabActive).forEach(selectedApp => {
                const yOffset = selectedApp.element.getBoundingClientRect().y - app.element.getBoundingClientRect().y
                const xOffset = selectedApp.element.getBoundingClientRect().x - app.element.getBoundingClientRect().x

                const selectedAppWrapper = selectedApp.element.querySelector(".app-wrapper")
                selectedAppWrapper.style.position = "absolute";
                selectedAppWrapper.style.top = mouseY + this.grabOffset.y + yOffset + "px"
                selectedAppWrapper.style.left = mouseX + this.grabOffset.x + xOffset + "px"
                selectedAppWrapper.style.zIndex = 500
            })

            const xDiff = mouseX - this.mouseStartingPosition.x
            const yDiff = mouseY - this.mouseStartingPosition.y

            if (Math.abs(xDiff) > 10 || Math.abs(yDiff) > 10) {
                this.blockNextClick = true

                let nextRowIndex = app.row
                let nextColIndex = app.col

                if (xDiff < -((this.iconSize.width / 5) * 3 + this.iconSize.gap)) {
                    this.mouseStartingPosition.x -= this.iconSize.width + this.iconSize.gap
                    nextColIndex = nextColIndex - 1 > 0 ? nextColIndex - 1 : 0

                } else if (xDiff > ((this.iconSize.width / 5) * 3 + this.iconSize.gap)) {
                    this.mouseStartingPosition.x += this.iconSize.width + this.iconSize.gap
                    nextColIndex = nextColIndex + 1 >= this.desktopGridLayout[0].length ? this.desktopGridLayout[0].length - 1 : nextColIndex + 1
                }

                if (yDiff < -((this.iconSize.height / 5) * 3 + this.iconSize.gap)) {
                    this.mouseStartingPosition.y -= this.iconSize.height + this.iconSize.gap
                    nextRowIndex = nextRowIndex - 1 > 0 ? nextRowIndex - 1 : 0
                } else if (yDiff > ((this.iconSize.height / 5) * 3 + this.iconSize.gap)) {
                    this.mouseStartingPosition.y += this.iconSize.height + this.iconSize.gap
                    nextRowIndex = nextRowIndex + 1 >= this.desktopGridLayout.length ? this.desktopGridLayout.length - 1 : nextRowIndex + 1
                }

                if(app.row !== nextRowIndex || app.col !== nextColIndex) {
                    // update other selected apps
                    this.apps = this.apps.map(selectedApp => {
                        let nextSelectedRowIndex = nextRowIndex - app.row
                        let nextSelectedColIndex = nextColIndex - app.col

                        return {
                            ...selectedApp,
                            row: selectedApp.selected || selectedApp.id === app.id ? selectedApp.row + nextSelectedRowIndex : selectedApp.row,
                            col: selectedApp.selected || selectedApp.id === app.id ? selectedApp.col + nextSelectedColIndex : selectedApp.col
                        }
                    })
                }
            }
        }

and also here are the screen of console log when the error occures

console screenshot

Weird character showing up during automation of GitHub PR changelog

I’m using a workflow to automate the creation of a changelog when a PR is open.

Basically, I’m fetching all the commits in a PR and updating the PR body to look something like this:

Changelog:
- feat: new file
- feat: update to file

However, along the way something weird is happening and this is what I see in the output:
enter image description here

Not sure where this arrow is coming from.

For clarity, this is what the code looks like:

          const commitMessages = `${{ steps.fetch-commits.outputs.commit_messages }}`;
          let changelog = "## Changelog:n";
          
          commitMessages.split('|').forEach(line => {
            changelog += `${line}`;
          });
          
          const prNumber = context.payload.pull_request.number;
          const owner = context.repo.owner;
          const repo = context.repo.repo;
          
          await github.rest.pulls.update({
            owner: owner,
            repo: repo,
            pull_number: prNumber,
            body: changelog,
            headers: {
              authorization: `token ${process.env.PAT}`
            }
          });

Firebase Phone Authentication: FirebaseError: An internal AuthError has occurred (auth/internal-error)

use https://firebase.google.com/docs/auth/web/phone-auth
I’m trying to implement phone number authentication using Firebase in my web application. When I call the signInWithPhoneNumber method, I receive the following error:

FirebaseError: Firebase: An internal AuthError has occurred. (auth/internal-error)

I have correctly set up the reCAPTCHA verification, and I’ve added the authorized domains to my Firebase console. I am testing on localhost and also have the following domain added for my Chrome extension: chrome-extension://
Despite these steps, I continue to receive the internal error. Any help or suggestions on how to resolve this issue would be greatly appreciated!

Here is the relevant code snippet:

popup html and popup.js

document.addEventListener('DOMContentLoaded', function () {
  const siteList = document.getElementById('siteList');

  // Firebase config
  const firebaseConfig = {
    apiKey: "********",
    authDomain: "*******m",
    projectId: "*********-1",
    storageBucket: "******om",
    messagingSenderId: "************",
    appId: "1*************c*7",
    measurementId: "***************ZG"
  };

  // Firebase başlat
  firebase.initializeApp(firebaseConfig);
  const auth = firebase.auth();

  // Cashback fırsatları objesi
  const sites = {
    'tren.com': {
      name: 'tren',
      cashbackRate: '5%',
      logo: 'images/tren.png'
    },
    'burada.com': {
      name: 'burada',
      cashbackRate: '4%',
      logo: 'images/burada.png'
    }
  };

  // Cashback fırsatlarını listele
  function listCashbackSites() {
    siteList.innerHTML = '';
    for (const [domain, site] of Object.entries(sites)) {
      const siteElement = document.createElement('div');
      siteElement.className = 'site-item';
      siteElement.innerHTML = `
        <img src="${site.logo}" alt="${site.name}" class="site-logo">
        <div class="site-info">
          <div class="site-name">${site.name}</div>
          <div class="cashback-rate">${site.cashbackRate} Cashback</div>
        </div>
      `;
      siteList.appendChild(siteElement);
    }
  }

  // Giriş formunu göster
  function showLoginForm() {
    document.body.innerHTML = `
      <div class="login-container">
        <h3>Lütfen Telefon Numaranızı Girin</h3>
        <form id="phoneForm">
          <input type="tel" id="phoneNumber" placeholder="Telefon Numarası" required>
          <div id="recaptcha-container"></div>
          <button type="submit">Onay Kodunu Gönder</button>
        </form>
        <div id="verificationContainer" style="display: none;">
          <h3>Onay Kodunu Girin</h3>
          <form id="verificationForm">
            <input type="text" id="verificationCode" placeholder="Onay Kodu" required>
            <button type="submit">Doğrula</button>
          </form>
        </div>
    
      </div>
      <div class="register-container" style="display: none;">
        <h3>Kayıt Olun</h3>
        <form id="registerForm">
          <input type="tel" id="registerPhone" placeholder="Telefon Numarası" required>
          <input type="text" id="registerVerificationCode" placeholder="Onay Kodu" required>
          <button type="submit">Kayıt Ol</button>
        </form>
        <p>Zaten hesabınız var mı? <a href="#" id="showLogin">Giriş Yapın</a></p>
      </div>
    `;
  





  
    // Giriş formu işlemleri
    document.getElementById('phoneForm').addEventListener('submit', (e) => {
      e.preventDefault();


      let verify1 = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
        'size': 'normal', // 'compact' ya da 'invisible' da kullanabilirsiniz
        'callback': function(response) {
          alert("hata123: "+response)
          // ReCaptcha çözüldü
        }
      });
      verify1.render().then(function(widgetId) {
        window.recaptchaWidgetId = widgetId;
      });
      

      const phoneNumber = document.getElementById('phoneNumber').value;
      //setupRecaptcha();
    
     
      debugger;
      let verify = new firebase.auth.RecaptchaVerifier('recaptcha-container');
      auth.signInWithPhoneNumber(phoneNumber,verify1)
        .then((confirmationResult) => {
          // Onay kodu gönderildi
          debugger;
          window.confirmationResult = confirmationResult;
          document.getElementById('verificationContainer').style.display = 'block';
          console.log('Onay kodu gönderildi');
        })
        .catch((error) => {
          debugger;
          alert('Hata: ' + error.message);
        });
    });
    
    // Onay kodunu doğrula
    document.getElementById('verificationForm').addEventListener('submit', (e) => {
      e.preventDefault();
      const verificationCode = document.getElementById('verificationCode').value;
      debugger;
      window.confirmationResult.confirm(verificationCode)
        .then((result) => {
          // Kullanıcı başarıyla giriş yaptı
          const user = result.user;
          console.log('Giriş başarılı:', user);
        })
        .catch((error) => {
          console.error('Doğrulama hatası:', error);
          alert('Hata: ' + error.message);
        });
    
    });

    

    // Form geçişleri
    document.getElementById('showRegister')?.addEventListener('click', (e) => {
      e.preventDefault();
      document.querySelector('.login-container').style.display = 'none';
      document.querySelector('.register-container').style.display = 'block';
    });

    document.getElementById('showLogin')?.addEventListener('click', (e) => {
      e.preventDefault();
      document.querySelector('.register-container').style.display = 'none';
      document.querySelector('.login-container').style.display = 'block';
    });
  }

  // Kullanıcı oturum açma durumunu kontrol et
  firebase.auth().onAuthStateChanged(user => {
    if (user) {
      // Kullanıcı oturum açtıysa cashback fırsatlarını göster
      document.body.innerHTML = `
        <div class="main-container">
          <h2>Cashback Fırsatları</h2>
          <div id="siteList"></div>
          <button id="logoutButton">Çıkış Yap</button>
        </div>
      `;
      listCashbackSites();

      // Çıkış yapma işlemi
      document.getElementById('logoutButton').addEventListener('click', () => {
        auth.signOut().then(() => {
          console.log('Çıkış yapıldı');
          location.reload();
        });
      });
    } else {
      // Kullanıcı oturum açmadıysa login formunu göster
      showLoginForm();
    }
  });
});

// Background script
chrome.runtime.onInstalled.addListener(() => {
  console.log('Cashback extension installed');
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === 'TRACK_CASHBACK') {
    console.log('Cashback link clicked:', request.data);
  }
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Login  </title>

    <link
      type="text/css"
      rel="stylesheet"
      href="css/normalize.css"
    />
    <link
      type="text/css"
      rel="stylesheet"
      href="css/firebase-ui-auth.css"
    />

    <script src="js/firebase-app-compat.js"></script>
    <script src="js/firebase-auth-compat.js"></script>
    <script src="js/firebase-ui-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-auth.js"></script>


    <script src="js/firebase.js"></script>
    <script src="js/firebase-ui-setup.js"></script> 

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="robots" name="robots" content="noindex, nofollow" />
    <style>
      .login-container, .register-container {
  max-width: 300px;
  margin: 20px auto;
  padding: 20px;
}

input {
  width: 100%;
  padding: 8px;
  margin: 8px 0;
  border: 1px solid #ddd;
  border-radius: 4px;
}

button {
  width: 100%;
  padding: 10px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 10px;
}

button:hover {
  background-color: #45a049;
}

.main-container {
  padding: 20px;
}
    </style>
  </head>

  <body>
    <header>
      <div class="navigation-bar section">
        <a href="/" class="logo">
          <img
            src="/assets/img/lunar-industries-logo.png"
            alt="Lunar Industries"
          />
        </a>
      </div>
    </header>

    <div class="container">
      <div class="hero-banner"></div>
      <div class="login-explainer"></div>

      <div class="login-container">
        <h3>Lütfen Telefon Numaranızı Girin</h3>
        <form id="phoneForm">
          <input type="tel" id="phoneNumber" placeholder="5XX XXX XX XX" required />
          <div id="error-message" class="error-message"></div>
          
          <div id="recaptcha-container"></div>
          <button type="submit">Onay Kodunu Gönder</button>
      </form>
        <div id="verificationContainer" style="display: none;">
          <h3>Onay Kodunu Girin</h3>
          <form id="verificationForm">
            <input type="text" id="verificationCode" placeholder="Onay Kodu" required />
            <button type="submit">Doğrula</button>
          </form>
        </div>
       
      </div>
      

      <div id="firebaseui-auth-container"></div>
    </div>

    <script src="popup.js"></script>
  </body>
</html>

Flutter evaluateJavascript Not working for me

await webViewController.evaluateJavascript(source: """
    $(document).bind('DOMSubtreeModified', function () {
      if($("a[href^='/GenericUpload/GetFile']").length > 0){

    $("a[href^='/GenericUpload/GetFile']").off('click').on('click', function (){ window.flutter_inappwebview.callHandler('MobileAppDownload',$(this).attr('href')); });
      }}); """);

This code find a button in my app and execute my action. this code work once and after that not working. MUST uninstall app or clear data and cache in android to work again. this code work in IOS normally.

Decorator Function properties

I was trying to solve Task#1 from https://javascript.info/call-apply-decorators

It goes like this:

Create a decorator spy(func) that should return a wrapper that saves all calls to function in its calls property.

Every call is saved as an array of arguments.

For instance:

function work(a, b) {
  alert( a + b ); // 'work' is an arbitrary function or method
}

work = spy(work);

work(1, 2); // 3
work(4, 5); // 9

for (let args of work.calls) {
  alert( 'call:' + args.join() ); // "call:1,2", "call:4,5"
}

The solution is:

function spy(func) {

  function wrapper(...args) {
    wrapper.calls.push(args);
    return func.apply(this, args);
  }

  wrapper.calls = [];

  return wrapper;
}

What I do not understand in the given solution (and in Closures in general) is how the property that was assigned to the wrapper(wrapper.calls) is now also available for “work” function.

With the line work = spy(work) we created wrapper for the “work” function. The value of the latter now is

ƒ wrapper(...args) {
        wrapper.calls.push(args);
        return func.apply(this, args);
    }

Ok, the value of “work” is “wrapper” function. And “wrapper” has .calls property. But how this property is also present with “work”??

Regex pattern in RE2 for Apps Script

I’m trying to use the findText() function of the DocumentApp class to get the position of some text inside a Google Docs. To do so, I’m using a Regex (RE2) expression. I want it to find the following sequences: $$anyCharacterHere$$ ; basically anything enclosed in between $$ … $$. As there might be more than one of these sequences, we have to tell the regex to match any sequence of this type without any “$” inside.

I’ve tried using the following:

const re2 = new RegExp("\$\$.*(?!\$)\$\$");
let range = docBody.findText(re2)

but it says that this is an invalid expression. I tried changing the backslashes and that kind of things, but couldn’t get any further.

The goal I want to achieve is, for example, in this text:

Thi is a $$text$$ to show an $example$$ of how the $$regex$$ $$$hould$$ work.

the regex should find: $$text$$ and $$regex$$.