How do you store a quadtree from jsts?

I’m using a quadtree from the jsts library. I am wondering what ways of storing the quadtree would preserve the properties of it so that when it is being read again I could access the methods in the jsts library?

So far I have tried saving the quadtree as a json and creating a new Quadtree and setting the _root to the json that is read, but when it is stringified using the JSON.toString() it changes the types of some of the properties, such as [Node] is changed to [object]. When I read it back again, I can’t use the quadtree methods such as size() returns an error of this._root.size is not a function.

Unit testing private methods and event listeners in TypeScript class hierarchy

I’m new to writing unit tests for private methods and I’ve been researching how to approach this in TypeScript. However, I couldn’t find a solution that fits my scenario.

Here’s a simplified version of my TypeScript code:

export class CallbackManager {
   protected queue: Set<() => void>;
   constructor() {
        this.queue = new Set();
   }
}

export class Report extends CallbackManager {
   public addToQueue(publishMetric: () => void): void {
        this.queue.add(publishMetric);
   }

   private flushQueue(): void {
        if (this.queue.size > 0) {
            this.queue.forEach((cb) => {
                cb();
            });

            this.queue.clear();
        }
    }

    public initializeBatching(): void {
        addEventListener('visibilitychange', () => {
            if (document.visibilityState === 'hidden') {
                this.flushQueue();
            }
        });
    }
}

Here’s what I’m trying to achieve in my tests:

Testing addToQueue method: I want to ensure that calling addToQueue adds a function to the queue.

describe('addToQueue', () => {
    it('should add a function to the queue', () => {
        const mockFunction = jest.fn();
        const report = new Report();
        report.addToQueue(mockFunction);
        expect((report as any).queue.size).toBe(1); // Accessing protected property directly for testing
    });
});

However, I’m encountering this error: Property ‘queue’ is protected and only accessible within class ‘CallbackManager’ and its subclasses.

Testing initializeBatching method: I want to verify that the visibilitychange event listener is added when initializeBatching is called.
Testing flushQueue method indirectly: I also want to test whether the flushQueue method is called when the visibilitychange event occurs, but flushQueue is a private method.
I’d appreciate any insights on how to approach these tests effectively in TypeScript. Thank you!

Is there a safe way to create a Component or Directive that apply an encapsulated class to an input tag i.e. ?

my goal is to use the input tag with selector[attribute] instead of selector to avoid wrapping the input with a host component.

I prefer selector[attribute] over selector] to avoid reinventing the wheel i.e. if I want to support autocomplete, I have to use @Input() autocomplete: string = "" and then add it to the <com-input autocomplete='email'>

Expected usage

    <input
      com-input
      #inputEmail
      autocomplete="username"
      (input)="email = inputEmail.nativeElement.value"
      [isError]="errorMessage ? true : false"
      placeholder="Email"
      type="email"
    />

The problem is:

  1. when we add the attribute i.e. <input com-input> and then add a hash symbol i.e. <input com-input #inputElement>, the the inputElement type become InputComponent instead of HTMLInputElement. So, we can not access the input value.

The solution is:

  1. implement ElementRef<HTMLInputElement> and inject ElementRef to InputComponent. Then, set the nativeElement property.

However, Angular documentation warned that the ElementRef is not safe. So, I am pretty sure my work is a hack.

/* eslint-disable @angular-eslint/component-selector */
import { Component, ElementRef, HostBinding, Input } from '@angular/core';

@Component({
  selector: 'input[com-input]',
  standalone: true,
  imports: [],
  templateUrl: './input.component.html',
  styleUrl: './input.component.scss',
})
export class InputComponent implements ElementRef<HTMLInputElement> {
  nativeElement: HTMLInputElement;

  @HostBinding('class.error') @Input() isError: boolean = false;

  constructor(elementRef: ElementRef) {
    this.nativeElement = elementRef.nativeElement;
  }
}

Why does the first image work good but the second one does not? [duplicate]

I have two images a left and right button. The left button works great when hovering over it. However, the right button when hovering over it changes the left button to the right button highlighting it. The right button appears to work great just not in the correct position.

As part of troubleshooting I have removing the left image and the right image works perfect on the right side of the screen the way it is supposed to.

Any thoughts? I have tried many different things in troubleshooting the problem but still to no avail.

Here is my code:

<style>  
  .right-button {
  position: fixed;
  top: 50%;
  right: -325px;
}

.left-button {
position: fixed;
top: 50%;
left: 20px;
}
</style>

<body>    

      <a href="./search.html" class="left-button"><img src="./img/orig_left_stamp.png" onmouseover="changeImage('./img/left_stamp_on.png')" onmouseout="changeImage('./img/orig_left_stamp.png')" style="width:20%;height:auto;"/></a>
      
      <a href="./index.html" class="right-button"><img src="./img/orig_right_stamp.png" onmouseover="changeImage('./img/right_stamp_on.png')" onmouseout="changeImage('./img/orig_right_stamp.png')" style="width:20%;height:auto;"/></a>
</body>

  <script>  
    function changeImage (newSrc) {
      document.querySelector('img').src = newSrc;
    }
  </script>

rename key in array of objects using javascript

I have a simple array of objects as shown below

let input = [
    { "p1": [ 1, 0 ] },
    { "p2": [ 1, 6 ] },
    { "total": [ 0, 4 ] },
    { "p3plus": [ 0, 2 ] }
]

All i want to do is just rename the keys of this array of objects. so my final output should be as shown below. Basically i am renaming p1, p2, p3plus and total with P1, P2, P3+ and Total.

let output = [
    { "P1": [ 1, 0 ] },
    { "P2": [ 1, 6 ] },
    { "Total": [ 0, 4 ] },
    { "P3+": [ 0, 2 ] }
]

I tried the following code

const output = input.map(({
  p1: P1,
  p2: P2,
  p3plus: P3+,
  total: Total,

  ...rest
}) => ({
  P1,
  P2,
  P3+, 
  Total, 
  ...rest
}));

This code doesnot work primarily because i am trying to put P3+ and it errors out during compilation. Even if i skip renaming p3plus, the output is not as expected because it keeps adding undefined to the final output. Can someone point to me where i am going wrong

