How to create Jquery or Vanilla JS advanced range slider based pricing calculator?

I’ve been trying to create an advanced pricing calculator showcase for my client’s website.

Here is what i actually need;

I’will have 3 different price and value changer;

  1. Employee
  2. Office Number
  3. Benefits

As you can see on the image, those 3 price/value changers will do change 4 different values on the right depends on the left side value chances.

When the values higher up or selected any benefits; The “with Ozo” background will be filled like %3 but the “without Ozo” background will be filled like 13% (I’m using :before element for the background filling). Also the prices of both blocks will be increase but the “without Ozo” will be increased %15 more than the “with Ozo”. On the other hand when the people number every 15 will add 1 point “HR/Finance employees involved” and 1.5 hour to “hours spent per month” sections.

For the “What benefits do you offer?” tags each will increase only the “without Ozo” part by adding %10 more to the current price.

I know i designed something crazy to complete for my design but now i’m unable to get calculated.

Advanced Price Calculator

Here is what i did so far;

const costRanges = document.querySelectorAll('.cost_item input');

function handleUpdate() {
    const suffix = this.dataset.sizing || '' ;
    var rangeType = this.value+suffix;
    document.documentElement.style.setProperty(`--${this.name}` , rangeType );
}

costRanges.forEach(input => {
    input.addEventListener('change' , handleUpdate);
});
costRanges.forEach(input => {
    input.addEventListener('mousemove' , handleUpdate);
});


(function() {
    const rangeEmployee = document.querySelector('input[type="range"]#employee');
    const rangeCountry = document.querySelector('input[type="range"]#country');

    const rangeEmployeeValue = function(){
        const newValue = rangeEmployee.value;
        const target = document.querySelector('.wOzoValue');
        target.innerHTML = newValue;
    }

    const rangeCountryValue = function(){
        const newValue = rangeCountry.value;
        const target = document.querySelector('.woOzoValue');
        target.innerHTML = newValue;
    }

    rangeEmployee.addEventListener("input", rangeEmployeeValue);
    rangeCountry.addEventListener("input", rangeCountryValue);

})();

Jsonata how to avoid auto change in order javascript

I have array of objects as sample below.
After JSONata evaluation, the order of array of object changes. string with numeric id appears before i.e id 5 appears before id 4a.

How to avoid this change in order

var data = [
    {
        "id": 1,
        "body": "First Item",
        "severity": 1,
        "status": 0
    },
    {
        "id": 2,
        "body": "Second Item",
        "severity": 2,
        "status": 1
    },
{
        "id": 3,
        "body": "third Item",
        "severity": 4,
        "status": 2
    }, 
{
        "id": 4a,
        "body": "four Item",
        "severity": 2,
        "status": 1
    },
{
        "id": 5,
        "body": "five Item",
        "severity": 2,
        "status": 3
    }
]

var result = JSONata(query).evaluate(data)

How to use ast to parse javascript code and find out the execution conditions of specified methods

Issue with Parent Condition Context in Else Block During AST Traversal

I’m working on a JavaScript code analysis tool that traverses the Abstract Syntax Tree (AST) to analyze function calls and their conditions. The goal is to dynamically generate a condition chain for each function call based on nested if, else if, and else statements.

Problem Description

When processing an else block, I’m unable to correctly access the parent condition (parent.condition) to generate the negation of the preceding if condition. This results in incorrect condition chains for function calls inside the else block.

Here’s a simplified version of the code I’m analyzing:

javascript

复制

function foo(a, b) {
    var c = 100;
    if (c > a) {
        if (a > 200) {
            alert(1);
        } else if (a > 100) {
            alert(2);
        } else {
            alert(3); // Condition here is incorrect
        }
    }
}

Expected Output

For the else block (alert(3)), the condition should be:

复制

100 > a 并且 非(a > 200) 且 非(a > 100)

However, the current implementation generates incorrect conditions because the parent condition context is not properly maintained.

Current Implementation

Here’s the relevant part of my code:

javascript

复制

if (
    node.type === 'BlockStatement' &&
    parent.type === 'IfStatement' &&
    parent.alternate === node
) {
    // Attempt to get the parent condition
    const parentCondition = currentConditionContext.parent.condition; // ❌ Fails here
    currentConditionContext = {
        type: 'else',
        condition: `非(${parentCondition})`,
        parent: currentConditionContext.parent.parent, // Incorrect parent reference
        siblingNegation: null
    };
}

Issue

The problem occurs because:

  1. The else block incorrectly skips a level when referencing the parent context (parent.parent).

  2. The parent condition is not accessible due to the incorrect parent reference.

Question

How can I properly maintain the parent condition context for else blocks to generate the correct negation of the preceding if condition?

Desired Solution

I need a way to:

  1. Correctly reference the parent condition context for else blocks.

  2. Generate the negation of the preceding if condition dynamically.

  3. Ensure the condition chain is accurate for nested if-else structures.

Additional Context

  • I’m using esprima for parsing, estraverse for traversal, and escodegen for code generation.

  • The condition chain should support arbitrary levels of nesting.

Here’s the full code for reference:

javascript

复制

// Full code implementation

Any guidance or suggestions would be greatly appreciated!


This question is clear, concise, and provides all the necessary context for someone to understand and help with your issue. You can replace the // Full code implementation comment with your actual code if needed.

this is my code, The following code does not achieve the desired effect

import esprima from 'esprima';
import estraverse from 'estraverse';
import escodegen from 'escodegen';

