Date with timezoneOffset

I have the following code that can give different output with -1 day depending on time zone (my coworker can get 03/30/2020 when he/she inputs 04/01/2020 when calling new Date(formatDate(value, 'MMM d, yyyy')). I use date-fns‘s format api

const formatDate = (date, fnsFormatStyle) => {
  const dt = new Date(date);
  const dtDateOnly = new Date(
    dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000
  );
  return format(dtDateOnly, formatStyle);
};

anyone knows why it’s doing that exactly and what would be the direction to fix ?

Passing a state argument set by useEffect to RTK Query’s queryFn

I’m passing in a boolean argument, usingAsSignUp, into the queryFn.

Unfortunately, usingAsSignUp always results in undefined! How do I get it’s values? usingAsSignUp is state set by useEffect in the consuming component.

RTK Query createApi with queryFn:

export const firebaseApi = createApi({
  reducerPath: "firebaseApi",
  baseQuery: fakeBaseQuery(),
  tagTypes: ["Auth"], //Optional, https://redux-toolkit.js.org/rtk-query/api/createApi#tagtypes
  endpoints: (builder) => ({
    authenticateWithFirebase: builder.mutation({
      async queryFn({ email, password, usingAsSignUp }) {
        try {
          const auth = getAuth(firebaseApp);
          const userCredential = usingAsSignUp ? 
          await createUserWithEmailAndPassword(auth, email, password) : 
          await signInWithEmailAndPassword(auth, email, password);
          return {
            data: {
              uid: userCredential?.user?.uid,
              email: userCredential?.user?.email,
              usingAsSignUp: usingAsSignUp,
            },
          };
        } catch (e) {
          return { error: e };
        }
      },
      providesTags: ["Auth"], //Optional, https://redux-toolkit.js.org/rtk-query/api/createApi#providestags
    }),
  }),
});

export const { useAuthenticateWithFirebaseMutation } = firebaseApi;

Consuming component using useEffect to set the state passed to queryFn:

  import { useAuthenticateWithFirebaseMutation } from "../../persistence/apiSlices";

  const [signup, setSignup] = useState(true);
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const location = useLocation();

  const [authenticateNow, result, data] = useAuthenticateWithFirebaseMutation();

  useEffect(() => {
    location.pathname === "/login" ? setSignup(false) : setSignup(true);
  }, [location.pathname] );

  async function onSubmitACB() {
    await authenticateNow({ email, password, signup });
  }

What’s the Raku equivalent of the super keyword as used in JavaScript and Python?

Whenever you extend a class in JavaScript or Python, the derived class must use the super keyword in order to set attributes and/or invoke methods and constructor in the base class. For example:

class Rectangle {
    constructor(length, width) {
        this.name = "Rectangle";
        this.length = length;
        this.width = width;
    }

    shoutArea() {
        console.log(
            `I AM A ${this.name.toUpperCase()} AND MY AREA IS ${this.length * this.width}`
        );
    }
    
    rectHello() {
        return "Rectanglish: hello";
    }
}

class Square extends Rectangle {
    constructor(length) {
        super(length, length);
        this.name = "Square"
    }
    
    squaHello() {
        const h = super.rectHello();
        return "Squarish:" + h.split(':')[1];
    }
}

const rect = new Rectangle(6, 4);
rect.shoutArea(); //=> I AM A RECTANGLE AND MY AREA IS 24

const squa = new Square(5);
squa.shoutArea(); //=> I AM A SQUARE AND MY AREA IS 25

console.log(squa.squaHello()); //=> Squarish: hello

Wait for a certain time before checking if the api endpoint was invoked or not using cy.intercept

I have a scenario where I need to check if the api endpoint was invoked or not.

I tried to use this code but it seems like it is getting always nothing

`let interceptFlag = false

 cy.intercept(`**PATH/${search}/trigger`, (req) => {
   interceptFlag = true
   req.continue((res) => {
      interceptFlag = true
   })
 })

 cy.wrap(interceptFlag).should('eq', true)`

I even tried this but always getting nothing
cy.intercept(**PATH/${search}/trigger, cy.spy().as("getSMSCall")); cy.get('@getSMSCall').should('have.been.called');

I am thinking that there is a delay. May I know how can I put some delay there to wait for the endpoint be invoked and that delay should also be working in the opposite scenario as well, which is api endpoint should not be called

How do I utilize execute_script method using Selenium to print dynamic data?

So for the last two weeks I picked up programming again, and I thought it would be fun to learn web-scraping. The first project I wanted to try was to scrape Cryptocurrency prices from Binance. I first tried Beautifulsoup, however that library can’t access Javascript so I find it quite limited. I then moved on to Selenium and found that you cant actually write Javascript using execute_script driver method.

But here comes the Caviar. If you log onto Binance and head over to the BTC/USDT pair and select the 1 second chart, you will see that the price is constantly updating. I tried a few methods to scrape and print the 1 second, first I tried Beautifulsoup which didn’t work, I then tried using their Api, which works great, but unfortunately the api is limit to only display the average price per 5 min, which isn’t what I want. Working Api code below for those that are interested ↓

import requests
import time

url_btc = 'https://api.binance.com/api/v3/avgPrice?symbol=BTCUSDT'
url_eth = 'https://api.binance.com/api/v3/avgPrice?symbol=ETHUSDT'

while True:
    response_btc = requests.get(url_btc)
    response_eth = requests.get(url_eth)
    data_btc = response_btc.json()
    data_eth = response_eth.json()
    print("Current price for Bitcoin is: " + data_btc['price'] + "n" + "And the current price for Etherium is: " + data_eth['price'])

    time.sleep(1)

    #While this utilizes the api, it only gets the avg price per 5min. That's why you don't see the price change very much.  The point is the scrape the live price.
   

Moving on, so from a lot of Googling and reading some documentation I found execute_script() which apparently can get the live 1 second price from Binance and print it to console since Binance uses Javascript to update that price. However I don’t know how I can use the Javascript to retrieve that 1 second price. Below is some more information for convenience’s sake. Thanks in advance 🙂

Link: https://www.binance.com/en/trade/BTC_USDT?_from=markets&theme=dark&type=spot

Link to documentation: https://www.selenium.dev/selenium/docs/api/py/api.html

Element I’m trying to scrape ↓

<div class="showPrice" style="color: rgb(14, 203, 129);">30,455.26</div>
  • Tried Beautifulsoup
  • Tried Api
  • Tried using Selenium and figuring it out, but can’t
  • Tried reading documentation

I tried to fill out a form my employer sent me through email several times but snagajob.com would not let me sign in when I clicked the sign in button

Why is the adblock error: net::ERR_BLOCKED_BY_ADBLOCKER preventing the JavaScript from loading?

An employer sends me an email to fill out a tax form on snagajob.com. I attempt to sign in. Upon clicking on the sign in button the website becomes unresponsive. However I used Google Chrome and I was able to login in and fill out the document. So there is an adblock error preventing the JavaScript from being seen on Opera and Microsoft Edge but not on Google Chrome.

I tried multiple browsers including Microsoft Edge and Google Chrome. The only browser that has allowed me to sign in to do the form is Google Chrome. Microsoft does the exact some thing Opera does it just becomes completely unreactive. In response to the website becoming buggy I opened up the Developer tool built into browsers. I was searching to see what the problem could be. I found a ADBlock error that was preventing the script from registering properly.
Image of the Error Inside of the Developer Tool

I went to snagajob.com expecting to login and fill out a form.

New to react and getting data from axios

I know my data exists and I can see it within the async function but when I pass it out its just the promise.

Two part question; how do I pass this data outside of the async function and how do I use within my react component? I considered just using handlebars but I have been wanting to figure out react. Please view my codepen.

https://codepen.io/rachelCoder/pen/zYMKPGY?editors=1011

Just trying loop through the data. I know how to do with jquery and xhttp requests. Fighting the async / promise function.

const Books = () => {
    const [bookData, setBookData] = React.useState('');
    async function getDat () {
   const res = await fetch('https://raw.githubusercontent.com/coderxdesigner/bookstore/main/src/json/childrens.json');
   const data = await res.json();
     return data;
}
    return (
        <div className="row">
            <h1>Books</h1>
            {console.log(data.book.map(itm => itm.title))}          
        </div>
        
    )
}

Fetching Data from Multiple APIs and Handling Errors in Node.js

I’m working on a Node.js application that needs to fetch data from multiple APIs concurrently. Each API may respond with varying data structures, and I need to handle errors gracefully while ensuring all requests complete successfully. The following code shows an example of how I’m attempting to achieve this using async/await and Promise.all. However, I’m facing some unexpected issues with error handling and data processing. Can someone help me identify the problem and propose a better approach?

async function fetchDataFromApis(apiUrls) {
  try {
    const promises = apiUrls.map(async (url) => {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`API request failed with status: ${response.status}`);
      }
      return await response.json();
    });

    const results = await Promise.all(promises);

    // Data processing logic here...

    return results;
  } catch (error) {
    // Handle errors here...
  }
}