Here is the error enter image description here

also, if i dont rename p3Plus and go ahead and rename other two, i see undefined objects which are not needed. how can i eliminate getting those undefined objects

enter image description here

How do I correctly render glyphs in dataTables PDF export?

I am using dataTables to render a table of best times for a swim team. There are two levels of qualifying times in the applicable universe, and I am annotating times in the table with unicode star glyphs. Outline for level B, and solid for level A. U+2606 and U+2605, respectively. The browser view is as expected:

dataTables view of best times table

However, when I export to PDF, the glyphs don’t render.

PDF export of dataTables view of best times

What I’ve read says this is an issue with the fact that Roboto, the default font in pdfMake doesn’t support the glyph, and that to include other fonts I would have to edit the vfs_fonts.js file and include the base-64 encoded definitions of the font I want to include. That presents its own challenge in that you need the redistribution license for a given font. I have found it rather difficult to confirm whether a given font has the glyph I need, and my attempt to use a free font I found produced strange errors.

$.ajax({
    url: '/ttf/FreeSans-LrmZ.ttf' ,
    success: function(result) {
        window.pdfMake.vfs["FreeSans.ttf"] = btoa(unescape(encodeURIComponent(result))) ;
    }
});

....

customize: function (doc) {
    pdfMake.fonts = {
        Roboto: {
            normal: 'Roboto-Regular.ttf',
            bold: 'Roboto-Medium.ttf',
            italics: 'Roboto-Italic.ttf',
            bolditalics: 'Roboto-MediumItalic.ttf'
        },
        freeSans: {
            normal: 'FreeSans.ttf',
        }
    };
    doc.defaultStyle.font = "freeSans";

....

embedded.js:15 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'advanceWidth')

I suppose that I really have two questions: 1) How do I find an appropriate font to correctly render the two unicode characters that I need? 2) How do I properly incorporate it so that I can resolve my js error and successfully export the correctly rendered PDF?

I have referenced the following questions and was unable to solve my problem:

Cannot use special characters/glyphs with PDFMAKE, even with imported Fonts which include those

pdfmake – using own fonts not working

DataTables & PDFmake

whatsapp-web.js Cannot read properties of undefined (reading ‘default’)

So I follow along the getting started guide to use whatsapp-web.js
Link To Guide

const { Client } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');

const client = new Client();

client.on('ready', () => {
    console.log('Client is ready!');
});

client.on('qr', qr => {
    qrcode.generate(qr, {small: true});
});

client.initialize();

When I start the code with node main.js the qr code is logged in terminal and I could scan it, but the problem is after I scan the QR code fro whatsapp login, I got undefined (reading ‘default’) error like below

0|main  | Error: Evaluation failed: TypeError: Cannot read properties of undefined (reading 'default')
0|main  |     at __puppeteer_evaluation_script__:5:95
0|main  |     at ExecutionContext._evaluateInternal (/home/ubuntu/haro-node/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:221:19)
0|main  |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
0|main  |     at async ExecutionContext.evaluate (/home/ubuntu/haro-node/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:110:16)
0|main  |     at async Client.initialize (/home/ubuntu/haro-node/node_modules/whatsapp-web.js/src/Client.js:323:9)

XTerm JS: allowing user input/making commands (in html/js)

Ive been checking out a few HTML/JS/CSS terminals I could use for my site and I came across XTerm.js. I decided to try it out, but I’m a little confused, after plugging in some code I found from a site (forgot the name), I had the terminal loaded, but my main problem is: I don’t know how to add commands, or have user input in general. I added a snippet of the terminal below, and if you try to type in it, nothing will happen, no keys are being registered.

I tried taking a look at a few questions here/sites, but none seem to help me, my knowledge is pretty low though when it comes to JS and HTML so it could also be that. I also checked the XTerm docs, but to be honest, it’s a little confusing for me. Could somebody please help me out here? Id like to know how to make commands for the terminal, and allow user input.

Here is my current code right now:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>XTerm terminalr</title>
  <link href="https://unpkg.com/xterm/css/xterm.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm.js"></script>
</head>
<body>
    
 <div id="terminal"></div>
 <link href="https://cdn.jsdelivr.net/npm/[email protected]/meslo.min.css" rel="stylesheet">
 
<style>

#terminal {
 width: 37%;
 font-size: 20px;
 font-family: meslo;
}

</style>

<script>
 
const term = new Terminal();
const shellprompt = 'root@XTerm:~# ';

function termKeyPress(ev) {
  term.on('key', (key, ev) => {
        console.log(key.charCodeAt(0));
        if (key.charCodeAt(0) == 13)
            term.write('n');
        term.write(key);
});
}

term.open(document.getElementById('terminal'));
term.prompt = function () {
  term.write('r' + shellprompt);
};

term.writeln('AT: localHostn');
term.prompt();

 </script>
</body>
</html>

shorten a URL in googlesheets with Apps Script?

I have a long URL for a google slide that is put into a calendar event. Everything works, but the long URL looks funky and I would like to be able to use a short URL that google sheets can generate on it’s own, without needing another API other than Apps Script.
enter image description here

/*
* Calendar must be created in advance. 
* Once created go to Settings and scroll down to  Calender ID. 
* Copy and paste ID into <Calendar ID> cell of main worksheet
* Copy and paste the calendar name into <Calendar Name> cell of main worksheet
*/
function CreateManyEvents() {
//Retrieve information from the spreadsheet 
// Make the 3rd sheet active in the active spreadsheet.
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[2]);

// Call function BulkDeleteEventsDelete on any pre-existing events 
BulkDeleteEvents(spreadsheet)

//Retrieve the value of the calendar ID from the cell that it lives in.
//Call CalendarApp service to open the calendar
var calendarId = spreadsheet.getRange("A1").getValue();
var eventCal = CalendarApp.getCalendarById(calendarId);

var classes = spreadsheet.getRange("A3:F8").getValues();
for (x=0; x<classes.length; x++) {   
    var item = classes[x];
    
    var title = item[0];
    var startTime = item[1];
    var endTime = item[2];
    var session = item[3];
    var what = item[5] + 'rnn' + item[4];

    //create event with data selected
    eventCal.createEvent(title, startTime, endTime,{location: session,description: what});  
    
  }

}