export function analyzeFunctionCalls(code, functionName, methods) {
    try {
        const ast = esprima.parseScript(code, { range: true, tokens: true, comment: true });
        const functionCalls = [];
        const variableMap = {};
        
        // 树形条件链管理(使用根节点初始化)
        let currentConditionNode = { 
            condition: null, 
            parent: null, 
            children: [] 
        };

        estraverse.traverse(ast, {
            enter: (node, parent) => {
                // 变量声明处理
                if (node.type === 'VariableDeclaration') {
                    node.declarations.forEach(declaration => {
                        if (declaration.init) {
                            variableMap[declaration.id.name] = escodegen.generate(declaration.init);
                        }
                    });
                }

                // 处理 if 语句
                if (node.type === 'IfStatement') {
                    const condition = processCondition(node.test, variableMap);
                    
                    // 创建新的条件节点并链接到当前节点
                    const newNode = {
                        condition: condition,
                        parent: currentConditionNode,
                        children: []
                    };
                    
                    currentConditionNode.children.push(newNode);
                    currentConditionNode = newNode;
                }

                // 处理 else if
                if (
                    node.type === 'IfStatement' &&
                    parent.type === 'IfStatement' &&
                    parent.alternate === node
                ) {
                    // 获取父 if 的条件
                    const parentIfCondition = currentConditionNode.parent.condition;
                    const currentCondition = processCondition(node.test, variableMap);
                    
                    // 创建 else if 节点
                    const newNode = {
                        condition: `非(${parentIfCondition}) 且 ${currentCondition}`,
                        parent: currentConditionNode.parent, // 关键修正:父节点指向原 if 的父级
                        children: []
                    };
                    
                    currentConditionNode.parent.children.push(newNode);
                    currentConditionNode = newNode;
                }

                // 处理 else 块
                if (
                    node.type === 'BlockStatement' &&
                    parent.type === 'IfStatement' &&
                    parent.alternate === node
                ) {
                    // 获取父 if 的条件
                    const parentIfCondition = currentConditionNode.parent.condition;
                    
                    // 创建 else 节点
                    const newNode = {
                        condition: `非(${parentIfCondition})`,
                        parent: currentConditionNode.parent, // 关键修正:父节点指向原 if 的父级
                        children: []
                    };
                    
                    currentConditionNode.parent.children.push(newNode);
                    currentConditionNode = newNode;
                }

                // 收集函数调用
                if (node.type === 'CallExpression') {
                    const callee = node.callee;
                    const fullCondition = getFullConditionChain(currentConditionNode);
                    
                    if (
                        (callee.type === 'Identifier' && methods.includes(callee.name)) ||
                        (callee.type === 'MemberExpression' &&
                         callee.object.name === functionName &&
                         methods.includes(callee.property.name))
                    ) {
                        functionCalls.push({
                            call: escodegen.generate(node),
                            condition: fullCondition
                        });
                    }
                }
            },
            leave: (node) => {
                // 离开 if 语句时回溯到父节点
                if (node.type === 'IfStatement') {
                    currentConditionNode = currentConditionNode.parent;
                }
            }
        });

        return functionCalls;
    } catch (error) {
        console.error('Error parsing code:', error);
        return [];
    }
}

// 生成完整条件链(从根到当前节点)
function getFullConditionChain(node) {
    const path = [];
    let current = node;
    while (current && current.condition !== null) {
        path.unshift(current.condition);
        current = current.parent;
    }
    return path.join(' 并且 ');
}

// 优化后的条件处理
function processCondition(testNode, variableMap) {
    return escodegen.generate(testNode)
        .replace(/bw+b/g, m => variableMap[m] || m)
        .replace(/>=/g, '≥')
        .replace(/<=/g, '≤')
        .replace(/===/g, '等于')
        .replace(/==/g, '等于')
        .replace(/!=/g, '≠')
        .replace(/>/g, '>')
        .replace(/</g, '<')
        .replace(/||/g, ' 或 ')
        .replace(/&&/g, ' 且 ')
        .replace(/!/g, '非');
}
console.log(analyzeFunctionCalls(`


function foo(a, b) {
    var c = 100
    if(c > a){
        if( a > 200){
            alert(1)
            alert(4)
        }else if(a > 100){
            alert(2)
            if(b > 200){
                alert(5)
            }else{
                alert(6)
            }
            alert(7)
        }else{
            alert(3)

            if(b > 200){
                alert(8)
            }else{
                alert(9)
            }
            alert(10)
        }    
    }
}




`, "DV",["alert"]));

expected result

[
{ call: 'alert(1)', condition: '100>a 并且 a>200' },
{ call: 'alert(4)', condition: '100>a 并且 a>200' },
{ call: 'alert(2)', condition: '100>a 并且 非(a>200) 且 a>100' },
{ call: 'alert(5)', condition: '100>a 并且 非(a>200) 且 a>100 并且 b>200' },
{ call: 'alert(6)', condition: '100>a 并且 非(a>200) 且 a>100 并且 非(b>200)' },
{ call: 'alert(7)', condition: '100>a 并且 非(a>200) 且 a>100' },
{ call: 'alert(3)', condition: '100>a 并且 非(a>200) 且 非(a>100)' }, // ✅ 正确
{ call: 'alert(8)', condition: '100>a 并且 非(a>200) 且 非(a>100) 并且 b>200' }, // ✅
{ call: 'alert(9)', condition: '100>a 并且 非(a>200) 且 非(a>100) 并且 非(b>200)' }, // ✅
{ call: 'alert(10)', condition: '100>a 并且 非(a>200) 且 非(a>100)' } // ✅
]

actual result

[
  { call: 'alert(1)', condition: '100 > a 并且 a > 200' },
  { call: 'alert(4)', condition: '100 > a 并且 a > 200' },
  {
    call: 'alert(2)',
    condition: '100 > a 并且 a > 200 并且 非(a > 200) 且 a > 100'
  },
  {
    call: 'alert(5)',
    condition: '100 > a 并且 a > 200 并且 非(a > 200) 且 a > 100 并且 b > 200'
  },
  {
    call: 'alert(6)',
    condition: '100 > a 并且 a > 200 并且 非(a > 200) 且 a > 100 并且 非(非(a > 200) 且 a > 100)'
  },
  {
    call: 'alert(7)',
    condition: '100 > a 并且 a > 200 并且 非(a > 200) 且 a > 100'
  },
  { call: 'alert(3)', condition: '100 > a 并且 a > 200 并且 非(a > 200)' },
  {
    call: 'alert(8)',
    condition: '100 > a 并且 a > 200 并且 非(a > 200) 并且 b > 200'
  },
  {
    call: 'alert(9)',
    condition: '100 > a 并且 a > 200 并且 非(a > 200) 并且 非(非(a > 200))'
  },
  { call: 'alert(10)', condition: '100 > a 并且 a > 200 并且 非(a > 200)' }
]