const apiUrls = [
  'https://api.example.com/data',
  'https://api.anotherexample.com/data',
  'https://api.yetanotherexample.com/data',
];

fetchDataFromApis(apiUrls)
  .then((data) => console.log('Data:', data))
  .catch((error) => console.error('Error:', error.message));

Please note that the code provided is a simplified version of my actual implementation. I need assistance with understanding how to correctly handle errors and ensure that all API requests are processed successfully, despite their varying data structures. Your insights are much appreciated!

Web component doesn’t listen to event emitted by other component

console.log doesn’t get executed because componentB does not receive the event it is listening to from componentA.

class componentA extends HTMLElement {
    connectedCallback() {
        this.dispatchEvent(new CustomEvent('custom-event', {
            bubbles: true,
            composed: true
        }));
    }
}
  
window.customElements.define('component-a', componentA);
  
class componentB extends HTMLElement {
    connectedCallback() {
        document.addEventListener('custom-event', (event) => {
            console.log('ok');
        });
    }
}

window.customElements.define('component-b', componentB);
  

I tried to replace this.dispatchEvent with document.dispatchEvent but it did’nt change the output. Note that I don’t get any error messages.

load event doesn’t wait till react containers are rendered (difference in Firefox and Chromium)

I have a script that needs access to elements that are being rendered by react.
But in the onload event on firefox the last element rendered by React is not available, on Chromium it is. When the webpack mode is set to production there is no problem.