So is there a way to shorten the URL, item[5], before joining it with the other item that will go in the calendar event description?

I’ve looked around and solutions all involve changing a setting in Tools that I don’t have – all I’ve got is the Apps Script under Extensions – and/or using some 3rd party API.

I’m hoping for a solution that is ‘in house’ as it were. Even changing the URL to its IP address would be OK.

Trying to change the background color of a grid element on click event

I cant seem to figure out the syntax to get an element in my grid to change colors on a click event. Ive tried a few different iterations of this, and cant seem to figure out what im doing wrong. Pretty new to javscript so go easy on me if its a very obvious answer.

JS

    const gridContainer = document.getElementById('container');

    function makeGrid(rows, cols) {
      gridContainer.style.setProperty('--grid-rows', rows);
      gridContainer.style.setProperty('--grid-cols', cols);

      for (i = 0; i < rows * cols; i++) {
        let cell = document.createElement('div');
        gridContainer.appendChild(cell).className = 'grid-item';
      }
    }
    makeGrid(16, 16);

    const gridElement = document.getElementById('grid-item');
    gridElement.addEventListener('click', () => {
        gridElement.target.style.backgroundColor = 'white';
    })

css

:root {
    --grid-cols: 1;
    --grid-rows: 1;
}

#container{
    display: grid;
    grid-gap: 0em;
    grid-template-rows: repeat(var(--grid-rows), 1fr);
    grid-template-columns: repeat(var(--grid-cols), 1fr);
    background-color: black;
}

.grid-item{
    padding: 1em;
    border: 1px solid #131313;
    text-align: center;
}

.grid-item:hover{
    background-color: #ddd;
}

I tried to make a separate function to target the grid element and on clicking the element the color would change but it doesnt seem to click.

Why does this say ” Cannot read properties of null (reading ‘style’)”

I’m making a blog website .And I’m making it responsive and i made that responsive too.But the navbar doesn’t working.I think the problem is on Javascript
pls help me….

I went up to check on console .And it showed me this “Uncaught TypeError: Cannot read properties of null (reading ‘style’)
at HTMLButtonElement.openNav” . i looked up on youtube and stack overflow but nothing worked.Here is my code

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
    <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.0/css/line.css">
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
</head>
<body>
    <nav>
        <div class="container nav_container">
            <a href="index .html" class="nav_logo">blog</a>
            <ul class="nav_items">
                <li><a href="blog.html">Blog</a></li>
                <li><a href="about.html">About</a></li>
                <li><a href="signin.html">Sign in</a></li>
                <li><a href="contact.html">Contact</a></li>
                <li class="nav_profile">
                    <div class="avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <ul>
                        <li><a href="dashboard.html">Dashboard</a></li>
                        <li><a href="logout.html">Dashboard</a></li>
                    </ul>
                </li>
            </ul>
            <button id="open_nav-btn"><i class="uil uil-bars"></i></button>
            <button id="close_nav-btn"><i class="uil uil-multiply"></i></button>
        </div>
    </nav>
<!------------------------------NAV END---------------------------------->

<!------------------------------FEATURED---------------------------------->
<section class="featured">
    <div class="container featured_container post">
        <div class="post_thumbnail">
            <img src="./images/pintrest.jpg" alt="">
        </div>
        <div class="post_info">
            <a href="" class="category_button">Wild Life</a>
            <h2 class="post_title"><a href="post.html">Lorem ipsum dolor, sit amet consectetur 
                adipisicing elit.</a></h2>
            <p class="post_body">
                Lorem ipsum dolor sit amet consectetur adipisicing elit.
                Ipsum labore fuga repellendus.
            </p>
            <div class="post_author">
                <div class="post_author-avatar">
                    <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                </div>
                <div class="post_author-info">
                    <h5>By: JebaJeevan</h5>
                    <small>December,30,2023 - 07:23</small>
                </div>
            </div>
        </div>
    </div>
</section>

<!------------------------------FEATURED END---------------------------------->
<section class="posts">
    <div class="container post_container">
        <article class="post">
            <div class="post_thumbnail">
                <img src="./images/pintrest.jpg" >
            </div>
            <div class="post_info">
                <a href="" class="category_button">Wild Life</a>
                <h3 class="post_title">
                    <a href="post.html">Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, voluptatem.</a>
                </h3>
                <p class="post_body">
                    Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam optio, hic quibusdam eveniet animi aliquid soluta, aut totam alias 
                </p>
                <div class="post_author">
                    <div class="post_avatar-avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <div class="post_author-info">
                        <h5>By:Koala Man</h5>
                        <small>January 06 2024 - 2:21pm</small>
                    </div>
                </div>
            </div>
        </article>
        <article class="post">
            <div class="post_thumbnail">
                <img src="./images/pintrest.jpg" >
            </div>
            <div class="post_info">
                <a href="" class="category_button">Wild Life</a>
                <h3 class="post_title">
                    <a href="post.html">Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, voluptatem.</a>
                </h3>
                <p class="post_body">
                    Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam optio, hic quibusdam eveniet animi aliquid soluta, aut totam alias 
                </p>
                <div class="post_author">
                    <div class="post_avatar-avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <div class="post_author-info">
                        <h5>By:Koala Man</h5>
                        <small>January 06 2024 - 2:21pm</small>
                    </div>
                </div>
            </div>
        </article>
        <article class="post">
            <div class="post_thumbnail">
                <img src="./images/pintrest.jpg" >
            </div>
            <div class="post_info">
                <a href="" class="category_button">Wild Life</a>
                <h3 class="post_title">
                    <a href="post.html">Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, voluptatem.</a>
                </h3>
                <p class="post_body">
                    Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam optio, hic quibusdam eveniet animi aliquid soluta, aut totam alias 
                </p>
                <div class="post_author">
                    <div class="post_avatar-avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <div class="post_author-info">
                        <h5>By:Koala Man</h5>
                        <small>January 06 2024 - 2:21pm</small>
                    </div>
                </div>
            </div>
        </article>
        <article class="post">
            <div class="post_thumbnail">
                <img src="./images/pintrest.jpg" >
            </div>
            <div class="post_info">
                <a href="" class="category_button">Wild Life</a>
                <h3 class="post_title">
                    <a href="post.html">Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, voluptatem.</a>
                </h3>
                <p class="post_body">
                    Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam optio, hic quibusdam eveniet animi aliquid soluta, aut totam alias 
                </p>
                <div class="post_author">
                    <div class="post_avatar-avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <div class="post_author-info">
                        <h5>By:Koala Man</h5>
                        <small>January 06 2024 - 2:21pm</small>
                    </div>
                </div>
            </div>
        </article>
        <article class="post">
            <div class="post_thumbnail">
                <img src="./images/pintrest.jpg" >
            </div>
            <div class="post_info">
                <a href="" class="category_button">Wild Life</a>
                <h3 class="post_title">
                    <a href="post.html">Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, voluptatem.</a>
                </h3>
                <p class="post_body">
                    Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam optio, hic quibusdam eveniet animi aliquid soluta, aut totam alias 
                </p>
                <div class="post_author">
                    <div class="post_avatar-avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <div class="post_author-info">
                        <h5>By:Koala Man</h5>
                        <small>January 06 2024 - 2:21pm</small>
                    </div>
                </div>
            </div>
        </article>
        <article class="post">
            <div class="post_thumbnail">
                <img src="./images/pintrest.jpg" >
            </div>
            <div class="post_info">
                <a href="" class="category_button">Wild Life</a>
                <h3 class="post_title">
                    <a href="post.html">Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, voluptatem.</a>
                </h3>
                <p class="post_body">
                    Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam optio, hic quibusdam eveniet animi aliquid soluta, aut totam alias 
                </p>
                <div class="post_author">
                    <div class="post_avatar-avatar">
                        <img src="./images/person_FILL0_wght400_GRAD0_opsz24.png" alt="">
                    </div>
                    <div class="post_author-info">
                        <h5>By:Koala Man</h5>
                        <small>January 06 2024 - 2:21pm</small>
                    </div>
                </div>
            </div>
        </article>
    </div>