Is there a god to help me

thank you

my 3 hourly forecast data is not reflecting on my hd have shown all the able forecast

I am on my first JavaScript project and I am building a weather app.

I was able to extract the 3 hourly forecast Data but it is only showing the first data. Like only 21:00 data in my html but the rest is not reflecting. It should have shown all the able forecast data.

While writing my html I wrote 6 elements that are responsible for holding the forecast data but I actually needed 8. So I created the forecast item as a way of creating the remaining html. I am meant to add it by the forecast container.innerHTML += forecast item. I didn’t do that yet because only 1 of the 6 I already had showed the forecast data. What I want is for the 3 hourly forecast data to automatically reflect in the html for every 24 hours

async function getForecast() {
  try {

    // const citySearch = document.getElementById('search-city').value;
    const apiKey = 'APIKEY';
    const forecastFullUrl = `https://api.openweathermap.org/data/2.5/forecast?q=Dudley&appid=${apiKey}&units=metric`;

    console.log('fetching forecast URL:', forecastFullUrl)
    const forecastResponse = await fetch(forecastFullUrl)
    const forecastData = await forecastResponse.json()
    console.log(forecastData);

    displayCurrentForecastData(forecastData.list);
  } catch (error) {
    console.error('error fetching hourly forecast data:', error);
    alert('Failed to fetch hourly forecast data');
  }
}
getForecast();

function displayCurrentForecastData(forecastList) {


  // compare today date with the date in the dorecast data and return only the ones that matches today's date

  const currentDate = new Date();
  const currentDateStringToShowOnMainPage = currentDate.toISOString().split('T')[0];
  // to make the date on the main page be the current date
  document.getElementById('current-date').textContent = currentDateStringToShowOnMainPage;

  //  to only shown current day's weather forecast
  const currentDayForecast = forecastList.filter((forecast) => {

    const time = new Date(forecast.dt * 1000)
    const forecastDateString = time.toISOString().split('T')[0];
    return forecastDateString === currentDateStringToShowOnMainPage;
  })


  // this to make  more html children since i only created 6 spaces in th e html for the 3 hours weather details
  const forecastContainer = document.getElementById('selected-hour-details')
  // forecastContainer.innerHTML = '';

  currentDayForecast.forEach((forecast, index) => {

    const forecastItem = document.createElement('div');
    forecastItem.className = 'selected-hour-details';
    const time = new Date(forecast.dt * 1000)
    const timeString = time.toLocaleTimeString([], {
      hour: "2-digit",
      minute: "2-digit"
    });

    const temp = forecast.main.temp;
    const description = forecast.weather[0].description;
    const icon = `http://openweathermap.org/img/wn/${forecast.weather[0].icon}`;

    // to update the 3 hourly html with the forecast details

    document.querySelector('.forecasttime').textContent = timeString;
    document.querySelector('.forecasttemp').textContent = `${temp}°C: ${description}`;
    document.querySelector('.weather-icon').src = icon;

    // /create more spaces

    forecastItem.innerHTML = `
              <span class="forecasttime">${timeString}</span>
              <img class="weather-icon" src="${icon}" alt="weather icon"/>
              <span class="forecasttemp">${temp}°C: ${description}</span>
            `
    forecastContainer.innerHTML += forecastItem;
  })
}
<div class="selected-hour-weather-details" id="selected-hour-weather-details">

  <div class="specific-hours">
    <span class="forecasttime">00:00</span>
    <img class="weather-icon" src="" alt="weather icon"/>
    <span class="forecasttemp">4°</span>
  </div>

  <div class="specific-hours">
    <span class="forecasttime">01:00</span>
    <img class="weather-icon" src="" alt="weather icon"/>
    <span class="forecasttemp">5•</span>
  </div>

  <div class="specific-hours">
    <span class="forecasttime">02:00</span>
    <img class="weather-icon" src="" alt="weather icon"/>
    <span class="forecasttemp">5•</span>
  </div>

  <div class="specific-hours">
    <span class="forecasttime">03:00</span>
    <img class="weather-icon" src="" alt="weather icon"/>
    <span class="forecasttemp">5•</span>
  </div>

  <div class="specific-hours">
    <span class="forecasttime">04:00</span>
    <img class="weather-icon" src="" alt="weather icon"/>
    <span class="forecasttemp">5•</span>
  </div>

  <div class="specific-hours">
    <span class="forecasttime">05:00</span>
    <img class="weather-icon" src="" alt="weather icon"/>
    <span class="forecasttemp">5•</span>
  </div>
</div>

weird behaviour in useeffect setInterval [duplicate]

In the strict mode of react it is running this function “fetchLiveChatMsg(liveChatId);” twice in setInterval, but when the unmounting happen it still keeps running this function even the interval is clear but in the non-strict mode of react everything is working fine

NOTE: I’ve already tried the useRef in intervalId, pageToken, it is still working the same

import { useEffect, useRef } from "react";
import LiveChatMsg from "./LiveChatMsg";
import { useDispatch, useSelector } from "react-redux";
import { addLiveMsg } from "../utils/LiveChatSlice";

const LiveChat = ({videoId}) => {
    const dispatch = useDispatch();
    let intervalId;
    let pageToken;

    const liveMessages = useSelector((store) => store.liveChat.messages);

    const fetchLiveChatMsg = async (liveChatId) => {
        const response = await fetch(`https://www.googleapis.com/youtube/v3/liveChat/messages?pageToken=${pageToken ? pageToken : ""}&liveChatId=${liveChatId}&part=snippet,authorDetails&key=${import.meta.env.VITE_GOOGLE_API_KEY}&maxResults=1`);

        const data = (await response.json());

        pageToken = data.nextPageToken;
        console.log(pageToken);
        console.log(data);
        dispatch(addLiveMsg(data?.items[0]));
    }

    const fetchLiveId = async () => {
        const response = await fetch(`https://www.googleapis.com/youtube/v3/videos?part=liveStreamingDetails&id=${videoId}&key=${import.meta.env.VITE_GOOGLE_API_KEY}`);
        
        const liveChatId = (await response.json())?.items[0]?.liveStreamingDetails?.activeLiveChatId;
        
        if(liveChatId) {
            console.log(liveChatId);
            intervalId = setInterval(() => {
                fetchLiveChatMsg(liveChatId);
            }, 3000);
        }
    }

    useEffect(() => {
        fetchLiveId();

        return () => {
            console.log(intervalId);
            clearInterval(intervalId);
        }
    }, []);

    return (
        liveMessages && <div className="w-2/5 flex border-[1.5px] rounded-xl p-[8px] h-[450px] flex-col-reverse gap-[10px] overflow-y-scroll bg-gray-50">
            {
                liveMessages.map((el, idx) => <LiveChatMsg key={idx} profile={el?.authorDetails?.profileImageUrl} author={el?.authorDetails?.displayName} msg={el?.snippet?.displayMessage} />)
            }
        </div>
    );
};