Here is a simplified example

HTML:

<textarea></textarea>
<div class="textAreaContainer"></div>
<textarea></textarea>

<!--This one is not available on firefox-->
<div class="textAreaContainer"></div>

<textarea></textarea>

<!--React script that renders the textareas-->
<script src="react/textAreas.js"></script>
<script>
    window.addEventListener("load", () => {
        const textAreas = document.getElementsByTagName("textarea");
        // The last textarea rendered by React is not available in Firefox
        console.log([...textAreas]);
    });
</script>

React (index.js):

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const containers = document.getElementsByClassName("textAreaContainer");

for (const container of containers) {
    const root = ReactDOM.createRoot(container);

    root.render(
        <React.StrictMode>
            <App />
        </React.StrictMode>
    );
}

React (App.js):

import React from "react";
function App() {
  return (
      <textarea name="from-react" cols="30" rows="10"></textarea>
  );
}

export default App;

webpack.config.js:

const path = require('path');

module.exports = {
    mode: "development",
    entry: {
        textAreas: './react/index.js',
    },
    output: {
        filename: "[name].js",
        path: path.join(__dirname, "public/react")
    },
    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader",
                }
            }
        ]
    }
};

.babelrc:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime"]
}

package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "build": "webpack"
  },
  "keywords": [],
  "author": "Laurent Dhont (LDIT BV)",
  "license": "ISC",
  "dependencies": {
    "@babel/core": "^7.22.6",
    "@babel/plugin-proposal-export-default-from": "^7.22.5",
    "@babel/plugin-transform-runtime": "^7.22.6",
    "@babel/preset-env": "^7.22.6",
    "@babel/preset-react": "^7.22.5",
    "babel-loader": "^9.1.2",
    "babel-preset-react-app": "^10.0.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "webpack": "^5.88.1",
    "webpack-cli": "^5.1.4"
  }
}