</section>
<!------------------------------POSTS END---------------------------------->

<section class="category_buttons">
     <div class="container category_buttons-container">
        <a href="" class="category_button">Art</a>
        <a href="" class="category_button">Wild Life</a>
        <a href="" class="category_button">Best</a>
        <a href="" class="category_button">Funny</a>
        <a href="" class="category_button">Useful</a>
        <a href="" class="category_button">DIY</a>
     </div>
</section>
<!------------------------------CB END---------------------------------->


<footer>
    <div class="footer_socials">
        <a href="https://www.youtube.com/@EGATORTUTORIALS" target="_blank"><i class="uil uil-youtube">
        </i></a>
        <a href="https://www.youtube.com/@EGATORTUTORIALS" target="_blank"><i class="uil uil-facebook-f">
        </i></a>
        <a href="https://www.youtube.com/@EGATORTUTORIALS" target="_blank"><i class="uil uil-twitter">
        </i></a>
        <a href="https://www.youtube.com/@EGATORTUTORIALS" target="_blank"><i class="uil uil-instagram">
        </i></a>
    </div>
    <div class="container footer_container">
        <article>
            <h4>Categories</h4>
            <ul>
                <li><a href="">Art</a></li>
                <li><a href="">Music</a></li>
                <li><a href="">Business</a></li>
                <li><a href="">Car</a></li>
                <li><a href="">Call</a></li>
                <li><a href="">Road</a></li>
            </ul>
        </article>
        <article>
            <h4>Support</h4>
            <ul>
                <li><a href="">Online Support</a></li>
                <li><a href="">Location</a></li>
                <li><a href="">Email</a></li>
                <li><a href="">Social Support</a></li>
                <li><a href="">Call</a></li>
            </ul>
        </article>
        <article>
            <h4>Blog</h4>
            <ul>
                <li><a href="">Safety</a></li>
                <li><a href="">repair</a></li>
                <li><a href="">Popular</a></li>
                <li><a href="">Recent</a></li>
                <li><a href="">Categories</a></li>
            </ul>
        </article>
        <article>
            <h4>Permalinks</h4>
            <ul>
                <li><a href="#">Home</a></li>
                <li><a href="#">Blog</a></li>
                <li><a href="#">About</a></li>
                <li><a href="#">Services</a></li>
                <li><a href="#">Contact</a></li>
            </ul>
        </article>
    </div>
</footer>
   <script src="main.js" defer></script>
</body>
</html>

css

:root{
  --color-primary:#6f6af8;
  --color-primary-light:hsl(242, 91%, 69%, 18%);
  --color-primary-variant:#5854c7;
  --color-red:#da0f3f;
  --color-red-light:hsl(346,87%,47%,15%);
  --color-green:#00c476;
  --color-green-light:hsl(156, 100%, 38%, 15%);
  --color-gray-900:#1e1e66;
  --color-gray-700:#2b2b7c;
  --color-gray-300:rgba(242, 242, 254, 0.3);
  --color-gray-200:rgba(242, 242, 254, 0.7);
  --color-white:#f2f2fe;
  --color-bg:#0f0f3e;

  --transition:all 300ms ease ;

  --container-width-lg:74%;
  --container-width-md:88%;
  --form-width:40%;

  --card-border-radius-1:0.3rem;
  --card-border-radius-2:0.5rem;
  --card-border-radius-3:0.8rem;
  --card-border-radius-4:2rem;
  --card-border-radius-5:5rem;
}
/*==========GENERAL==========*/
*{
  margin:0;
  padding: 0;
  outline: 0;
  border: 0;
  appearance: 0;
  list-style: none;
  text-decoration: none;
}

body{
  font-family: 'Poppins', sans-serif;
  line-height: 1.6;
  color: var(--color-gray-200);
  overflow-x: hidden;
  background: var(--color-bg);
  font-size: 0.9rem;
}

.container{
  width: var(--container-width-lg);
  max-width: 1800px;
  margin-inline: auto;
}

section{
  margin-top: 3rem;
  width: 100vw;
}

h1,h2,h3,h4,h5{
  color: var(--color-white);
  line-height: 1.3;
}