export default LiveChat;

I want while unmounting the “fetchLiveChatMsg(liveChatId);” function doesn’t run because the timer id of the setinterval is cleared

Speech-to-text In React Native

So i trying to build the speech to text application in react-native for android i am using react-native-audio-recorder-player libary for recording and for storage RNFS, the audio is saving in the location but the voice is not coming, the code is given below


import RNFS from 'react-native-fs';
import AudioRecorderPlayer, {
    AVEncoderAudioQualityIOSType,
    AVEncodingOption,
    AVModeIOSOption,
    AudioEncoderAndroidType,
    AudioSourceAndroidType,
    OutputFormatAndroidType,
} from 'react-native-audio-recorder-player';
import { useState } from 'react';
import { Platform } from 'react-native';

const audioRecorderPlayer = new AudioRecorderPlayer();
audioRecorderPlayer.setSubscriptionDuration(0.1);
const useRecording = () => {
    const [recording, setRecording] = useState(false);
    const [recordingPath, setRecordingPath] = useState('');
    const audioSet = {
        AudioEncoderAndroid: AudioEncoderAndroidType.AAC,
        AudioSourceAndroid: AudioSourceAndroidType.MIC,
        AVModeIOS: AVModeIOSOption.measurement,
        AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high,
        AVNumberOfChannelsKeyIOS: 2,
        AVFormatIDKeyIOS: AVEncodingOption.aac,
        OutputFormatAndroid: OutputFormatAndroidType.AAC_ADTS,
    };
    const startRecording = async () => {
        const dirs =
            Platform.OS === 'ios'
                ? RNFS.DocumentDirectoryPath
                : RNFS.ExternalDirectoryPath;
        console.log('dir', dirs);
        const path = Platform.select({
            ios: `${dirs}hello.m4a`,
            android: `${dirs}/hello.wav`,
        }); // File name for your recording
        console.log('Recorded File Path:', path);
        const result = await audioRecorderPlayer.startRecorder(
            path,
            audioSet,
            true,
        );
        console.log('result', result);
        setRecording(true);
        setRecordingPath(result);
    };
    const stopRecording = async () => {
        const result = await audioRecorderPlayer.stopRecorder();
        setRecording(false);
    };
    return {
        startRecording,
        stopRecording,
        setRecording,
        recording,
        recordingPath,
    }
};
export default useRecording;

i have tried the audio is not coming i am expecting to listen the audio

React custom debounce hook clearInverval not working

I have the following custom hook:

import { useEffect, useRef } from 'react';

export const useDebounce = (cb: Function, timeout: number) => {
  const timeoutRef = useRef(null);

  useEffect(() => {
    return () => {
      clearTimeout(timeoutRef.current);
    };
  }, []);

  return (...args) => {
    if (timeoutRef.current) {
      console.log('clearing timeout', timeoutRef.current)
      clearTimeout(timeout);
      timeoutRef.current = null;
    }

    timeoutRef.current = setTimeout(() => {
      console.log('callback invoked')
      cb(...args);
    }, timeout);

    console.log('latest', timeoutRef.current)
  };
};

and below component:

const CountriesList = () => {

  const [inputValue, setIinputValue] = useState('');
  const [countries, setCountries] = useState([]);


  const apiCall = useCallback(async (input: string) => {
    const response =  await fetch(`https://.../practice/countries/${input}`);
  },[]);

  const debouncedApiCall = useDebounce(apiCall, 5000);

  useEffect(() => {
    inputValue && debouncedApiCall(inputValue);
  }, [inputValue]);

  const handleInput= (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setIinputValue(value);
  }

  return (
    <div>
      <input type="text" onChange={(e)=> {handleInput(e)}} />
    </div>
  );
};

I was expecting the line

if (timeoutRef.current) {
      console.log('clearing timeout', timeoutRef.current)
      clearTimeout(timeout);
      timeoutRef.current = null;
}

to clear existing timeouts before starting a new one. But when I run this code, after the iterval, I get N number of API calls, for example if I typed 6 characters I get 6 API calls.

Why isn’t the above line clearTimeout(timeout); clearing existing timeouts? I was only expecting the last one to fire.

Restoring checkbox state after Javascript runs and returns an error

I have a simple table with a checkbox in one table cell – when the user clicks the checkbox it calls a script which calls a php file to make some database updates. This is all working well.

If there’s an error with the php script or it returns an error I would like to restore the checkbox to its original checked state before the user clicked on it. I thought the following would do this but it’s making any change:

if (checkedState === 'true') {
  $this.attr('checked', false);
} else { 
  $this.attr('checked', true);
}

I’ve attached the full script which works except for the above part where it attempts to restore the checkbox to it’s original checked state.