Avoid running createEffect twice in solidjs

I have a table made with solidjs. A simplified version of my code is:

table.tsx

const Table = () => {
    const [page, setPage] = createSignal(1);
    const [currentRows, setCurrentRows] = createSignal([]);
    const [tableOptions, setTableOptions] = createSignal({});

    let worker;
    onCleanup(() => worker?.terminate());

    createEffect(() => {
        if (worker instanceof Worker) return;
        worker = new Worker("./myWorker.ts");
        worker.onmessage = e => {
            const {rows} = e
            setCurrentRows(rows);
            setPage(1);   
                          
        }
    })

    createEffect(() => {
        const currPage = page();
        const currOptions = tableOptions();
        
        worker.postMessage({currPage, currOptions})
    })

    return (
        <div>
            <TableOptionsForm r={tableOptions} w={setTableOptions}/>
            <table>
                <For each{currentRows()}>
                    {rowData => <MyTableRow {...rowData} />}
                </For> 
            </table>
            <button onClick={() => {setPage(page() - 1)}}>Page-</button>
            <div>Page {page()}</div>
            <button onClick={() => {setPage(page() + 1)}}>Page+</button>
        </div>
    )
}

myWorker.ts

onmessage = async (m) = > {
    const {currPage, currOptions} = m.data;
    const allRows = await callSomeAPIsAndDoExpensiveComputations(currOptions);
    const rows = allRows.slice(currPage, currPage + 20);
    postMessage(rows);
}

myWorker.ts calculates the rows that the table is showing at any given moment, using tableOptions and page. It will be called when the user changes the page he wants to see or when the user modifies the table options.

I want to reset to page 1 anytime the user changes any table options, but I want to avoid calling the worker two times, I would like to pass him the page number and the new table options in one step, so it only have to calculate the new rows to show once.

By the way, I only want to store in currentRows the rows that are being showed in the current page. I cannot store all the rows for all the pages in that variable because there are tens of thousands or even more of them and it would be too much.

I’m not really sure how to approach this functionality, any help will be welcome.

why is the large gif image causing the app to flicker?

There is electron js application + react. Necessary show many gif images, but on some devices observed flickering screen.
This was fixed on windows by adding the line app.commandLine.appendSwitch('force_high_performance_gpu');. The problem is seen on iMac. Graphic/Display:

Radeon Pro 575:

  Chipset Model: Radeon Pro 575
  Type: GPU
  Bus: PCIe
  PCIe Lane Width: x16
  VRAM (Total): 4 GB
  Vendor: AMD (0x1002)
  Device ID: 0x67df
  Revision ID: 0x00c4
  ROM Revision: 113-D000AA-931
  VBIOS Version: 113-D0001A1P-025
  EFI Driver Version: 01.00.931
  Metal: Supported, feature set macOS GPUFamily2 v1
  Displays:
    iMac:
      Display Type: Built-In Retina LCD
      Resolution: 5120 x 2880 Retina
      Framebuffer Depth: 30-Bit Color (ARGB2101010)
      Main Display: Yes
      Mirror: Off
      Online: Yes
      Rotation: Supported
      Automatically Adjust Brightness: No