h1{
  font-size: 3rem;
  margin: 1rem 0;
}

h2{
  font-size: 1rem;
  margin: 1rem 0;
}

h3{
  font-size: 0.9rem;
  margin: 0.8rem 0 0.5rem;
}

h4{
  font-size: 1rem;
}

a{
  color: var(--color-white);
  transition: var(--transition);
}
img{
  display: block;
  width: 100%;
  object-fit: cover;
}


/*==========NAV==========*/

nav{
  background: var(--color-primary);
  width: 100vw;
  height: 4.5rem;
  position: fixed;
  top: 0;
  z-index: 10;
  box-shadow: 0  1rem 1rem rgba(0, 0, 0, 0.2);
}
nav button{
  display: none ;
}
.nav_container{
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.avatar{
  width: 2.5rem;
  aspect-ratio: 1/1;
  height: 2.5rem;
  border-radius: 50%;
  overflow: hidden;
  border: 0.3rem solid var(--color-bg);
}
.nav_logo{
  font-weight: 600;
  font-size: 1.2rem;
}
.nav_items{
  display: flex;
  align-items: center;
  gap: 4rem;
}
.nav_profile{
  position: relative;
  cursor: pointer;
}
.nav_profile ul{
  position: absolute;
  top: 125%;
  right: 0;
  display: flex;
  flex-direction: column;
  box-shadow: 0 3rem 3rem rgba(0, 0, 0, 0.4);
  visibility: hidden;
  opacity: 0;
  transition: var(--transition);
}
/*show nav ul when nav profile is hovered*/
.nav_profile:hover > ul{
  visibility: visible;
  opacity: 1;
}
.nav_profile ul li a{
  padding: 1rem;
  background: var(--color-gray-900);
  display: block;
  width: 100%;
}
.nav_profile ul li:last-child a{
  background: var(--color-red);
  color: var(--color-bg);
}
/*==========CATEGORY BUTTON==========*/

.category_button{
  background: var(--color-primary-light);
  color: var(--color-primary);
  padding: 0.5rem 1rem;
  border-radius: var(--card-border-radius-2);
  font-weight: 600;
  font-size: 0.8rem;
  text-align: center;
}
.category_button:hover{
  color: var(--color-white);
}
/*==========GENERAL POST==========*/

.post_thumbnail{
  border-radius: 3rem 0rem;
  border-color: #f2f2fe;
  overflow: hidden;
  margin-bottom: 1.6rem;
}
.post_author{
  display: flex;
  gap: 1rem;
  margin-top: 1.2rem;
}
.post_author-avatar{
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--card-border-radius-2);
  overflow: hidden;
}
/*==========FEATURED==========*/
.featured{
  margin-top: 8rem;
}
.featured_container{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4rem;
}
.featured .post_thumbnail{
  height: fit-content;
}


/*==========POST==========*/

.post_container{
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: 5rem;
  margin-bottom: 5rem;
}

/*==========CATEGORY BUTTON==========*/

.category_buttons{
  padding: 4rem 0rem;
  border-top: 2px solid var(--color-gray-900);
  border-bottom: 2px solid var(--color-gray-900);

}
.category_buttons-container{
  width: fit-content;
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: 1rem;
}

/*==========FOOTER==========*/

footer{
  background: var(--color-gray-900);
  padding: 5rem 0 0;
  box-shadow: inset 0rem 1.5rem 1.5rem rgba(0, 0, 0, 0.2);
}
.footer_socials{
  margin-inline: auto;
  width: fit-content;
  margin-bottom: 5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1.2rem;
}
.footer_socials a{
  background: var(--color-bg);
  border-radius: 50%;
  width: 2.3rem;
  height: 2.3rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.footer_socials a:hover{
  background: var(--color-white);
  color: var(--color-bg);
}
.footer_container{
  display: grid;
  grid-template-columns: repeat(4,1fr);
}
footer h4{
  color: var(--color-white);
  margin-bottom: 0.6rem;
}
footer ul li{
  padding: 0.4rem 0;
}
footer ul a{
  opacity: 0.75;
}
footer ul a:hover{
  letter-spacing: 0.2rem;
  opacity: 1;
}
/*==========MEDIA QUERIES [MEDIUM DEVICES]==========*/
@media screen and (max-width:1024px) {
  /*==========GENERAL==========*/
    .container{
      width: var(--container-width-md);
    }

    h2{
      font-size: 1.6rem;
    }
    h3{
      font-size: 1.2rem;
    }
    h5{
      font-size: 0.8rem;
    }
  /*==========NAV==========*/
  nav button{
    display: inline-block;
    font-size: 1.5rem;
    background: transparent;
    color: var(--color-white);
    cursor: pointer;
  }
  nav button#close_nav-btn{
    display:none;
  }
  .nav_container{
    position: relative;
  }
  .nav_items{
    position: absolute;
    flex-direction: column;
    top: 100%;
    right: 0;
    width: 12rem;
    align-items: flex-start;
    justify-content: center;
    gap: 0;
    display: none;
  }
  .nav_items li{
    width: 100%;
    height: 4.5rem;
    display: flex;
    align-items: center;
    box-shadow: -2rem 3rem 7rem rgba(0, 0, 0, 0.8);
    border-top: 1px solid var(--color-bg);
  }
  .nav_items li a{
    border-radius: 0;
    width: 100%;
    height: 100%;
    background: var(--color-gray-900);
    padding: 0 2rem;
    display: flex;
    align-items: center;
  }

  .nav_profile{
    background: var(--color-gray-900);
  }

  .nav_profile ul{
    top: 100%;
    width: 100%;
  }

  .nav_profile .avatar{
    margin-left: 2rem;
    border: 0;
  }
}

js

const navItems = document.querySelector('nav_items');
const openNavBtn = document.querySelector('#open_nav-btn');
const closeNavBtn = document.querySelector('#close_nav-btn');

const openNav = () => {
    navItems.style.display = 'flex';
    openNavBtn.style.display = 'none';
    closeNavBtn.style.display = 'inline-block';
}
openNavBtn.addEventListener('click',openNav);

Duplicate Recording Requests in Browser Extension for Mic/Screen sourced Audio Transcription