$(document).ready(function() {
  $("input.select-item").click(function() {
    //console.log( 'starting Checklist Item Update' );
    var recid = $(this).closest('td').attr('id');
    var checkedState = $(this).is(":checked");
    // Create a reference to $(this) here:
    $this = $(this);
    $.post('updateItem.php', {
      type: 'updateItem',
      recid: recid,
      selectionType: checkedState
    }, function(data) {
      data = JSON.parse(data);
      //console.log( data );
      if (data.error) {
        var ajaxError = (data.text);
        var errorAlert = 'There was an error updating the Completed Checklist Item - ' + ajaxError + '. Please contact the Developer';
        $this.closest('td').addClass("table-danger");

        // Restore checkbox to it's original state
        if (checkedState === 'true') {
          $this.attr('checked', false);
        } else {
          $this.attr('checked', true);
        }
        //display AJAX error details
        $("#updateSelectionsErrorMessage").html(errorAlert);
        $("#updateSelectionsError").show();
        return; // stop executing this function any further
      } else {
        $this.closest('td').addClass("table-success")
        $this.closest('td').removeClass("table-danger");

        // Restore checkbox to it's original state
        if (checkedState == true) {
          $this.attr('checked', false);
        } else {
          $this.attr('checked', true);
        }
      }
    }).fail(function(xhr) {
      var httpStatus = (xhr.status);
      var ajaxError = 'There was an error updating the Completed Checklist Item - AJAX request error. HTTP Status: ' + httpStatus + '. Please contact the Developer';
      $this.closest('td').addClass("table-danger");
      //display AJAX error details
      $("#updateSelectionsErrorMessage").html(ajaxError);
      $("#updateSelectionsError").show();
      $this.attr('checked', false); // Unchecks it
    });
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

<table class="table table-striped table-hover table-bordered">
  <thead>
    <th style="width: 20%" scope="col">Category</th>
    <th style="width: 60%" scope="col">Description</th>
    <th style="width: 10%" scope="col">Completed</th>
    <th style="width: 10%" scope="col">Check</th>
  </thead>
  <tbody>
    <tr id="1">
      <td>Main</td>
      <td>Lorem ipsum dolor sit amet</td>
      <td>1</td>
      <td id="1"><input type="checkbox" class="select-item checkbox" name="select-item" value="1"></td>
    </tr>
  </tbody>
</table>

webrtc shows extra stream with only one peer connection

I am trying to allow my webrtc video call meeting app have multiple peer connections and have the stream appear on the remote video call dynamically using javascript. I have only two tabs in two windows open on the video test room url. One is incognito and one is regular google chrome browser but both are google chrome browsers. They are both logged in to superusers. The problem is, the remote videos are shown twice of the same user of the remote user.

This is my video call html file.

<!DOCTYPE html>
<html>

<head>
  <style>
    button {
      border: 0;
      background-color: orange;
      padding: 10px;
      color: white;
      border-radius: 7px;
    }

    video {
      border-radius: 15px;
    }

    .videoContainer {
      display: flex;
      margin: 20px;
      width: 640px;
    }

    .videoContainer h2 {
      color: white;
      position: relative;
      bottom: -380px;
      left: -350px;
      width: max-content;
    }

    #meet {
      display: flex;
    }

    #recordButton.recording {
      background-color: red;
    }

    #downloadButton {
      background-color: #4caf50;
    }

    button:disabled {
      background-color: #cccccc;
      cursor: not-allowed;
    }
  </style>
  <title>A Free Bird Video Call</title>
  <script src="https://meet.jit.si/external_api.js"></script>
</head>

<body>
  <div id="meet">
    
    <div id="remote-videos">
      <div class="videoContainer">
        <video id="localVideo" autoplay playsinline></video>
        <h2>{{ request.user.full_name }}</h2>
      </div>
    </div>
    <!-- <div class="videoContainer">
      <video id="remoteVideo" autoplay playsinline></video>
      <h2></h2>
    </div> -->
  </div>

  <div>
    <button onclick="startCall()">Start Call</button>
    <button id="recordButton" onclick="toggleRecording()" disabled>
      Start Recording
    </button>
    <button id="downloadButton" onclick="downloadRecording()" disabled>
      Download Recording
    </button>
  </div>
  <script>
    // Global variables
    let peerConnection = null;
    let localStream = null;
    let isInitiator = false;
    let iceCandidatesQueue = [];
    let mediaRecorder = null;
    let recordedChunks = [];
    let isRecording = false;
    const roomName = "{{ room_name }}";
    const signalingChannel = new WebSocket(
      `wss://${window.location.host}/ws/webrtc/${roomName}/`
    );

    setupMediaStream();

    signalingChannel.onopen = async () => {
      console.log("WebSocket connected!");
      signalingChannel.send(
        JSON.stringify({
          type: "join",
          room: roomName,
          username: "{{ request.user.full_name }}",
        })
      );
    };

    async function setupMediaStream() {
      try {
        localStream = await navigator.mediaDevices.getUserMedia({
          video: true,
          audio: true,
        });
        document.getElementById("localVideo").srcObject = localStream;
        console.log("Local stream setup successful");
        globalThis.localStream = localStream;
      } catch (err) {
        console.error("Error accessing media devices:", err);
        throw err;
      }
    }

    async function initializePeerConnection(localStream) {
      const servers = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}


      peerConnection = new RTCPeerConnection(servers);
      console.log("Created peer connection");


      peerConnection.addStream(globalThis.localStream);
      console.log("✅ Track added to peer connection");

      peerConnection.ontrack = (event) => {
        const userId = "{{ request.user.id }}"
        console.log("Received remote track");
        const remoteVideo = document.getElementById("remote-videos");
        const stream = event.streams[0];
        const container = document.createElement("div");
        container.classList.add("videoContainer");
        container.id = `remote-video-${userId}`;
        const videoElement = document.createElement('video');
        videoElement.srcObject = stream;
        videoElement.play();


        remoteVideo.appendChild(container);
        container.appendChild(videoElement);
        container.appendChild(document.createElement("h2"));
        setupRecording();
        
      };

      // ICE candidate handling



      peerConnection.onsignalingstatechange = (event) => {
        console.log("Signaling state change:", peerConnection.signalingState);
      };

      peerConnection.onconnectionstatechange = (event) => {
        console.log("Connection state change:", peerConnection.connectionState);
      };

      return peerConnection;
    }

    
    async function setupAndStart() {
      try {
        if (!peerConnection) {
          await initializePeerConnection();
          const remoteStreams = peerConnection.getRemoteStreams();
          console.log(remoteStreams);

        }
      } catch (err) {
        console.error("Error in setupAndStart:", err);
      }
    }

    async function handleCandidate(message) {
      try {
        if (!peerConnection || !peerConnection.remoteDescription) {
          console.log("Queueing ICE candidate");
          iceCandidatesQueue.push(message.candidate);
          
        }

        if (message.candidate) {
          await peerConnection.addIceCandidate(
            new RTCIceCandidate(message.candidate)
          );
          console.log("Added ICE candidate");
        }
      } catch (err) {
        console.error("Error adding ICE candidate:", err);
      }
    }

    async function handleOffer(message) {
      try {
        if (!peerConnection) {
          await setupAndStart();
        }

        await peerConnection.setRemoteDescription(
          new RTCSessionDescription({
            type: "offer",
            sdp: message.sdp,
          })
        );

        peerConnection.onicecandidate = (event) => {
        if (event.candidate) {
          console.log("Sending ICE candidate");
          signalingChannel.send(
            JSON.stringify({
              type: "ice-candidate",
              candidate: event.candidate,
              username: "{{ request.user.full_name }}",
            })
          );
          console.log("ICE candidate sent");
        }
      };

        // Process queued candidates
        while (iceCandidatesQueue.length > 0) {
          const candidate = iceCandidatesQueue.shift();
          await peerConnection.addIceCandidate(
            new RTCIceCandidate(candidate)
          );
          console.log("Added queued ICE candidate");
        }

        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);

        signalingChannel.send(
          JSON.stringify({
            type: "answer",
            sdp: answer.sdp,
            username: "{{ request.user.full_name }}",
          })
        );
      } catch (err) {
        console.error("Error handling offer:", err);
      }
    }

    async function handleAnswer(message) {
      try {
        await peerConnection.setRemoteDescription(
          new RTCSessionDescription({
            type: "answer",
            sdp: message.sdp,
          })
        );

        // Process queued candidates
        while (iceCandidatesQueue.length > 0) {
          const candidate = iceCandidatesQueue.shift();
          await peerConnection.addIceCandidate(
            new RTCIceCandidate(candidate)
          );
          console.log("Added queued ICE candidate");
        }
      } catch (err) {
        console.error("Error handling answer:", err);
      }
    }

    async function startCall() {
      try {
        iceCandidatesQueue = [];
        await setupAndStart();

        const offer = await peerConnection.createOffer();
        await peerConnection.setLocalDescription(offer);

        signalingChannel.send(
          JSON.stringify({
            type: "offer",
            sdp: offer.sdp,
            username: "{{ request.user.full_name }}",
          })
        );
      } catch (err) {
        console.error("Error starting call:", err);
      }
    }

    function updateRemoteUsername(message) {
      const userId = "{{ request.user.id }}"
      const remoteVideoContainer = document.getElementById(`remote-video-${userId}`);
        if (remoteVideoContainer) {
          const remoteNameElement = remoteVideoContainer.querySelector("h2");
          if (remoteNameElement) {
            remoteNameElement.textContent = message;
          }
        }
    }

    signalingChannel.onmessage = async (event) => {
      const message = JSON.parse(event.data);
      console.log("Received message:", message);

      try {
        switch (message.type) {
          case "join":
            if (!message.self) {
              isInitiator = true;
              updateRemoteUsername(message.username);
              await setupAndStart();
            }
            break;
          case "offer":
            
              updateRemoteUsername(message.username);
              await handleOffer(message);
            
            break;
          case "answer":
            await handleAnswer(message);
            break;
          case "ice-candidate":
            await handleCandidate(message);
            break;
          case "candidate":
            await handleCandidate(message);
            break;
          default:
            console.log("Unknown message type:", message.type);
        }
      } catch (err) {
        console.error("Error handling message:", err);
      }
    };

    async function setupRecording(stream) {
      try {
        // Create a canvas element to combine the videos
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext("2d");

        // Get video elements
        const localVideo = document.getElementById("localVideo");
        const remoteVideo = document.getElementById("remoteVideo");

        // Wait for both streams to be available
        if (!localVideo.srcObject || !remoteVideo.srcObject) {
          console.log(
            "Waiting for both streams before setting up recording..."
          );
          return;
        }

        // Set canvas size to match the video container width and height
        const videoContainer = document.querySelector(".videoContainer");
        const containerStyle = window.getComputedStyle(videoContainer);
        canvas.width = videoContainer.offsetWidth * 2; // Width of two videos
        canvas.height = videoContainer.offsetHeight;

        // Create a stream from the canvas
        const canvasStream = canvas.captureStream(30); // 30 FPS

        // Add audio tracks from both streams
        const localAudioTrack = localVideo.srcObject.getAudioTracks()[0];
        const remoteAudioTrack = remoteVideo.srcObject.getAudioTracks()[0];
        if (localAudioTrack) canvasStream.addTrack(localAudioTrack);
        if (remoteAudioTrack) canvasStream.addTrack(remoteAudioTrack);

        // Create MediaRecorder
        mediaRecorder = new MediaRecorder(canvasStream, {
          mimeType: "video/webm;codecs=vp8,opus",
        });

        // Handle data available event
        mediaRecorder.ondataavailable = (event) => {
          if (event.data && event.data.size > 0) {
            recordedChunks.push(event.data);
          }
        };

        // Handle recording stop
        mediaRecorder.onstop = () => {
          console.log("Recording stopped");
          document.getElementById("downloadButton").disabled = false;
        };

        // Enable record button
        document.getElementById("recordButton").disabled = false;

        // Draw frames to canvas
        function drawFrame() {
          // Draw local video
          ctx.drawImage(localVideo, 0, 0, canvas.width / 2, canvas.height);
          // Draw remote video
          ctx.drawImage(
            remoteVideo,
            canvas.width / 2,
            0,
            canvas.width / 2,
            canvas.height
          );

          // Add names under videos
          ctx.fillStyle = "white";
          ctx.font = "20px Arial";
          const localName = "{{ request.user.full_name }}";
          const remoteName =
            remoteVideo.parentElement.querySelector("h2").textContent;

          // Add local name
          ctx.fillText(localName, 10, canvas.height - 20);
          // Add remote name
          ctx.fillText(remoteName, canvas.width / 2 + 10, canvas.height - 20);

          if (isRecording) {
            requestAnimationFrame(drawFrame);
          }
        }

        // Start the recording loop when recording starts
        mediaRecorder.onstart = () => {
          drawFrame();
        };

        console.log("Recording setup complete");
      } catch (err) {
        console.error("Error setting up recording:", err);
      }
    }

    async function toggleRecording() {
      const recordButton = document.getElementById('recordButton');

      if (!isRecording) {
        // Start recording
        recordedChunks = [];
        try {
          if (!mediaRecorder) {
            await setupRecording();
          }

          if (mediaRecorder && mediaRecorder.state === 'inactive') {
            mediaRecorder.start(1000); // Record in 1-second chunks
            isRecording = true;
            recordButton.textContent = 'Stop Recording';
            recordButton.classList.add('recording');
            console.log("Recording started");
          }
        } catch (err) {
          console.error("Error starting recording:", err);
        }
      } else {
        // Stop recording
        if (mediaRecorder && mediaRecorder.state === 'recording') {
          mediaRecorder.stop();
          isRecording = false;
          recordButton.textContent = 'Start Recording';
          recordButton.classList.remove('recording');
          console.log("Recording stopped");
        }
      }
    }

    function downloadRecording() {
      try {
        if (!recordedChunks.length) {
          console.error("No recording data available");
          return;
        }

        const blob = new Blob(recordedChunks, {
          type: 'video/webm'
        });

        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        document.body.appendChild(a);
        a.style.display = 'none';
        a.href = url;
        a.download = `call-recording-${new Date().toISOString()}.webm`;

        a.click();

        // Cleanup
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
        recordedChunks = [];
        document.getElementById('downloadButton').disabled = true;
      } catch (err) {
        console.error("Error downloading recording:", err);
      }
    }

    // Cleanup when the call ends
    window.onbeforeunload = () => {
      if (localStream) {
        localStream.getTracks().forEach((track) => track.stop());
      }
      if (peerConnection) {
        peerConnection.close();
      }
      if (signalingChannel) {
        signalingChannel.close();
      }
      if (mediaRecorder && mediaRecorder.state === 'recording') {
        mediaRecorder.stop();
      }
      isRecording = false;
    };

    window.onunload = () => {
  if (peerConnection) {
    peerConnection.close();
  }
};
  </script>