How can I page through an e-Book using WKWebView

I’ve danced around this question previously, asking specific questions which I hoped might lead me to the next step in solving the larger problem. That didn’t work (no answers), so this is the larger problem.

Original questions
How can I ignore all touch events in WKWebView and its enclosingScrolllView

How can I paginate html in a wkwebview?

Context

Apple originally provided a very full featured and functional API for displaying HTML – WebView. Many tools were written for WebView, for example to display the pages of an e-book.
WebView is now deprecated in favour of WKWebView, which ‘feels’ minimally functional – a lot of the features that made WebView easy to work with don’t exist in WebView, and I can’t find any tools for displaying the pages of an e-Book neatly.

I want to produce a tool which can be shared publicly on GitHub which will provide this e-book functionality, both so that I can benefit from it myself – but also so that everyone else can.

Problem

At minimum, this needs to work on macOS (I’ll worry about iOS later)

So far, my code scrolls the book content neatly one page at a time if you press the buttons – but if you swipe on the mouse or trackpad (guessing here) acceleration ruins the functionality (it should work in the same way as pressing the buttons). I can get it so that a swipe will move the book content along by one page – but once the page has been updated the acceleration / deceleration effect of the swipe continues – and the page moves on past the point where it should have stopped moving.

The page scroll is horizontal.

The code I have so far is

//
//  ReaderWindowController.m
//

#import "ReaderWindowController.h"
#import "LibrarianFormatPluginInterface.h"

#define WindowSideLeft 0
#define WindowSideRight 1

@interface WKWebView (SynchronousEvaluateJavaScript)
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
@end

@implementation WKWebView (SynchronousEvaluateJavaScript)

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script {
    __block NSString *resultString = nil;
    __block BOOL finished = NO;
    
    [self evaluateJavaScript:script completionHandler:^(id result, NSError *error) {
        if (error == nil) {
            if (result != nil) {
                resultString = [NSString stringWithFormat:@"%@", result];
            }
        } else {
            NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
        }
        finished = YES;
    }];
    
    while (!finished) {
        [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
    }
    
    return resultString;
}

- (void)scrollWheel:(NSEvent *)event {
    NSLog(@"Scrolled wheel");
}

- (BOOL)scrollRectToVisible:(NSRect)rect {
    NSLog(@"Scroll rect to visible %f x %f , %f x %f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
    
    return true;
}

- (void)scrollClipView:(NSClipView *)clipView toPoint:(NSPoint)point {
    NSLog(@"Scroll clip view %@ to point %f x %f", clipView, point.x, point.y);
}

- (NSRect)adjustScroll:(NSRect)newVisible {
    NSRect modifiedRect=newVisible;
    
    // snap to 72 pixel increments
    modifiedRect.origin.x = (int)(modifiedRect.origin.x/72.0) * 72.0;
    //        modifiedRect.origin.y = (int)(modifiedRect.origin.y/72.0) * 72.0;
    
    // return the modified rectangle
    return modifiedRect;
}

- (void)scrollRangeToVisible:(NSRange)range {
    NSLog(@"Scroll range to visible");
}

- (void)scrollPoint:(NSPoint)point {
    NSLog(@"Scroll point to visible");
}

- (void)reflectScrolledClipView:(NSClipView *)clipView {
    NSLog(@"reflectScrolledClipView point to visible");
}

@end


@interface NSView ( TouchEvents )

@end

@implementation NSView ( TouchEvents )

float beginX, endX;


- (void)touchesBeganWithEvent:(NSEvent *)event {
    if(event.type == NSEventTypeGesture){
        NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self];
        if(touches.count == 2){
            for (NSTouch *touch in touches) {
                beginX = touch.normalizedPosition.x;
            }
        }
    }
}

- (void)touchesEndedWithEvent:(NSEvent *)event {
    
    if(event.type == NSEventTypeGesture){
        NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self];
        NSDictionary* userInfo;
        if(touches.count == 2){
            for (NSTouch *touch in touches) {
                endX = touch.normalizedPosition.x;
            }
            // since there are two touches, endX will always end up with the data from the second touch
            
            if (endX > beginX) {
                NSLog(@"swipe right!");
                userInfo = @{@"direction": @(WindowSideRight)};
            }
            else if (endX < beginX) {
                NSLog(@"swipe left!");
                userInfo = @{@"direction": @(WindowSideLeft)};
            }
            else {
                NSLog(@"no swipe!");
            }
            
            [NSNotificationCenter.defaultCenter postNotificationName:@"pageScrollEvent" object:nil userInfo:userInfo];
        }
    }
    
}