I have a website where a function is the user can transcribe audio from their mic and pc audio at the same time, to run other things (core function). Since he browser won’t let computer audio to be recorded through it, I need to have this done through an extension.

The extension does this, and sends the chunks to the API, this happens perfectly.

What happens that shouldn’t, though, is it appears to keep duplicating recording requests on itself. This leads to a lot of errors in transcription, and corrupt audio files.

So, in a nut shell, if someone can help me find a way to make it loop without erroneous/duplicate blobs being sent, while not duplicating processes to it is light on the computer memory, I would be very greatful!

Here’s the contentScript.js code (where these functions are happening)

// contentScript.js
console.log("Content script has loaded.");

let mediaRecorder;
let isRecording = false;
let audioContext = new AudioContext();
let mixedStream = null;
let recording = false;
let recordingCount = 0; // Variable to count how many times startRecording has been invoked
let lastAudioHash = null;
let audioQueue = [];
let processingAudio = false;
let mediaRecording = 0;
const MIN_TIME_GAP = 5000; // Minimum time difference (in milliseconds) to consider non-duplicate
let lastAudioSentTime = null; // Stores the timestamp of the last sent audio

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log("Received command:", request.command);
    switch (request.command) {
        case "startRecording":
            if (!isRecording) {
                startRecording(sendResponse);
            } else {
                console.log("Recording already in progress.");
                sendResponse({ status: 'success', message: 'Recording already started.' });
            }
            break;
        case "stopRecording":
            stopRecording();
            sendResponse({ status: 'success', message: 'Recording stopped.' });
            console.log("Recording stopped.");
            break;
    }
    return true; // This keeps the sendResponse callback valid for asynchronous use
});

async function startRecording(sendResponse) {
    console.log(`Starting recording`);
    if (recordingCount > 1) {
        console.log("Exiting function because recordingCount is greater than 1.");
        return; // Exit the function
    }
    
    try {
        if (!mixedStream || !isStreamActive(mixedStream)) {
            mixedStream = await initializeStreams();
        }
        if (audioContext.state === 'suspended') {
            await audioContext.resume();
        }
        setupRecorder(mixedStream);
        //isRecording = true;
        recording = true;
        
        console.log("Recording started successfully.");
    } catch (error) {
        console.error("Failed to start media capture:", error);
    }
}

async function initializeStreams() {
    let micStream = await navigator.mediaDevices.getUserMedia({ audio: true });
    let displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true });
    return mixStreams(micStream, displayStream);
}

function mixStreams(micStream, displayStream) {
    const micSource = audioContext.createMediaStreamSource(micStream);
    const displaySource = audioContext.createMediaStreamSource(displayStream);
    const destination = audioContext.createMediaStreamDestination();
    micSource.connect(destination);
    displaySource.connect(destination);
    return destination.stream;
}

function setupRecorder(stream) {

    mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm; codecs=opus' });
    mediaRecorder.ondataavailable = handleDataAvailable;
    if (mediaRecording === 0){
        mediaRecording = 1;
        mediaRecorder.start(20000); // Record in 10-second chunks
    }

    mediaRecorder.addEventListener("dataavailable", event => {
        if (event.data.size > 0 && recording) {
            if (recording) { // If still recording, restart immediately
                mediaRecording = 0;
                mediaRecorder.stop; // Record in 10-second chunks
                setupRecorder(stream);
            }
            processQueue(event.data); // Pass episodeId to function
        }
    });

    mediaRecorder.onstop = () => {
        console.log("Recorder stopped.");
        if (isRecording) {
            console.log("Restarting recording...");
        }
    };
}

function handleDataAvailable(event) {
    if (event.data.size > 0) {
        enqueueAudio(event.data); // Changed to enqueue audio for processing
    }
}

function enqueueAudio(blob) {
    audioQueue.push(blob);
    if (!processingAudio) {
        processQueue();
    }
}

function processQueue() {
    if (audioQueue.length === 0) {
        processingAudio = false;
        return;
    }
    
    processingAudio = true;
    const blob = audioQueue.shift();
    sendAudioToServer(blob);
}

function sendAudioToServer(blob) {
    // Basic check for valid blob size (avoid empty blobs)
    if (!blob || blob.size === 0) {
      console.log("Empty audio blob detected. Skipping sending.");
      processingAudio = false;
      processQueue(); // Process next item in the queue
      return;
    }
  
    // Check for content type (MIME type) if available
    if (blob.type && !blob.type.startsWith('audio/')) {
      console.log("Invalid audio file type:", blob.type, ". Skipping sending.");
      processingAudio = false;
      processQueue(); // Process next item in the queue
      return;
    }
  
    // Basic silence detection
    const audioContext = new AudioContext();
    const reader = new FileReader();
    reader.onload = () => {
      const arrayBuffer = reader.result;
      audioContext.decodeAudioData(arrayBuffer, (decodedAudio) => {
        const channelData = decodedAudio.getChannelData(0); // Assuming single channel audio
  
        let isSilent = true;
        for (let i = 0; i < channelData.length; i++) {
          if (Math.abs(channelData[i]) > SILENCE_THRESHOLD) {
            isSilent = false;
            break;
          }
        }
  
        if (isSilent) {
          console.log("Audio clip appears silent. Skipping sending.");
          processingAudio = false;
          processQueue(); // Process next item in the queue
          return;
        } else {

            const currentAudioHash = simpleHash(blob); // Hash the blob directly
            const currentTime = Date.now(); // Get current timestamp
        
            // Check for duplicate based on hash and time difference
            if (currentAudioHash === lastAudioHash && currentTime - lastAudioSentTime < MIN_TIME_GAP) {
            console.log("Duplicate audio detected (within time threshold). Skipping...");
            processingAudio = false;
            processQueue(); // Process next item in the queue
            return;
            }

            lastAudioHash = currentAudioHash;
            lastAudioSentTime = currentTime;
        
            lastAudioHash = currentAudioHash;
        
            const reader = new FileReader();
            reader.onloadend = () => {
            const base64AudioMessage = reader.result.split(',')[1];
        
            console.log("Sending new audio data to background for transcription...");
        
            chrome.runtime.sendMessage({
                command: "transcribeAudio",
                audioData: reader.result
            }, response => {
                if (chrome.runtime.lastError) {
                console.error("Error sending audio data:", chrome.runtime.lastError.message);
                } else {
                console.log("Transcription response received:", response);
                sendTranscription(response);
                }
            });
            };
        
            // Read data only if not a duplicate
            reader.readAsDataURL(blob);
        
      }
    }, (error) => {
      console.error("Error decoding false audio file (ignore this and the DOM Exception):", error);
      processingAudio = false;
      processQueue(); // Process next item in the queue
      return;
    });
  };
  reader.readAsArrayBuffer(blob);

  // Silence detection constants (adjust as needed)
  const SILENCE_THRESHOLD = 0.001; // Adjust based on your audio levels and noise floor
  }
  
  // Function to compute a simple hash from a string
  function simpleHash(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
  }
  
  function sendTranscription(transcription) {
    console.log("Sending transcription to background...");
    chrome.runtime.sendMessage({
        command: "sendTranscription",
        transcription: transcription
    }, response => {
        if (chrome.runtime.lastError) {
            console.error("Error sending transcription data:", chrome.runtime.lastError.message);
        } else {
            console.log("Transcription response received:", response);
        }
    });
}