</body>

</html>

Any clues as to why there is an extra remote video? By the way, I am using the same webcam for both peer connections.

So I would end up seeing 3 webcams. One for local. 2 for remote peers. There should only be 2 webcam streams. 1 for local. 1 for remote peer. This is because I only had two tabs open for the video call meeting room.

Maintain table order and page selection in Shiny JS call to edit cell in DT

The code below replicates the problem. I have an app for users to update values and I need to refresh the page after they update the values because the colors need to update, but I haven’t been able to make it so pagination and ordering are preserved after the edits. GPT helped write a minimal working example but couldn’t solve the issue

ui <- fluidPage(
  tags$head(
    tags$script(HTML("
      // Custom handler: update the select element and row color without redrawing.
      Shiny.addCustomMessageHandler('updateDropdown', function(message) {
        var tableEl = $('#demo_table table');
        if (!tableEl.length) {
          console.warn('Table element not found');
          return;
        }
        var table = tableEl.DataTable();
        var colIndex = 2; // Category column index.
        var rowIndexes = table.rows().indexes().filter(function(idx) {
          return table.row(idx).data()[0] == message.row;
        });
        if (rowIndexes.length > 0) {
          var rowIndex = rowIndexes[0];
          var cellNode = table.cell(rowIndex, colIndex).node();
          $(cellNode).find('select').val(message.new_value);
          $(table.row(rowIndex).node()).css('background-color', message.new_color);
        }
      });
    "))
  ),
  DTOutput("demo_table")
)

server <- function(input, output, session) {
  # Create reactive data.
  data <- reactiveVal(data.frame(
    ID = 1:100,
    Value = sample(1:1000, 100),
    Category = sample(c("A", "B", "C"), 100, replace = TRUE),
    stringsAsFactors = FALSE
  ))
  
  # Helper function to create dropdown HTML.
  createDropdown <- function(row_id, current) {
    sprintf(
      '<select data-row="%s" onchange="Shiny.setInputValue('category_change', {row: %s, value: this.value, nonce: Math.random()})">
         <option value="A" %s>A</option>
         <option value="B" %s>B</option>
         <option value="C" %s>C</option>
       </select>',
      row_id, row_id,
      ifelse(current == "A", "selected", ""),
      ifelse(current == "B", "selected", ""),
      ifelse(current == "C", "selected", "")
    )
  }
  
  # Render the table only once by isolating the reactive data.
  output$demo_table <- renderDT({
    df <- isolate(data())
    # Replace Category column with dropdown HTML.
    df$Category <- sapply(df$ID, function(id) {
      cat <- data()[data()$ID == id, "Category"]
      createDropdown(id, cat)
    })
    datatable(
      df,
      escape = FALSE,
      rownames = FALSE,
      options = list(
        pageLength = 10,
        stateSave = TRUE,
        order = list(list(1, "asc")),
        rowCallback = JS("function(row, data, index) {
          // Set hidden row id.
          $(row).attr('data-row-id', data[0]);
          // Color rows based on Value.
          var val = parseInt(data[1]);
          $(row).css('background-color', val > 500 ? 'lightblue' : 'white');
        }")
      )
    )
  }, server = TRUE)
  
  proxy <- dataTableProxy("demo_table")
  
  # When the dropdown value changes, update the data and replace the table data.
  observeEvent(input$category_change, {
    req(input$category_change)
    info <- input$category_change
    df <- data()
    row_idx <- which(df$ID == info$row)
    if (length(row_idx) == 0) return()
    
    # Update the underlying data.
    df$Category[row_idx] <- info$value
    data(df)
    
    # Rebuild the table data with the updated dropdown HTML.
    new_df <- isolate(data())
    new_df$Category <- sapply(new_df$ID, function(id) {
      cur <- df[df$ID == id, "Category"]
      createDropdown(id, cur)
    })
    
    # Update the table without resetting pagination.
    replaceData(proxy, new_df, resetPaging = FALSE)
  })
}

shinyApp(ui, server)



I tried tweaking the JS by directly updating the DOM but that didn’t work. GPT has given me many solutions but the problem always persists. It works if it’s a single page. I have looked at other posts – the problem in here is similar, but they don’t use JS as the option is not a dropdown, so I wasn’t able to replicate the solution

Blockquote

Force all slides to have same height in Owl Carousel

I have dynamic slides in Owl Carousel, but I can’t get them all to have the same height. The height automatically adjusts based on the amount of text inside each slide, causing inconsistent sizes that disrupt the layout. Everything works fine, there are no errors in the console, and the slides transition smoothly, but their height varies. How can I force all slides to have the same height regardless of content?

Screen:

enter image description here

  $(document).ready(function () {
        $('.slider-carousel').owlCarousel({
            loop: true,
            margin: 0,
            nav: false,
            center: true,
            autoplay: true,
            dots: true,
            responsive: {
                0: {
                    items: 1
                },
                768: {
                    items: 1
                },
                1000: {
                    items: 1
                }
            }
        });
    });

    $(document).ready(function () {
        $('.post-car').owlCarousel({
            loop: true,
            margin: 20,
            nav: true,
            center: true,
            autoplay: false,
            dots: false,
            responsive: {
                0: {
                    items: 1
                },
                768: {
                    items: 2
                },
                1000: {
                    items: 3
                }
            }
        });
    });

I tried changing to

$('.slider-carousel').owlCarousel({
    loop: true,
    margin: 0,
    nav: false,
    center: true,
    autoplay: true,
    dots: true,
    autoHeight: false, // WYŁĄCZAMY AUTO HEIGHT!
    responsive: {
        0: { items: 1 },
        768: { items: 1 },
        1000: { items: 1 }
    },
    onInitialized: function() {
        setTimeout(runHeightFix, 100);
    },
    onTranslated: function() {
        runHeightFix();
    }
});

and adding this to css

/* Wymuszenie tej samej wysokości dla każdego kafelka */
.slider-carousel .owl-stage-outer,
.post-car .owl-stage-outer {
    min-height: 400px !important;
    height: auto !important;
    overflow: hidden !important;
}

.slider-carousel .owl-item,
.post-car .owl-item {
    display: flex !important;
    align-items: stretch !important;
    min-height: 450px !important; /* Możesz zwiększyć */
    height: 100% !important;
    max-height: 600px !important; /* Możesz dostosować */
}

.slider-carousel .item,
.post-car .item {
    display: flex;
    flex-direction: column;
    height: 100% !important;
}

.slider-carousel .box-in,
.post-car .box-in {
    flex-grow: 1;
}

but no luck

Javascript Delay Timer Only Applies To Last Changed Textarea

I have multiple textareas on a web page like below. What happens is when you type into one of the textareas, there is a delay which triggers on keyup and then takes the text in the textarea and sends it to an AJAX call to save it. The ajax call and saving is working fine.

The issue is if I type in more than one textarea before the delay trigger runs, only the last textarea I type in is sent to an ajax call.

Currently what is happening:

  1. Type in textarea 1
  2. Type in textarea 2
  3. Only changes in textarea 2 are sent to ajax call

What should happen:

  1. Type in textarea 1 and changes trigger call ajax call to save changes
  2. Type in textarea 2 and changes trigger call ajax call to save changes

Typing into textarea 2 should not affect the saving of textarea 1.

HTML

<div data-mid="123">
    <div class="box" data-id="1">
        <textarea class="note" placeholder="...write something">123</textarea>
    </div>
    <div class="box" data-id="2">
        <textarea class="note" placeholder="...write something">1234</textarea>
    </div>
    <div class="box" data-id="3">
        <textarea class="note" placeholder="...write something">12345</textarea>
    </div>
    <div class="box" data-id="4">
        <textarea class="note" placeholder="...write something">123456</textarea>
    </div>
</div>

Javascript

function delay(callback, ms) {
    var timer = 0;
    
    return function() {
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
            callback.apply(context, args);
        }, ms || 0);
    };
}