- (void)scrollWheel:(NSEvent *)event {
    NSLog(@"user scrolled %f horizontally and %f vertically", [event deltaX], [event deltaY]);
}

@end


@interface ReaderWindowController ()

@end

@implementation ReaderWindowController

- (void)createButtonOnSide:(int)side withSelector:(SEL)aSelector {
    int x = 0, y = 100, width = 40, height = 230;
    NSRect framesize = NSMakeRect(x, y, width, height);
    
    NSString* label = side==WindowSideLeft?@"<":@">";
    
    NSButton *myButton = [NSButton.alloc initWithFrame:CGRectZero];
    [myButton setButtonType:NSButtonTypeMomentaryPushIn];
    if (@available(macOS 11.0, *)) {
        NSImage* arrow = side==WindowSideLeft?[NSImage imageWithSystemSymbolName:@"arrowshape.left.fill" accessibilityDescription:label]:[NSImage imageWithSystemSymbolName:@"arrowshape.right.fill" accessibilityDescription:label];
        [myButton setImage:arrow];
    } else {
        [myButton setTitle:label];
    }
    [myButton setBezelStyle:NSBezelStyleTexturedSquare];
    [myButton setTarget:self];
    [myButton setAction:aSelector];
    [myButton setTag:side];
    
    myButton.translatesAutoresizingMaskIntoConstraints = false;
    [self.window.contentView addSubview:myButton];
    [myButton.widthAnchor constraintEqualToConstant:framesize.size.width].active = YES;
    [myButton.heightAnchor constraintEqualToConstant:framesize.size.height].active = YES;
    [myButton.centerYAnchor constraintEqualToAnchor:self.window.contentView.centerYAnchor].active = YES;
    if (side == WindowSideLeft) {
        [myButton.leadingAnchor constraintEqualToAnchor:self.window.contentView.leadingAnchor constant:0].active = YES;
    } else {
        [myButton.trailingAnchor constraintEqualToAnchor:self.window.contentView.trailingAnchor constant:0].active = YES;
    }
    
    NSTrackingArea* trackingArea = [NSTrackingArea.alloc
                                    initWithRect:myButton.bounds
                                    options: NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways
                                    owner:self userInfo:nil];
    [self.window.contentView addTrackingArea:trackingArea];
}

- (void)mouseEntered:(NSEvent *)theEvent{
    NSLog(@"entered");
}

- (void)mouseExited:(NSEvent *)theEvent{
    NSLog(@"exited");
}

- (void)windowDidLoad {
    [super windowDidLoad];
    [self.window setDelegate:self];
    
    [bookPages setAllowedTouchTypes:(NSTouchTypeMaskDirect | NSTouchTypeMaskIndirect)];
    
    [self.window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]];
    
    [bookPages setNavigationDelegate:self];
    pageCount = 0; // might want to load this from preferences
    
    [NSNotificationCenter.defaultCenter addObserver:self
                                           selector:@selector(loadDidFinish:)
                                               name:@"LoadDidFinishNotification"
                                             object:nil];
    [NSNotificationCenter.defaultCenter addObserver:self
                                           selector:@selector(buttonPressed:)
                                               name:@"PageScrollEvent"
                                             object:nil];
    
    [self createButtonOnSide:WindowSideLeft withSelector:@selector(buttonPressed:)];
    [self createButtonOnSide:WindowSideRight withSelector:@selector(buttonPressed:)];
    
}