async function stopRecording() {
    if (mediaRecorder && mediaRecorder.state === 'recording') {
        mediaRecorder.stop();
    }
    isRecording = false;
    recording = false;
    audioContext.close(); // Clean up the audio context
    audioContext = new AudioContext(); // Reinitialize the audio context
    mixedStream = null; // Reset the mixed stream
}

function isStreamActive(stream) {
    return stream && stream.active;
}

After implementing some filters for this issue, the transcription happens as needed, however, because the duplication is ongoing, it keeps doubling the processes, even though they’re being exited it not used.

This uses a LOT of memory on the user’s computer, leading it to crash in a few minutes.

I can’t make the chunks bigger to reduce process duplication as the transcription needs to be pretty stable. The website adds time stamps according to when the audio transcription is added, so a streaming voice-to-text won’t work. So I have to do the chunking.

I’ve tried for days to fix this, but it either does this, or doesn’t transcribe past the first audio chunk when I implement any safeguards other than the below.

I’m sorry for the amount of code, but I want you to have all of the context to see what could be wrong.

I can’t have it request premission every time it loops for another chunk to record, because this won’t allow seamless transcription, as the user will keep having to select the screen sharing options and approve, which is a terrible UX.

Fatal error: Uncaught ArgumentCountError: Too few arguments to function twig_split_filter(), 2 passed

i’working on a project php/twig and i got problem in my template while calling the split function of Twig
here is my admin.twig

{% set safeties = ["allg", "labor", "s1"] %}
{% set suArrRequire = ("1" ~ data.pflichtSU)|split('') %}
{% for safety in safeties %}
    <div class="col-xs-4 col-sm-3">
        <div class="btn-group btn-group-toggle" data-toggle="buttons">
            <label class="btn btn-secondary radio-inline {% if suArrRequire[loop.index0] == 1 %}active{% endif %}" for="yes{{ loop.index0 }}">
                <input type="radio" name="data[suRequire][{{ safety }}]" id="yes{{ loop.index0 }}" value="1" autocomplete="off" {% if suArrRequire[loop.index0] == 1 %}checked{% endif %}><span class="tr" key="yes"></span>
            </label>
            <label class="btn btn-secondary radio-inline {% if suArrRequire[loop.index0] == 0 %}active{% endif %}" for="no{{ loop.index0 }}">
                <input type="radio" name="data[suRequire][{{ safety }}]" id="no{{ loop.index0 }}" value="0" autocomplete="off" {% if suArrRequire[loop.index0] == 0 %}checked{% endif %}><span class="tr" key="no"></span>
            </label>
        </div>
    </div>
{% endfor %}
</div>

and here is the function from Twig class CoreExtension.php:

function twig_split_filter(Environment $env, $value, $delimiter, $limit = null)
{
    if (strlen($delimiter) > 0) {
        return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
    }

    if ($limit <= 1) {
        return preg_split('/(?<!^)(?!$)/u', $value);
    }

    $length = mb_strlen($value, $env->getCharset());
    if ($length < $limit) {
        return [$value];
    }

    $r = [];
    for ($i = 0; $i < $length; $i += $limit) {
        $r[] = mb_substr($value, $i, $limit, $env->getCharset());
    }

    return $r;
}

    public function getFilters(): array
    {
        return [
            // formatting filters
            new TwigFilter('date', 'twig_date_format_filter', ['needs_environment' => true]),
            new TwigFilter('date_modify', 'twig_date_modify_filter', ['needs_environment' => true]),
            new TwigFilter('format', 'sprintf'),
            new TwigFilter('replace', 'twig_replace_filter'),
            new TwigFilter('number_format', 'twig_number_format_filter', ['needs_environment' => true]),
            new TwigFilter('abs', 'abs'),
            new TwigFilter('round', 'twig_round'),

            // encoding
            new TwigFilter('url_encode', 'twig_urlencode_filter'),
            new TwigFilter('json_encode', 'json_encode'),
            new TwigFilter('convert_encoding', 'twig_convert_encoding'),

            // string filters
            new TwigFilter('title', 'twig_title_string_filter', ['needs_environment' => true]),
            new TwigFilter('capitalize', 'twig_capitalize_string_filter', ['needs_environment' => true]),
            new TwigFilter('upper', 'twig_upper_filter', ['needs_environment' => true]),
            new TwigFilter('lower', 'twig_lower_filter', ['needs_environment' => true]),
            new TwigFilter('striptags', 'strip_tags'),
            new TwigFilter('trim', 'twig_trim_filter'),
            new TwigFilter('nl2br', 'nl2br', ['pre_escape' => 'html', 'is_safe' => ['html']]),
            new TwigFilter('spaceless', 'twig_spaceless', ['is_safe' => ['html']]),

            // array helpers
            new TwigFilter('join', 'twig_join_filter'),
            new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]),
            new TwigFilter('sort', 'twig_sort_filter'),
            new TwigFilter('merge', 'twig_array_merge'),
            new TwigFilter('batch', 'twig_array_batch'),
            new TwigFilter('column', 'twig_array_column'),
            new TwigFilter('filter', 'twig_array_filter'),
            new TwigFilter('map', 'twig_array_map'),
            new TwigFilter('reduce', 'twig_array_reduce'),

            // string/array filters
            new TwigFilter('reverse', 'twig_reverse_filter', ['needs_environment' => true]),
            new TwigFilter('length', 'twig_length_filter', ['needs_environment' => true]),
            new TwigFilter('slice', 'twig_slice', ['needs_environment' => true]),
            new TwigFilter('first', 'twig_first', ['needs_environment' => true]),
            new TwigFilter('last', 'twig_last', ['needs_environment' => true]),

            // iteration and runtime
            new TwigFilter('default', '_twig_default_filter', ['node_class' => DefaultFilter::class]),
            new TwigFilter('keys', 'twig_get_array_keys_filter'),
        ];
    }