function save(rootNode)
{
    var note = rootNode.val();
    
    ajaxCall(null, 2, note, false);
}

$('.box').on('keyup', 'textarea', delay(function(event) {
    save($(this));
}, saveDelay));

When would I need `Object.preventExtensions()`?

Javascript has Object.seal() to prevent any extensions to an object (and Object.freeze() to completely freeze it). But it also has Object.preventExtensions() that seems to do the same job as Object.seal(), aside from some really small implementation differences.

In what situations would I want/need to use Object.preventExtensions() rather than Object.seal()?

how to activate free scrolling back to top of page after javascript scroll animation (WEB DEVELOPMENT)

I have a series of animations which ends with text and image being animated upwards(think of starwars narrative). when the animation is complete however, I am able to keep scrolling down the page to other content but i cannot scroll back up to reread the text which will be necessary. can anyone help provide a solution? here is my script.

//PAGE TITLE FADE+SCROLL ANIMATION
document.addEventListener("DOMContentLoaded", function () {
    const title = document.querySelector(".pagetitle");
    const text = document.querySelector(".text");
    const heroImage = document.querySelector(".hero");

    title.style.opacity = "1";
    text.style.opacity = "0";
    heroImage.style.transform = "translateY(0)";
    text.style.transform = "translateY(0)";

    setTimeout(() => {
        title.style.opacity = "0";
    }, 2000);

    setTimeout(() => {
        text.style.opacity = "1";
        text.style.transition = "opacity 1.5s ease-in-out";
    }, 2500);

    setTimeout(() => {
        const textBottomOffset = text.getBoundingClientRect().bottom;
        const windowHeight = window.innerHeight;
        const distanceToMove = textBottomOffset - windowHeight;

        heroImage.style.transition = "transform 3s ease-in-out";
        text.style.transition = "transform 3s ease-in-out";
        heroImage.style.transform = `translateY(-${distanceToMove}px)`;
        text.style.transform = `translateY(-${distanceToMove}px)`;

        // Enable scrolling after animation finishes
        setTimeout(() => {
            document.body.style.overflow = "auto";
        }, 3000);
    }, 4000);
});

I have tried changing the css positioning as well as animation using both transform and manipulating the whole page scroll positioning without success. I tried using CHATGPT which was no help. I’m sure the solution is some simple activation but I am very inexperienced with javascript.