- (id)initWithBookPlugin:(id)bookPlug andWindowController:(NSNibName)windowNibName {
    if (bookPlug && ![[bookPlug className] isEqualToString:[NSNull className]] && (self = [super initWithWindowNibName:windowNibName])) {
        bookPlugin = bookPlug;
    }
    return self;
}

- (void)loadDidFinish:(NSNotification*)notification {
    NSURLRequest* thisRequest = [bookPlugin getURLRequestForIndex:8];
    [bookPages loadRequest:thisRequest];
}

- (void)windowWillClose:(NSNotification *)notification {
    [NSNotificationCenter.defaultCenter removeObserver:self];
    if (bookPlugin) { bookPlugin = nil; }
}

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSString *cssString = @"body { overflow: -webkit-paged-x !important; direction: ltr !important; -webkit-overflow-scrolling: touch; scroll-snap-type: x mandatory;  scroll-snap-align: center; }";
    NSString *javascriptString = @"var style = document.createElement('style'); style.innerHTML = '%@'; document.head.appendChild(style)";
    NSString *javascriptWithCSSString = [NSString stringWithFormat:javascriptString, cssString];
    [webView evaluateJavaScript:javascriptWithCSSString completionHandler:nil];
}

-(NSSize)getViewDimensionsForwebView:(WKWebView *)webView {
    NSString* width = [webView stringByEvaluatingJavaScriptFromString:@"Math.max( document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth )"];
    NSString* height = [webView stringByEvaluatingJavaScriptFromString:@"Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight )"];
    
    return NSMakeSize(width.floatValue,height.floatValue);
}

- (void)buttonPressed:(id)sender {
    if ([[sender className] isEqualToString:@"NSButton"]) {
        if ([sender tag] == WindowSideLeft) { pageCount--; } else { pageCount++; }
    } else if ([[sender className] isEqualToString:@"NSConcreteNotification"]) {
        if ([[sender userInfo][@"direction"] isEqualTo: @(WindowSideLeft)]) { pageCount--; } else { pageCount++; }
    }
    
    pageCount = pageCount<0?0:pageCount;
    NSInteger pageWidth = self.window.contentView.frame.size.width;
    
    NSString* jsString = [NSString stringWithFormat:@"window.scrollTo({top: 0, left: %ld, behavior: "smooth",});", pageWidth * pageCount];
    [bookPages evaluateJavaScript:jsString completionHandler:nil];
}

@end

I’d love to hear what I’m doing wrong here – but I’d also be happy to hear of any project which does the same thing (provided that it doesn’t contain any deprecations)

Trouble displaying escaped text with Flask and JavaScript

So I’m trying to make a website whose front page consists of “cards”, each of which has a unique pop-up when clicked:

  <div class="format-rows">
    {% for row in cartelles|reverse %}
    <div><img class="card-image" src="{{ url_for('static', filename=row.img_url) }}">
      <div class="text-box">
        <p onclick='popupAction("{{row.full_text}}")'>{{row.question}}</p>
      </div>
    </div>
    {% endfor %}
  </div>
  <div id="grey-blur"></div>
  <div id="popup">
    <div>
      <button class="close" onclick="closePopup()"></button>
      <img id="popup-image"
        src="https://images.unsplash.com/photo-1543207564-1271b510019d?crop=entropy&cs=srgb&fm=jpg&ixid=MnwzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE2ODI3NjY4ODM&ixlib=rb-4.0.3&q=85">
    </div>
    <div id="popup-textbox"></div>
  </div>

The popupAction code being:

function popupAction(text){

  let popup = document.getElementById('popup');
  popup.classList.add('active');

  let greyBlur = document.getElementById('grey-blur');
  greyBlur.classList.add('active');

  let popupText = document.getElementById('popup-textbox');
  popupText.innerHTML = text;

}

I’ve escaped my code already on my app.py file as I retrieve it from my MySQL account, but when I attempt to display the "{{row.full_text}}", which is the following(in greek):

<span>Τί εν το αγαπημένο σας πράμα για τη ζωή σας στην Κύπρο;</span> <br><br> Λοιπον, το πιο αγαπημενο μου πραγμα στην Κυπρο εν το ότι μπορώ οποιαδήποτε στιγμή θελήσω να μπώ στο αυτοκηνιτο μου τζιαι να παω θάλασσα. Ειδικά τον χειμώνα. Εν το αγαπημένο μου κομμάτι της Κύπρου. <br><br> <span>Γιατί εν σου αρέσκει να ζεις στην Κύπρο;</span> <br><br> Το οτι εν μπορώ να πάω κάπου τζιαι να είμαι ανάμεσα σε αγνώστους. Δηλαδή ακόμα τζιαν εχω επιλογη να μεν συναντησω καποιο γνωστο.. το πιο πιθανο... εννοω ακομα τζιαι αν εχω αναγκη να ειμαι μονη μου εν θα μπορεσω να ειμαι καπου μονη μου. Συνηθως βρισκεις ατομα που τουσ ξερεις τζιαι ξερουν σε. Ναι, νομιζω εσχιει αναγκη ο ανδρωπος την...το να γινει αορατος μεσα στην πολη του. Τζιαι ισως το αλλο που με ενοχλει παρα πολλα τζιαι εν θεμα επιλογης δικης μου εν το κινητο. Το οτι με το κινητο εν εσχεις την ελευθερια να αρνηθεις να μιλησεις σε καποιον γιατι, επιαννα σε, εν μου απαντησες. Τζιαι εννα επιμενει να  πιασει τζιαι δευτερη φορα η να στιλει μυνημα, οποταν ναι, ακομα τζιαν θελεις να αποφυγεις καποιον, εν μπορεις ευκολα(...) Η σκεφτου πιο σοφον ηταν να ηταν τζινον που θα ελεγες, η ποσον ποσον πολλα ηταν να φκεννει που μεσα σου αν, αντι για να του μιλησεις που το κινητο, εγγραφες του μιαν επιστολη. Μπορει να επερνε μιαν εβδομαδα να παει τζιαι μιαν εβδομαδα ναρτει, ποσον ηταν να κατασταλαξουν τα συναισθηματα, γιατι, ξερεις, λεστο την βρα που εισαι μεστον θυμο, η μεσα στην θλιψη,  η μεσα στην απόγνωση, ενω μπορει δεκα λεπτα μετα να μενι νιωθεις ακριβως ετσι. Ενω αν τα εβαλλες σε λεξεις ναταν λίο διαφορετικα. Ναι, η αληθκεια, ισχυει. Τζιαι το αλλο που με ενοχλει με το κινητο ειναι το "που εισαι;" Εν σε αφορά ρε φίλε. Εννοώ, τι σημαινει "που εισαι"; [γελιο]"Τι κάμνεις;"[γελιο]. Ειμαστεν παντελως αδιακριτοι. <br><br>

the console log comes up with the following error:
SyntaxError: Unexpected EOF, pointing to this line:

")'>What&#39;s your favourite thing about living in Cyprus?</p>

referring to this line in my code:

<p onclick='popupAction("{{row.full_text}}")'>{{row.question}}</p>

I understand that there’s something weird going on with the quotes between the end of the input to my popupAction() function and the rest of the code,")'>, but I can’t figure out how to get JS/jinja to parse this properly. I’ve tried combinations of JSON.parse on JS and safe/escape/tojson on the ninja side, resulting again in incorrect parsing in one form or another. Can anyone help?