Extend window object and write d.ts files for tsc

we have a javascript project and want to move to typescript.
In the current setup, we have a global object (let’s call it myGlobal) with variables and functions that we are accessing either through windows.myGlobal.myFunction or directly as myGlobal.varA.

As expected, tsc would complain that windows.myGlobal does not exist on the window object.

My guess was to write a global.d.ts declaration file for tsc like this:

interface myGlobal {
  myFunction: any
}


// Extend the Window interface
interface Window {
  myGlobal: MyGlobal;
}

// Declare myGlobal as a global variable
declare let myGlobal: MyGlobal;

and even though I have

  "include": [
    "**/*.ts",
    "**/*.js"
  ],

in my tsconfig, it still complaints about the same error.
Can someone help?

Cannot read properties of undefined (reading ‘actives’) – multi sort

Good day guys. I am currently using ngx-mat-multi-sort. When I try to sort the error occurs which is Cannot read properties of undefined (reading ‘actives’)
at TableData.onSortEvent (table-data.ts:69:49).

Anyone has an idea what seems to be the problem ?. Is it timing issue ? that causes the sorting to fail ?. Thanks and highly appreciated.

#html code

  <table
                          mat-table
                          [dataSource]="closureListTable.dataSource"
                          matMultiSort
                          (matSortChange)="closureListTable.onSortEvent()"
                        >
                          <ng-container *ngFor="let column of displayedColumns" [matColumnDef]="column">
                       
                            <th mat-multi-sort-header="{{column}}" mat-header-cell *matHeaderCellDef>
                              {{ getColumnHeader(column) }}
                            </th>
                            <td mat-cell *matCellDef="let element">
                              <div class="td-value-name">
                                {{ getColumnValue(element, column) }}
                              </div>
                            </td>
                          </ng-container>

                          <tr
                            mat-header-row
                            *matHeaderRowDef="displayedColumns; sticky: true"
                          ></tr>
                          <tr
                            mat-row
                            *matRowDef="let row; columns: displayedColumns"
                          ></tr>
                        </table>

#ts code

 displayedColumns: string[] = [];

    closureListTable: TableData<IStoreClosureProgram>;
    this.closureListTable = new TableData<any>([]);

    ngOnInit(): void {
        // NOTE: do not initialze MatMultiSortTableDataSource here because `this.sort` may not be loaded yet
      }
    
      ngAfterViewInit(): void {
        this.selectedTab = this.selectedTab || 'FL';  // Set default tab if not already set
        this.displayedColumns = this.columnsMapping[this.selectedTab] || this.columnsMapping['FL'];
        setTimeout(() => {
          this.initTable();
          this.initSearch(); 
        }, 5000);
      }
      
      initSearch(): void {
        if (this.searchInput?.nativeElement) {
          fromEvent<any>(this.searchInput.nativeElement, 'keyup')
            .pipe(
              map((event) => event.target.value),
              startWith(''),
              debounceTime(500),
              distinctUntilChanged(),
              switchMap(async (search) => {
                this.closureListTable.pageIndex = 0;
                this.getData();
              })
            )
            .subscribe({ complete: noop });
        }
      }
    
      initTable() {
        this.closureListTable.dataSource = new MatMultiSortTableDataSource(
          this.sort,
          this.CLIENT_SIDE
        );
        this.closureListTable.nextObservable.subscribe(() => {
          this.getData();
        });
        this.closureListTable.sortObservable.subscribe(() => {
          console.log("1234")
          this.getData();
        });
        this.closureListTable.previousObservable.subscribe(() => {
          this.getData();
        });
        this.closureListTable.sizeObservable.subscribe(() => {
          this.getData();
        });
    
        if (
          (!!this.storeClosureAcessSettings?.ReceivingStoreList &&
            this.storeClosureAcessSettings?.ReceivingStoreList !== 'None') ||
          (!!this.storeClosureAcessSettings?.FullStoreClosureList &&
            this.storeClosureAcessSettings?.FullStoreClosureList !== 'None')
        ) {
          this.getData();
        }
    
        if (
          !!this.storeClosureAcessSettings?.AggregatesCounts &&
          this.storeClosureAcessSettings?.AggregatesCounts !== 'None'
        ) {
          // this.getAggregate();
        }
      }

Maps API loaded in NextJs component not directly available to instantiated class

I am using the Google Maps API and am working with NextJS and typescript.

I have to instantiate a class used for rendering purposes, but I am getting an error.

My code:

TLDR: loading Map, MapsLibraries and instantiating the Controller class.

Map.tsx

function GoogleMap() {
  const position = { lat: 53.0708031, lng: 12.7569469 };
  const mapId = "test";
  

  return (
    <div className="h-full w-full relative">
      <APIProvider
        apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY as string}
      >
        <Map
          defaultCenter={position}
          defaultZoom={16}
          mapId={mapId}
          fullscreenControl={false}
          tilt={0}
          tiltInteractionEnabled={false}
          rotateControl={false}
          mapTypeControl={false}
          mapTypeId="satellite"
        ></Map>
        <Directions />
      </APIProvider>
    </div>
  );
}

function Directions() {
  const map = useMap();
  const coreLibrary = useMapsLibrary("core");
  const markerLibrary = useMapsLibrary("marker");
  const routesLibrary = useMapsLibrary("routes");
  const mapsLibrary = useMapsLibrary("maps");
  const [directionsService, setDirectionsService] =
    useState<google.maps.DirectionsService>();
  const [directionsRenderer, setDirectionsRenderer] = useState<Directions>();

  useEffect(() => {
    if (
      !routesLibrary ||
      !map ||
      !coreLibrary ||
      !markerLibrary ||
      !mapsLibrary ||
      !window.google
    ) {
      return;
    }
    console.log("google", window.google);
    setDirectionsService(new routesLibrary.DirectionsService());

    // --- relevant initialization ----

    setDirectionsRenderer(new Controller(map));
  }, [
    coreLibrary,
    markerLibrary,
    mapsLibrary,
    routesLibrary,
    map,
  ]);

controller.ts (simplified):

 class Controller extends google.maps.OverlayView implements Directions {
         private map;
        constructor(map: google.maps.Map) {
             this.map = map
        }
 }

But this results in Error: “google is undefined”, even though google is defined at the time we are creating our component.

This can be fixed like this:

    // instead of new Controller() you call initialize() in <Directions/>
    function initialize(map: google.maps.Map){
    class Controller extends google.maps.OverlayView implements Directions {
         private map;
        constructor(map: google.maps.Map) {
             this.map = map
        }
     }
     return new Controller(map)
    }

I thought it could be an issue with nextjs seeing this as server side code and compiling it before it has ever seen the client (and no Maps API loaded) , so I marked controller.ts as “use client”, but the issue persists.


Why is that? How are we “loosing” the window.google object?

Is there another workaround for this?

What are the technical details?

Thank you for any help, I am really stuck here.

Why i cannot fetch my images from my .json file?

here is my html code:

<div id="card-container" class="card-container"></div>
    <!-- card and ftech data from.json -->
    <script>
     document.addEventListener('DOMContentLoaded', function() {
            fetch('data/data.json') // Adjust the path if needed
                .then(response => response.json())
                .then(jsonData => {
                    const container = document.getElementById('card-container');
                    jsonData.forEach(movie => {
                        const card = document.createElement('div');
                        card.className = 'card';
                        card.innerHTML = `
                            <img src="${movie.image}" class="card-img-top" alt="${movie.movie_Name}">
                            <div class="card-body">
                                <h5 class="card-title">${movie.movie_Name}</h5>
                                <p class="card-text">Release Date: ${movie.release_Date}<br>
                                Director: ${movie.director}<br>
                                Location: ${movie.location}<br>
                                Genre: ${movie.genre}</p>
                                <a href="#" class="btn btn-primary">More Details</a>
                            </div>
                        `;
                        container.appendChild(card);
                    });
                })
                .catch(error => console.error('Error fetching JSON:', error));
        });


    </script>

here is my .json file:

 {
        "id": 1,
        "movie_Name": "Mission: Cross",
        "release_Date": "August 9, 2024",
        "director": "Lee Myeong-hun",
        "location": "South Korea",
        "genre": "Action/Comedy",
        "image": "/img/MissionCross.jpg"
        
        
    },

I already try many way, but still it didn’t work. can someone help me?

here is the output I get:
enter image description here

TypeError: Cannot read properties of null (reading ‘getformula’)

function countColoredCells(countRange, colorRef) {

    var activeRange = SpreadsheetApp.getActiveRange();

    var activeSheet = SpreadsheetApp.getActiveSheet();

    var activeformula = activeRange.getformula();

    var countRangeAddress = activeformula.match(/((.*),/).pop().trim();

    var backGrounds = activeSheet.getRange(countRangeAddress).getBackgrounds();

    var colorRefAddress = activeformula.match(/,(.*))/).pop().trim();

    var BackGround = activeSheet.getRange(colorRefAddress).getBackground();

    var countColorCells = 0;

    for (var i = 0; i < backGrounds.length; i++)

    for (var k = 0; k < backGrounds[i].length; k++)

        if (backGrounds[i][k] == BackGround)

            countColorCells = countColorCells + 1;

    return countColorCells;

};

trying to count cells by color and this is the error
how to solve this problem pls

Webauthn on localhost failed to pass navigator.credentials.get()

I am making a sample-app to test a working flow of fido2-server.

I face a ploblem that after registration completed(with Mac touchId or Yubikey 5 series)
chrome sais there is no pass key that registered on the website.

Steps I already made were

  1. registration request (configure Fido2Lib) with user id and send back challenge
  2. receive registration options from server and exec navigator.credentials.create
  3. send credentials to server and save public key to user.
  4. send login request with user id and return fido2 assertionOptions that has allowCredentials.
  5. then try to navigator.credentials.get and have a pop up of “there is no key to this website”

I need to determine what is the cause of the problem.
First I use two way of registration that platform(touch id) and cross-platform(yubikey) for authenticatorAttachment.

when touch id, it sais there is no key to the website(localhost)
when yubikey, this security key is not registered to the website.

I think I made misstake of challenge or allowCredential to pass navigator.credentials.get

codes are too long so I put a part of important.

registerRequest

const fido2 = new Fido2Lib({
  timeout: 60000,
  rpId: "localhost",
  rpName: "localhost:3000",
  rpIcon: "https://example.com/icon.png",
  challengeSize: 32,
  attestation: "direct",
  authenticatorAttachment: "cross-platform",
  authenticatorRequireResidentKey: false,
  authenticatorUserVerification: "preferred"
});
  const user = users[userId] || {
    id: userId,
    name: username,
    displayName: username,
  };
  users[userId] = user;

  const options = await fido2.attestationOptions();
  options.user = user;
  options.challenge = base64url.encode(options.challenge);
  if (options.user && options.user.id) {
    options.user.id = base64url.encode(Buffer.from(options.user.id, 'utf-8'));
  }

registerResponse


    req.body.rawId = new Uint8Array(base64url.toBuffer(req.body.id)).buffer;

    const attestationResult = await fido2.attestationResult(req.body, {
      challenge: user.challenge,
      origin: "http://localhost:3000",
      factor: "either"
    });

    if (attestationResult.audit.complete) {
      const d = attestationResult.authnrData;

      user.authenticator = JSON.stringify({
        publickey : d.get('credentialPublicKeyPem'),
        counter : d.get('counter'),
        fmt: d.get('fmt'),
        credId : base64url.encode(d.get('credId')),
      });
      users[userId] = user;

loginResponse

    const user = users[userId];
    const authenticator = JSON.parse(user.authenticator);
    const assertionOptions = await fido2.assertionOptions();
    assertionOptions.challenge = base64url.encode(assertionOptions.challenge);
    let allowCredentials = [];
    allowCredentials.push({
      type: 'public-key',
      id: authenticator.credId,
      transports: ['usb', 'nfc', 'ble']
      // transports: ['internal']
    });
    assertionOptions.allowCredentials = allowCredentials;

client.js for register

            const options = response.data;

            options.challenge = Uint8Array.from(atob(options.challenge), (c) => c.charCodeAt(0));
            options.user.id = Uint8Array.from(atob(options.user.id), (c) => c.charCodeAt(0));

            const credential = await navigator.credentials.create({ publicKey: options }) as PublicKeyCredential;

            const res = publicKeyCredentialToJSON(credential.response);
// publicKeyCredentialToJSON is kind of base64encode
            if (credential) {
                const attestationResponse = {
                    id: credential.id,
                    rawId: Array.from(new Uint8Array(credential.rawId)),
                    type: credential.type,
                    response: res
                };

client.js for login


            const options = response.data;
            options.challenge = Uint8Array.from(base64url.decode(options.challenge)).buffer;
            options.allowCredentials = options.allowCredentials.map((c: any) => {
                c.id = Uint8Array.from(base64url.decode(c.id)).buffer;
                return c;
            });
            const assertion = await navigator.credentials.get({ publicKey: options }) as PublicKeyCredential;

The passkey of localhost is successfully saved on my Mac.

Let me know if there is any information missing to help me solve the problem.
Or tell me where I’m going wrong in this.

Getting error in installing the nextjs app using npx create-next-app@latest

npm WARN deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm WARN deprecated @humanwhocodes/[email protected]: Use @eslint/config-array instead
npm WARN deprecated [email protected]: Rimraf versions prior to v4 are no longer supported
npm WARN deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm WARN deprecated @humanwhocodes/[email protected]: Use @eslint/object-schema instead
npm ERR! code ECONNRESET
npm ERR! network aborted
npm ERR! network This is a problem related to network connectivity.
npm ERR! network In most cases you are behind a proxy or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network ‘proxy’ config is set properly. See: ‘npm help config’

npm ERR! A complete log of this run can be found in: C:Usersakshay shahiAppDataLocalnpm-cache_logs2024-08-15T05_24_41_129Z-debug-0.log

Aborting installation.
npm install has failed.

I have already installed the latest Nodejs.

Bootstrap not picking up in node.js express.js project

I have a project in which I have installed ejs and bootstrap but for some reason bootstrap is still not picking up. In my app.js file I have:

app.use(express.static("node_modulesbootstrapdistcss"));

Index.html file:

<html>
    <head>
        <link href="bootstrap.min.css" rel="stylesheet" />
        <style>
            .nav-separator {
                border-left: 1px solid rgba(0, 0, 0, 0.2); /* Light gray border */
                height: 30px; /* Adjust height as needed */
                margin: 0 15px; /* Spacing around the line */
            }
        </style>
        <title>Home</title>
    </head>
    <%- include('partials/navbar') %>
    <div class="container">
        <!-- Your page content goes here -->
        <h3>Welcome to Ando's Delivery Services!</h3>
    </div>
    </body>
</html>

I have made sure that the path in app.js is the relative path for bootstrap. I have also changed it so that the nav-bar is not in a seperate file and included in the index.html file and it still will not pick up the bootstrap.

The problem with the Yandex ID authorization settings

import React, { useEffect } from 'react';

  function LoginYaID({ onAuthSuccess }) {
    useEffect(() => {
      if (window.YaAuthSuggest) {
        window.YaAuthSuggest.init(
          {
            client_id: process.env.REACT_APP_YANDEX_CLIENT_ID,
            response_type: 'token',
            redirect_uri: 'https://support.hobbs-it.ru/redirect',
          },
          'https://support.hobbs-it.ru/',

        )
        .then(({ handler }) => handler())
        .then(data => {
          onAuthSuccess(data); // Передаем данные в App
        })
        .catch(error => {
          if (error.code !== 'in_progress') {
            console.error('Ошибка при инициализации Яндекс авторизации:', error);
          }
        });
      }
    }, [onAuthSuccess]);

    return (
      <div id="container"></div>
    );
  }

  export default LoginYaID;


import React, { useEffect } from "react";

const RedirectToken = () => {
  YaSendSuggestToken(
    'https://support.hobbs-it.ru/', 
    {
       flag: true
    }
 )
  useEffect(() => {
    const params = new URLSearchParams(window.location.hash.slice(1));
    const token = params.get('access_token');

  }, );

  return <div></div>;
};

export default RedirectToken;


import React, { useState, useEffect } from "react";
import Header from "./components/Header";
import ContactForm from "./components/ContactForm";
import ListAnnouncement from "./components/ListAnnouncement";
import MessengerWidget from "./components/MessengerWidget";
import LoginYaID from "./components/LoginYaID";
import RedirectToken from "./components/RedirectToken";
import FeedbackForm from "./components/FeedbackForm";
import Feedback from "./components/FeedBack";
import "./App.css";

function App() {
  const [isYandexAuth, setIsYandexAuth] = useState(false);
  const [isFeedbackOpen, setIsFeedbackOpen] = useState(false);

  useEffect(() => {
    // Проверка состояния авторизации при инициализации
    const token = localStorage.getItem("yandexToken");
    const isAuth = localStorage.getItem("isYandexAuth") === "true";

    if (token && isAuth) {
      // Если токен и состояние авторизации сохранены, используем их
      handleAuthSuccess({ access_token: token });
    }
  }, []);

  const handleAuthSuccess = (data) => {
    const token = data.access_token; // Получаем токен из данных
    if (token) {
      // Используем токен для запроса информации о пользователе
      fetch("https://login.yandex.ru/info?format=json", {
        method: "GET",
        headers: {
          Authorization: `OAuth ${token}`,
        },
      })
        .then((response) => response.json())
        .then((userInfo) => {
          const allowedDomains = ["kurganmk.ru", "reftp.ru", "hobbs-it.ru"];
          const userEmail = userInfo.default_email || "";

          if (typeof userEmail === "string" && userEmail.includes("@")) {
            const userDomain = userEmail.split("@")[1];
            if (allowedDomains.includes(userDomain)) {
              setIsYandexAuth(true);
              localStorage.setItem("isYandexAuth", "true");
              localStorage.setItem("yandexToken", token);
            } else {
              setIsYandexAuth(false);
              localStorage.removeItem("isYandexAuth");
              localStorage.removeItem("yandexToken");
              alert("Авторизация с этого домена недопустима.");
            }
          } else {
            alert("Не удалось получить данные пользователя для авторизации.");
          }
        })
        .catch((error) => {
          console.error(
            "Ошибка при получении информации о пользователе:",
            error
          );
        });
    }
  };

  const handleLogout = () => {
    setIsYandexAuth(false);
    localStorage.removeItem("isYandexAuth");
    localStorage.removeItem("yandexToken");
  };

  return (
    <div className="bg-gray-100 min-h-screen p-4">
      <div className="max-w-6xl mx-auto bg-white p-6 rounded-lg shadow-lg">
        <Header isYandexAuth={isYandexAuth} handleYandexLogout={handleLogout} />
        {isYandexAuth ? (
          <>
            <div className="md:flex">
            <ContactForm />
            <ListAnnouncement />
      
            </div>
            <Feedback />
          </>
        ) : (
          <>
            <LoginYaID onAuthSuccess={handleAuthSuccess} />
            <RedirectToken onAuthSuccess={handleAuthSuccess} />
          </>
        )}
      </div>
    </div>
  );
}

export default App;

There are three components, they work in conjunction for authorization by Yandex ID, the authorization status is saved in localStorage so as not to ask to do it several times. Libraries are connected in public/index.html

Now the behavior is unclear to me. Catching the error object object

As expected, the authorization was successful, the data was saved in localstorage and there were no errors.`

enter image description here

Get the each cell data from Table cell and Append to UL

I have a table with multiple rows (dynamically) with cell data.

I want to get the each row data and append to as list item

jsFiddle

Table

enter image description here

Desired Output:

  • Raj

    • English: 86
    • Maths: 73
    • Science: 68
  • Mukund

    • English: 45
    • Maths: 39
    • Science: 64
$('table tr').each( (tr_idx,tr) => {
  $(tr).children('td').each( (td_idx, td) => {
    $('#results ul').html('<li>' + $(td).text() + '</li>');
  });                 
});
body{font-family:Verdana;font-size:16px}
table, td{
    border:1px solid #000000;
    border-collapse:collapse;
}
td{
    padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<table class="table-1">
<tr>
  <td>Name</td>
  <td>English</td>
  <td>Maths</td>
  <td>Science</td>
</tr>
<tr>
  <td>Raj</td>
  <td>86</td>
  <td>73</td>
  <td>68</td>
</tr>
<tr>
  <td>Mukund</td>
  <td>45</td>
  <td>39</td>
  <td>64</td>
</tr>
<tr>
  <td>Bhairav</td>
  <td>85</td>
  <td>93</td>
  <td>79</td>
</tr>
<tr>
  <td>Bhairav</td>
  <td>85</td>
  <td>93</td>
  <td>79</td>
</tr>
</table>

<div id="results">
<h3>
Information
</h3>
<ul>

</ul>
</div>

Understanding how requestanimationFrame works

I am trying to understand the requestAnimationFrame api but I am not able to understand how repaint occurs during the process.

I am working on a programming exercise to create a stopwatch.

function startTimer() {
  timerId = requestAnimationFrame(updateTimer);
}

function updateTimer() {
   timer.textContent = updateTime();
   timerId = requestAnimationFrame(updateTimer);
}

I understand that the requestAnimationFrame tells the browser to call the callback function updateTimer before the next browser repaint. But, I am assuming that the calculations inside the callback function updateTimer are causing the repaint in the first place.

So, I am struggling to understand how the repaint is triggered in this scenario? Does requestAnimationFrame triggers the repaint?

Converting crypto to crypto-js script (AES-256-CBC)

I have working encrypt()/decrypt() functions using Node.js native crypto lib and they works fine

const crypto = require('crypto');


const ENC_KEY = "bf37b0999c7717c17b2c1d90470cb41e";

function encrypt(data, key, ivLength = 16) {
  const iv = crypto.randomBytes(ivLength);
  const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  let encrypted = cipher.update(data, 'utf8', 'base64');
  encrypted += cipher.final('base64');

  const resultData = {
    'iv': iv.toString('base64'),
    'data': encrypted
  }
  const response = btoa(JSON.stringify(resultData))
  return response;
};

function decrypt(data, key) {
  const jsonData = JSON.parse(Buffer.from(data, 'base64').toString('ascii'));
  const iv = Buffer.from(jsonData['iv'], 'base64')
  
  let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  let decrypted = decipher.update(jsonData['data'], 'base64', 'utf8');
  const result = (decrypted + decipher.final('utf8'));
  return result;
};


const phrase = "ORIGINAL PHRASE";

const encryptedCrypto = encrypt(phrase, ENC_KEY);
const decryptedCrypto = decrypt(encryptedCrypto, ENC_KEY);
console.log(`Decoded string: ${decryptedCrypto}`);

I need to rewrite encrypt() function with crypto-js lib so the original decrypt() function work with it. I can’t modify anything except encrypt() function.

I’ve tried but I didn’t succeed. Could someone help me with that?

function encrypt(data, key, ivLength = 16) {
  const iv = CryptoJS.lib.WordArray.random(ivLength);
  const encrypted = CryptoJS.AES.encrypt(data, CryptoJS.enc.Hex.parse(key), {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.NoPadding
  });

  const resultData = {
    'iv': CryptoJS.enc.Hex.parse(iv).toString('base64'),
    'data': encrypted
  }
  const result = btoa(JSON.stringify(resultData))
  return result;
}

Want to move like Rubik’s Cube

`function rotateFace(axis, layer, direction) {
if (isAnimating) return;
isAnimating = true;

const angle = Math.PI / 2 * direction;  // 90도 회전
const rotationMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(...axis), angle);

// 회전할 블록들을 그룹화
const cubesToRotate = cubes.filter(cube => {
    return Math.round(cube.userData.originalPosition[layer]) === Math.round(selectedFace.object.position[layer]);
});

// 그룹 생성
const group = new THREE.Group();
cubesToRotate.forEach(cube => group.add(cube));
scene.add(group);

const animationDuration = 300; // milliseconds
const startTime = performance.now();

function animate(time) {
    const elapsedTime = time - startTime;
    const progress = Math.min(elapsedTime / animationDuration, 1);
    const currentAngle = angle * progress;

    // 그룹 회전
    group.rotation[axis[0] === 1 ? 'x' : axis[1] === 1 ? 'y' : 'z'] = currentAngle;

    if (progress < 1) {
        requestAnimationFrame(animate);
    } else {
        // 최종 회전 및 위치 조정
        group.rotation[axis[0] === 1 ? 'x' : axis[1] === 1 ? 'y' : 'z'] = angle;
        group.updateMatrixWorld(true);

        // 그룹에서 큐브를 씬으로 옮기기 전, 각 큐브의 최종 회전 및 위치 설정
        while (group.children.length > 0) {
            const cube = group.children[0];
            
            // 그룹의 변환을 적용하여 최종 위치와 회전을 큐브에 설정
            cube.applyMatrix4(group.matrixWorld);
            
            // 큐브의 회전 행렬을 정렬하여 회전이 180도 되는 문제 방지
            cube.rotation.setFromRotationMatrix(cube.matrix);

            // 원래 위치 정보 업데이트
            cube.userData.originalPosition.copy(cube.position);

            // 그룹에서 큐브를 씬으로 다시 옮김
            scene.attach(cube);
        }

        scene.remove(group); // 빈 그룹을 씬에서 제거
        isAnimating = false;
    }

    renderer.render(scene, camera);
}

requestAnimationFrame(animate);

}`

The rotation is 90 degrees, but the color is changing as if it were 180 degrees, and the block disappears when it is rotated

Want to move like Rubik’s Cube

Troubleshooting Checks and Changing Multiple Values

how to fix google sheet webapp returning undefined value in html page?

I am making a web app using google sheets and google app script. My web app have an index page and an input page. There is no problem with inputting the data, however when i try to populate the data from google sheet it displayed undefined value for cells that are empty in the google sheet.

My script is quite long so i attached it here:
https://drive.google.com/drive/folders/1Zl-8UsntRUTlJcnaBuYIgXP2PvD4mIyN?usp=sharing

What i want is, when i click the edit button the form will be populated with the right data and if the data in google sheet is empty then it will just display the placeholder.
Thank you in advance.

Screenshot of undefined