the Error i get is
enter image description here

i tried to wrok on the parametrs but still get the same problem

Cookies are empty inside getServerSideProps when in production but works on development(localhost)?

/pages/post/[id].tsx

export async function getServerSideProps(context: GetServerSidePropsContext) {
  const { id } = context.params as Params
  const { req, res } = context
  //this cookie is undefined in vercel deployment
  const { cookie } = req?.headers

  const parsedCookie = cookie && parse(cookie as string)
  res.setHeader('Cache-Control', 's-maxage=20, stale-while-revalidate')
 
    const postDetails = await getPostDetails(id as string)

    if (parsedCookie) {
      return {
        props: {
          postDetail: postDetails,
          cookie: parsedCookie
        }
      }
    } else {
//this statement runs in deployed site because of no cookies
      return {
        props: {
          postDetail: postDetails,
          cookie: {
            accessToken: '',
            refreshToken: ''
          }
        }
      }
    }
}

This is how I am setting http only cookie from my separate express api.

//server/auth.ts

   const environment = process.env.NODE_ENV // it is "production" in .env file of production

   res.cookie('refreshToken', refreshToken, {
          httpOnly: true,
          secure: environment === 'production',
          sameSite: environment === 'production' ? 'none' : 'lax',
          path: '/'
        })
        res.cookie('accessToken', accessToken, {
          httpOnly: true,
          secure: environment === 'production',
          sameSite: environment === 'production' ? 'none' : 'lax',
          path: '/'
        })

I am using NextJs pages and separate express backend. In local development, the cookies are available inside ‘getServerSideProps” and can access it. And I can see accessToken from it to my backend through axios.

But the deployed site on vercel has empty or undefined cookie “req.headers.cookie” while trying to access inside getServerSideProps in production. But yeah, with client side fetching even in vercel deployed site, I can see access token being available to backend. So, no problem of cookie not being set on deployed frontend domain. Just that there is cookies are undefined only inside getServerSideProps in vercel deployment.

What might be the issue? Am I missing something? Has anyone encountered this issue? I tried all methods of setting path to “/” and all others but still no luck

In local development, cookies are being populated in getServerSideProps as seen in the picture
enter image description here

Empty or undefined cookie just in vercel deployed domain(production) that’s why second else statement ran

enter image description here

jQuery filter results with checkboxes

I have the below code for a simple checkbox filter. It works well if all conditions are met with the checkboxes, i.e. Monday or Tuesday are checked. But checking both Monday and Tuesday gives no results. I would like it to show results that have a Monday or a Tuesday but can’t adapt what i have. Any help massively appreciated!

            <form id="filter">
                <h3>Filter</h3>
                <h4>Day</h4>
                <div class="checkDays"><input class="" type="checkbox" value="Monday" /> Monday</div>
                <div class="checkDays"><input class="" type="checkbox" value="Tuesday" /> Tuesday</div>
                <div class="checkDays"><input class="" type="checkbox" value="Wednesday" /> Wednesday</div>
                <div class="checkDays"><input class="" type="checkbox" value="Thursday" /> Thursday</div>
                <div class="checkDays"><input class="" type="checkbox" value="Friday" /> Friday</div>
                <div class="checkDays"><input class="" type="checkbox" value="Saturday" /> Saturday</div>
                <div class="checkDays"><input class="" type="checkbox" value="Sunday" /> Sunday</div>
                <h4>Time</h4>
                <div class="checkTime"><input class="" type="checkbox" value="AM" /> AM</div>
                <div class="checkTime"><input class="" type="checkbox" value="PM" /> PM</div>
                <div class="checkTime"><input class="" type="checkbox" value="ALL DAY" /> ALL DAY</div>
            </form>

            <div class="noresults"></div>
            <?php $loop = new WP_Query(array('post_type' => 'session', 'posts_per_page' => 50)); while ( $loop->have_posts() ) : $loop->the_post(); ?>
            <div class="card" data-combined="<?php the_field('AM/PM');?>,<?php $field = get_field_object('day'); $days = $field['value']; if($days): foreach( $days as $day ): echo ',' . $field['choices'][$day]; endforeach; endif; ?>">
            </div>

            <script>
            $('form#filter').on('change', function () {

                var filterList = [];

                $("form#filter .checkDays input:checked").each(function () {
                    var dataDays = $(this).val();
                    filterList.push(dataDays);
                });

                $("form#filter .checkTime input:checked").each(function () {
                    var dataTime = $(this).val();
                    filterList.push(dataTime);
                });

                if (filterList.length == 0) {
                    jQuery('.card').removeClass('is-hidden');
                    jQuery('.card').fadeIn();

                } else {

                    jQuery('.card').filter(function () {
                        const isVisible = ['data-combined'].some(attr => {
                            const attrValue = jQuery(this).attr(attr);
                            if (!attrValue) {
                                return;
                            }
                            const offerings = attrValue.split(',');
                            return filterList.every(offering => offerings.indexOf(offering) > -1);
                        });

                        if (isVisible) {
                            jQuery(this).fadeIn('slow');
                        } else {
                            jQuery(this).hide();
                        }
                    });
                }

            });
            </script>