P5.JS createSelect() not changing selected option when called in draw function

I’m working on a class project to build a drawing app using p5.js. One of my features is a tool to draw different shapes and includes using a dropdown menu to select a shape to draw to the canvas. I’m using the createSelect() function to build the menu. It works fine and draws each shape option when used outside the draw function, but this means the dropdown menu is visible even when the shape tool isn’t selected. (Not allowed)

If I hide the createSelect within its own function and call that in the draw function, it hides the dropdown, but not will not select any option other than the default and no longer draws any of the shapes. I’ve been staring at this for hours and can’t figure out where I went wrong. The console isn’t showing any errors when I try to draw or select a different shape. Does anyone have any advice on where to start fixing this? Or what I’m missing?

Any help is greatly appreciated!

(I’m sorry for my messy code. I usually don’t bother cleaning up until I get something finished. I also think this text editor added extra comments? This is my first post…)

   //Draws various shapes to canvas based on user selections

function drawShapesTool(){
//set an icon and a name for the object
this.icon = "assets/shapes.jpg";
this.name = "shapes";


var previousMouseX = -1;
var previousMouseY = -1;
var w;
var h;
var shapeOptions = ["rectangle", "circle", "triangle", "rhombus"];
var selectedShape;     
var shapeSelect;

//Adapted from mirrorDrawTool and p5.js library 
this.shapePicker = function(){
    shapeSelect = createSelect();
    shapeSelect.position(350, 700);    
   
    shapeSelect.option('rectangle');
    shapeSelect.option('circle');
    shapeSelect.option('triangle');
    shapeSelect.option('rhombus');

   shapeSelect.selected('rectangle');
 };

this.draw = function(){
//      this.shapePicker();
    var shapeSelect = createSelect();
    shapeSelect.position(350, 700);    
   
    shapeSelect.option('rectangle');
    shapeSelect.option('circle');
    shapeSelect.option('triangle');
    shapeSelect.option('rhombus');

//       shapeSelect.selected('rectangle');
  selectedShape = shapeSelect.value();
  var drawRect = function(){
      rect(previousMouseX,previousMouseY, w, h);
  }
  var drawCircle = function(){
      ellipse (previousMouseX, previousMouseY, Math.max(w, h) * 1.5);
  }
        var drawTri = function(){
           triangle(mouseX, mouseY - h, mouseX - w, mouseY + h, mouseX + w, mouseY +h);                
        }
        var drawRhom = function(){
         quad(mouseX, mouseY, w / 2, h / 2, previousMouseX,previousMouseY, w * 0.5, h);                
        }
    
  if(mouseIsPressed){
      if (previousMouseX == -1 && previousMouseY == -1){
          previousMouseX = mouseX;
          previousMouseY = mouseY;
      }
      else {
        w = mouseX - previousMouseX;
        h = mouseY - previousMouseY;
        updatePixels();
            
          if (selectedShape == 'rectangle'){
              drawRect();
          }
          else if (selectedShape == 'circle'){
              drawCircle();
          }
          else if (selectedShape == 'triangle'){
              drawTri();
          }
          else if (selectedShape == 'rhombus') {
              drawRhom();
          }
      }
  }
    else {
      previousMouseX = -1;
      previousMouseY = -1;
      loadPixels();
    }              
 };

}

Not able to edit text inside Array dynamically, which is stored in localStorage

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Notes App</title>
  <script defer src="script.js"></script>
  <link rel="stylesheet" href="style.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
  <div class="container">
    <div class="add">
      <i class = 'fa-solid fa-plus'></i>
    </div>
    <div class="create-note">
      <h1 class = 'heading'>New Note</h1>
      <textarea name="" id="" placeholder="Enter your note..."></textarea>
      <button class = 'create-btn'>Create Note</button>
      <button class = 'close-btn'>Close</button>
    </div>
    <div class="edit-note">
      <h1 class = 'heading'>Edit Note</h1>
      <textarea name="" id="" class = 'textarea2'></textarea>
      <button class = 'edit-btn'>Edit Note</button>
      <button class = 'close-btn2'>Close</button>
    </div>
  </div>
</body>
</html>

* {
  margin: 0px;
  padding: 0px;
  box-sizing: border-box;
}

body {
  background-color: #8c53ff;
  height: 94vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.container {
  height: 600px;
  width: 890px;
  background-color: white;
  border-radius: 10px;
  display: flex;
  flex-wrap: wrap;
  overflow-y: auto;
}
.add {
  width: 237px;
  height: 241px;
  border: 1px solid #ddd;
  background-color: #f9f9f9;
  margin-left: 4%;
  margin-top: 3%;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 64px;
  color: gainsboro;
}
.add:hover {
  background-color: #f1f1f1;
}
.create-note {
  width: 441px;
  height: 316px;
  border: 1px solid #ccc;
  background-color: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  text-align: center;
  border-radius: 10px;
  position: absolute;
  top: 22%;
  left: 34%;
  display: none;
}
.heading {
  margin-top: 16px;
  font-size: 32px;
}

textarea {
  border: 2px solid #8c53ff;
  border-radius: 10px;
  width: 300px;
  height: 144px;
  margin-left: 5%;
  margin-top: 12px;
  padding: 10px;
}
.create-btn {
  width: 100px;
  height: 36px;
  margin-top: 18px;
  margin-left: 15px;
  background-color: #8c53ff;
  color: white;
}
.close-btn {
  width: 100px;
  height: 36px;
  background-color: gainsboro;
  color: white;
  margin-left: 10px;
}
.create-btn,.close-btn {
  border: none;
  border-radius: 14px;
  font-size: 14px;
}
.note {
  background-color: #fff385;
  height: 240px;
  width: 240px;
  margin: 25px;
  border-radius: 10px;
  overflow-y: auto;
  display: none;
}
.inner-note {
  height: 190px;
  width: 190px;
  overflow-y: auto;
  margin-left: 32px;
  margin-top: 26px;
  word-break: break-all;
  word-spacing: 2px;
}
.fa-solid.fa-edit {
  padding-left: 94px;
}
.fa-solid.fa-trash {
  padding-left: 30px;
}
.edit-note {
  width: 441px;
  height: 316px;
  border: 1px solid #ccc;
  background-color: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  text-align: center;
  border-radius: 10px;
  position: absolute;
  top: 22%;
  left: 34%;
  display: none;
}
.edit-btn {
  width: 100px;
  height: 36px;
  margin-top: 18px;
  margin-left: 15px;
  background-color: #8c53ff;
  color: white;
  border: none;
  border-radius: 14px;
  font-size: 14px;
}
.close-btn2 {
  width: 100px;
  height: 36px;
  background-color: gainsboro;
  color: white;
  margin-left: 10px; 
  border: none;
  border-radius: 14px;
  font-size: 14px;
}
@media only screen and (max-width: 600px) {
  .container {
    width: 375px;
    display: block;
  }
  .add {
    margin-left: 20%;
  }
  .create-note {
    left: 0%;
    width: 374px;
  } 
  .note {
    margin-left: 70px;
    overflow-y: clip;
  }
  .inner-note {
    padding-top: 20px;
  }
  .fa-solid.fa-edit {
    padding-top: 10px;
  }
}

let add_btn = document.querySelector('.add');
let container = document.querySelector('.container');
let createNote = document.querySelector('.create-note');
let create = document.querySelector('.create-btn');
let close = document.querySelector('.close-btn');
// let note = document.querySelector('.note');
let text = '';
let editNote = document.querySelector('.edit-note');
let textArea = document.querySelector('textarea');
let closeBtn = document.querySelector('.close-btn2');
let editBtn = document.querySelector('.edit-btn');
let textArea2 = document.querySelector('.textarea2');
add_btn.addEventListener('click', () => {
  textArea.value = '';
  createNote.style.display = 'block';
})
close.addEventListener('click', () => {
  createNote.style.display = 'none';
})
create.addEventListener('click', () => {
  let note = document.createElement('div'); 
  note.className = 'note';
  let innerNote = document.createElement('div');
  innerNote.className = 'inner-note';
  text = textArea.value;
  innerNote.textContent = text;
  let edit = document.createElement('i');
  edit.className = 'fa-solid fa-edit';
  let del = document.createElement('i');
  del.className = 'fa-solid fa-trash';
  if (!textArea.value){
    return;
  }
  else {
  note.appendChild(innerNote);
  note.appendChild(edit);
  note.appendChild(del);
  container.appendChild(note); 
  createNote.style.display = 'none';
  note.style.display = 'block';
  edit.addEventListener('click', () => {
    textArea2.value = text;
    editNote.style.display = 'block';
  })
  editBtn.addEventListener('click', () => {
    innerNote.textContent = textArea2.value;
    editNote.style.display = 'none';
  })
  del.addEventListener('click', () => {
    container.removeChild(note);
  })
  // onLoad();
  let notesArr = [];
  let innerNote1 = document.querySelectorAll('.inner-note');
  innerNote1.forEach((val) =>  {
      if (val.textContent) {
        notesArr.push(val.textContent);
      }
      else {
        return;
      }
    });
  console.log(notesArr);
  localStorage.setItem('notes',JSON.stringify(notesArr));
  JSON.parse(localStorage.getItem('notes'));
  }
});

closeBtn.addEventListener('click', () => {
  editNote.style.display = 'none';
})
const onLoad = function () {
  let notesArr = JSON.parse(localStorage.getItem('notes'));
  console.log(notesArr);
  notesArr.forEach((val) => {
    let note = document.createElement('div');
    note.className = 'note';
    let innerNote = document.createElement('div');
    innerNote.className = 'inner-note';
    let edit = document.createElement('i');
    edit.className = 'fa-solid fa-edit';
    let del = document.createElement('i');
    del.className = 'fa-solid fa-trash';
    innerNote.textContent = val;
    note.appendChild(innerNote);
    note.appendChild(edit);
    note.appendChild(del);
    note.style.display = 'block';
    container.appendChild(note);
  edit.addEventListener('click', () => {
    val = textArea2.value;
    innerNote.textContent = val;
    editNote.style.display = 'block';
  })
  editBtn.addEventListener('click', () => {
    innerNote.textContent = textArea2.value;
    editNote.style.display = 'none';
  })
  del.addEventListener('click', () => {
    container.removeChild(note);
  })
  })
}

window.onload = onLoad;

I am trying to make a notes app with delete and edit functionality, when i edit the note , the local storage doesn’t save the edited note, it saves the previous note and similar things happen when i delete it.How do i edit the data inside the localStorage array after, editing the html element dynamically. Can i access the specific HTML element which is part of a nodeList (innerNote)

Prevent close when clicking Ant Design Dropdown submenu item

I have an Ant Design Dropdown component in a React application. The dropdown includes submenus. Right now, the submenu items close when they’re clicked on. I would like them to instead remain open on click.

The Ant Design docs have a solution for this for the base level dropdown menu items, however, this doesn’t work for sub menu items.

I also came across this Github issue where the problem is supposedly solved. However, the solution uses the old overlay prop for the Dropdown component, which is deprecated in favor of the menu prop.

Finally, I tried adding a div around the label of the submenu items with onClick={(e) => e.stopPropagation()}. This mostly works, except the menu item has some padding around itself, so the label does not take up the full space that the menu item takes up. Because of this, the submenu does not close when you click in the center of it (on the label), but it still closes if you click around the edge (on the padding).

How to get the default theme in React for Material UI v6?

I am creating a React Based application using Material UI V6. I am referring to a repo created in a similar project using MUI v4. Now here is where I am unable to proceed. So I have:

  1. Styles.js
import { makeStyles } from '@mui/styles';
import { red, green } from '@mui/material/colors';
export default makeStyles((theme) => ({
 avatarExpense: {
    color: theme.palette.getContrastText(red[500]),
    backgroundColor: red[500],
  }
}));
  1. I create an object in my List.jsx using
import React from 'react'
import styles from './styles';

const List = () => {
  const classes = styles();
  return (
  )}

I get an error:

styles.js:13 Uncaught TypeError: Cannot read properties of undefined (reading 'getContrastText')

My investigation suggests that it’s got to do with something related with the default theme… not sure…
I tried the ‘createTheme’ fix, but I am unable to import it.
Any help?

My index.js:

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

I have tried creating the default theme but was unsuccessful…

While scrolling, change the overflow property

I have an element that changes it’s overflow property during a scrolling event. Meaning, I can flick my mouse wheel, during that time while it’s still spinning, the overflow goes from hidden to auto and the scrollbar appears. It will only scroll once the mouse wheel stops OR if I move the mouse a tiny bit as the wheel is turning. Is there a way to force a redraw or something for the browser to recalculate what should be scrolling?

Thanks!

Having navigation bar href path routing issues whenever I am in a new html page

When I first load the page to index.html everything loads fine. I press the About link in the navigation bar to direct to the about.html page and it loads fine. But when I press on the About link again while I am in the about.html page it throws of an error that it cannot find it. I am guessing the href file paths change when routing to a different html page. I am not sure how to fix this issue. Also I am using a customized navigation-bar tag so I have one place to reference it as a component and reuse it in every page. This logic is in the main.js file.

//This is the main.js file
class Navigation extends HTMLElement {
    connectedCallback(){
        this.innerHTML = `
    <div class="menuContainer fadeInDown-animation">
        <div class="item"><a href="index.html"><button class="btn">Home</button></a></div>
        <div class="item"><a href="htmlPages/about.html"><button class="btn">About</button></a></div>
        <div class="item"><button class="btn">Skills</button></div>
        <div class="item"><a href="contact.html"><button class="btn">Contact</button></a></div>
    </div> 
    `
    }
}

customElements.define('navigation-bar', Navigation);

Here is my file structure:
src/index.html
src/htmlPages/about.html
src/htmlPages/contact.html
src/javascript/main.js
src/jquery-3.7.1.min.js
src/css/style.css

Spotify Info Fetcher in JavaScript isn’t outputting any data

I have this project, it fetches data about the user on Spotify using JavaScript.

When you press a button, it should show stuff like top albums, how much you listen to and such.

I’ve tried to clear my cache.

I use Firefox, so I switched to Chrome temporarily, and I still get the same error.

I’ve also tried to disable browser extensions such as PrivacyBadger and uBlock origin.

I’ve check on spotify forums for people who had the same errors, but no dice.

In the debug console I get this error

Spotify API Error 
Endpoint: /v1/me/playlists 
Status Code: 403 
Message: API request failed 
Timestamp: 2025-03-02T01:53:33.866Z errorHandling.js:40:17
    logError https://spotify.formen.cc/js/errorHandling.js:40
    fetchUserData https://spotify.formen.cc/js/app.js:55
    <anonymous> https://spotify.formen.cc/js/app.js:6
    <anonymous> https://spotify.formen.cc/js/login.js:47
    (Async: EventListener.handleEvent)
    <anonymous> https://spotify.formen.cc/js/login.js:39

Also another thing in the console

Full error details: 
Object { timestamp: "2025-03-02T01:53:33.866Z", name: "SpotifyAPIError", message: "API request failed", stack: "SpotifyAPIError@https://spotify.formen.cc/js/errorHandling.js:4:9nhandleApiResponse@https://spotify.formen.cc/js/errorHandling.js:82:15nasync*fetchUserData@https://spotify.formen.cc/js/app.js:35:15nasync*@https://spotify.formen.cc/js/app.js:6:18n@https://spotify.formen.cc/js/login.js:47:20nEventListener.handleEvent*@https://spotify.formen.cc/js/login.js:39:8n", context: "User Profile Fetch" }
errorHandling.js:75:13

The app.js

import { SpotifyAPIError, AuthenticationError, DataProcessingError, logError, handleApiResponse, handleAuthError, handleDataProcessingError } from './errorHandling.js';

// Listen for token received event from login.js
window.addEventListener('tokenReceived', (event) => {
    const token = event.detail.token;
    fetchUserData(token);
    fetchTopTracks(token);
    fetchTopArtists(token);
    fetchRecentlyPlayed(token);
    fetchSavedAlbums(token);
    createListeningTimeline(token);
});

// Fetch user profile data
async function fetchUserData(token) {
    try {
        const response = await fetch('https://api.spotify.com/v1/me', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(response, '/v1/me');
        const data = await response.json();

        // Update profile section with more user information
        document.getElementById('profile-image').src = data.images[0]?.url || '';
        document.getElementById('profile-name').textContent = data.display_name;
        document.getElementById('profile-country').textContent = `Country: ${data.country}`;
        document.getElementById('profile-type').textContent = `Account: ${data.product}`;
        document.getElementById('profile-followers').textContent = `Followers: ${data.followers.total}`;
        document.getElementById('profile-uri').textContent = `Spotify URI: ${data.uri}`;

        // Fetch user's playlists
        const playlistsResponse = await fetch('https://api.spotify.com/v1/me/playlists?limit=6', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(playlistsResponse, '/v1/me/playlists');
        const playlistsData = await playlistsResponse.json();

        // Display playlists
        const playlistsGrid = document.getElementById('user-playlists');
        playlistsGrid.innerHTML = playlistsData.items.map(playlist => `
            <div class="playlist-item">
                <img src="${playlist.images[0]?.url || ''}" alt="${playlist.name}">
                <div class="playlist-info">
                    <strong>${playlist.name}</strong>
                    <p>${playlist.tracks.total} tracks</p>
                    <small>${playlist.followers?.total || 0} followers</small>
                </div>
            </div>
        `).join('');

        document.getElementById('dashboard').classList.remove('hidden');
        document.getElementById('login-section').classList.add('hidden');
    } catch (error) {
        if (error instanceof SpotifyAPIError || error instanceof AuthenticationError) {
            logError(error, { context: 'User Profile Fetch' });
        } else {
            handleDataProcessingError(error, 'user profile');
        }
        document.getElementById('login-section').classList.remove('hidden');
    }
}

// Fetch user's top tracks
async function fetchTopTracks(token) {
    try {
        const response = await fetch('https://api.spotify.com/v1/me/top/tracks?limit=5', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(response, '/v1/me/top/tracks');
        const data = await response.json();
        const tracksList = document.getElementById('top-tracks');
        tracksList.innerHTML = data.items.map(track => `
            <div class="track-item">
                <img src="${track.album.images[2].url}" alt="${track.name}">
                <div>
                    <strong>${track.name}</strong>
                    <p>${track.artists[0].name}</p>
                </div>
            </div>
        `).join('');
    } catch (error) {
        if (error instanceof SpotifyAPIError || error instanceof AuthenticationError) {
            logError(error, { context: 'Top Tracks Fetch' });
        } else {
            handleDataProcessingError(error, 'top tracks');
        }
    }
}

// Fetch user's top artists
async function fetchTopArtists(token) {
    try {
        const response = await fetch('https://api.spotify.com/v1/me/top/artists?limit=6', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(response, '/v1/me/top/artists');
        const data = await response.json();
        const artistsGrid = document.getElementById('top-artists');
        artistsGrid.innerHTML = data.items.map(artist => `
            <div class="artist-item">
                <img src="${artist.images[1].url}" alt="${artist.name}">
                <p>${artist.name}</p>
            </div>
        `).join('');

        // Create genre distribution chart
        const genres = data.items.flatMap(artist => artist.genres);
        const genreCounts = genres.reduce((acc, genre) => {
            acc[genre] = (acc[genre] || 0) + 1;
            return acc;
        }, {});

        const sortedGenres = Object.entries(genreCounts)
            .sort((a, b) => b[1] - a[1])
            .slice(0, 5);

        new Chart(document.getElementById('genres-chart'), {
            type: 'doughnut',
            data: {
                labels: sortedGenres.map(([genre]) => genre),
                datasets: [{
                    data: sortedGenres.map(([, count]) => count),
                    backgroundColor: [
                        '#1DB954', '#1ED760', '#2EBD59', '#57B660', '#7C795D'
                    ]
                }]
            },
            options: {
                responsive: true,
                plugins: {
                    legend: {
                        position: 'right',
                        labels: { color: '#ffffff' }
                    }
                }
            }
        });

    } catch (error) {
        if (error instanceof SpotifyAPIError || error instanceof AuthenticationError) {
            logError(error, { context: 'Top Artists Fetch' });
        } else {
            handleDataProcessingError(error, 'top artists');
        }
    }
}

// Fetch recently played tracks
async function fetchRecentlyPlayed(token) {
    try {
        const response = await fetch('https://api.spotify.com/v1/me/player/recently-played?limit=5', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(response, '/v1/me/player/recently-played');
        const data = await response.json();
        const recentList = document.getElementById('recent-tracks');
        recentList.innerHTML = data.items.map(item => `
            <div class="track-item">
                <img src="${item.track.album.images[2].url}" alt="${item.track.name}">
                <div>
                    <strong>${item.track.name}</strong>
                    <p>${item.track.artists[0].name}</p>
                    <small>Played at: ${new Date(item.played_at).toLocaleString()}</small>
                </div>
            </div>
        `).join('');
    } catch (error) {
        if (error instanceof SpotifyAPIError || error instanceof AuthenticationError) {
            logError(error, { context: 'Recently Played Fetch' });
        } else {
            handleDataProcessingError(error, 'recently played tracks');
        }
    }
}

// Fetch saved albums
async function fetchSavedAlbums(token) {
    try {
        const response = await fetch('https://api.spotify.com/v1/me/albums?limit=4', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(response, '/v1/me/albums');
        const data = await response.json();
        const albumsGrid = document.getElementById('saved-albums');
        albumsGrid.innerHTML = data.items.map(item => `
            <div class="album-item">
                <img src="${item.album.images[1].url}" alt="${item.album.name}">
                <p>${item.album.name}</p>
                <small>${item.album.artists[0].name}</small>
            </div>
        `).join('');
    } catch (error) {
        if (error instanceof SpotifyAPIError || error instanceof AuthenticationError) {
            logError(error, { context: 'Saved Albums Fetch' });
        } else {
            handleDataProcessingError(error, 'saved albums');
        }
    }
}

// Create listening history timeline
async function createListeningTimeline(token) {
    try {
        const response = await fetch('https://api.spotify.com/v1/me/player/recently-played?limit=20', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        await handleApiResponse(response, '/v1/me/player/recently-played');
        const data = await response.json();

        if (!data.items || !data.items.length) {
            throw new Error('No listening history available');
        }

        // Process the data for the timeline
        const timelineData = data.items.map(item => ({
            timestamp: new Date(item.played_at).getTime(),
            hour: new Date(item.played_at).getHours()
        }));

        // Group plays by hour
        const hourCounts = Array(24).fill(0);
        timelineData.forEach(item => {
            hourCounts[item.hour]++;
        });

        // Create the timeline chart
        new Chart(document.getElementById('history-chart'), {
            type: 'bar',
            data: {
                labels: Array.from({length: 24}, (_, i) => `${i}:00`),
                datasets: [{
                    label: 'Plays by Hour',
                    data: hourCounts,
                    backgroundColor: '#1DB954',
                    borderColor: '#1DB954',
                    borderWidth: 1
                }]
            },
            options: {
                responsive: true,
                scales: {
                    y: {
                        beginAtZero: true,
                        grid: { color: '#333' },
                        ticks: { color: '#fff' }
                    },
                    x: {
                        grid: { color: '#333' },
                        ticks: { color: '#fff' }
                    }
                },
                plugins: {
                    legend: {
                        labels: { color: '#fff' }
                    }
                }
            }
        });
    } catch (error) {
        if (error instanceof SpotifyAPIError || error instanceof AuthenticationError) {
            logError(error, { context: 'Listening Timeline Creation' });
        } else {
            handleDataProcessingError(error, 'listening timeline');
        }
        const chartElement = document.getElementById('history-chart');
        if (chartElement) {
            chartElement.innerHTML = '';
        }
    }
}

// Theme toggle functionality
function initializeTheme() {
    const savedTheme = localStorage.getItem('theme') || 'dark';
    document.documentElement.setAttribute('data-theme', savedTheme);
    updateThemeIcon(savedTheme);
}

function updateThemeIcon(theme) {
    const themeIcon = document.querySelector('#theme-toggle i');
    themeIcon.className = theme === 'dark' ? 'fas fa-moon' : 'fas fa-sun';
}

document.getElementById('theme-toggle').addEventListener('click', () => {
    const currentTheme = document.documentElement.getAttribute('data-theme');
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
    
    document.documentElement.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);
    updateThemeIcon(newTheme);
});

// Initialize theme on page load
initializeTheme();

// Handle logout
document.getElementById('logout-button').addEventListener('click', () => {
    // Clear all authentication and state data
    localStorage.removeItem('spotify_access_token');
    localStorage.removeItem('state');
    sessionStorage.clear();
    
    // Clear URL hash to prevent auto-login on refresh
    window.location.hash = '';
    
    // Hide dashboard and show login
    document.getElementById('dashboard').classList.add('hidden');
    document.getElementById('login-section').classList.remove('hidden');
    
    // Optionally reload the page to clear any cached data
    window.location.reload();
});

The login.js

// Constants for Spotify authentication
const clientId = '{clientID}';
const redirectUri = 'https://spotify.formen.cc/';

console.log(clientId);
console.log(redirectUri);

// Generate a random string for state parameter
function generateRandomString(length) {
    let text = '';
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for (let i = 0; i < length; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    console.log(text);
    return text;
}

// Initialize login when the button is clicked
document.getElementById('login-button').addEventListener('click', () => {
    const state = generateRandomString(16);
    const scope = 'user-read-private user-read-email user-top-read user-read-recently-played user-library-read playlist-read-private';

    const authUrl = new URL('https://accounts.spotify.com/authorize');
    const params = {
        response_type: 'token',
        client_id: clientId,
        scope: scope,
        redirect_uri: redirectUri,
        state: state
    };
    console.log(authUrl)
    console.log(params)
    authUrl.search = new URLSearchParams(params).toString();
    window.location.href = authUrl.toString();
});

// Handle the redirect with token
window.addEventListener('load', () => {
    const hash = window.location.hash.substring(1);
    if (hash) {
        const params = new URLSearchParams(hash);
        const accessToken = params.get('access_token');
        if (accessToken) {
            localStorage.setItem('spotify_access_token', accessToken);
            // Trigger the data fetching functions from app.js
            window.dispatchEvent(new CustomEvent('tokenReceived', { detail: { token: accessToken } }));
        }
    }
});

// Handle logout
document.getElementById('logout-button')?.addEventListener('click', () => {
    // Clear all authentication and state data
    localStorage.removeItem('spotify_access_token');
    localStorage.removeItem('state');
    sessionStorage.clear();
    
    // Clear URL hash to prevent auto-login on refresh
    window.location.hash = '';
    
    // Hide dashboard and show login
    document.getElementById('dashboard')?.classList.add('hidden');
    document.getElementById('login-section')?.classList.remove('hidden');
    
    // Reload the page to clear any cached data
    window.location.reload();
});

and errorhandling.js if relevant

// Custom error types
class SpotifyAPIError extends Error {
    constructor(message, endpoint, statusCode) {
        super(message);
        this.name = 'SpotifyAPIError';
        this.endpoint = endpoint;
        this.statusCode = statusCode;
    }
}

class AuthenticationError extends Error {
    constructor(message, reason) {
        super(message);
        this.name = 'AuthenticationError';
        this.reason = reason;
    }
}

class DataProcessingError extends Error {
    constructor(message, dataType) {
        super(message);
        this.name = 'DataProcessingError';
        this.dataType = dataType;
    }
}

// Error logging function with different severity levels
const logError = (error, context = {}) => {
    const timestamp = new Date().toISOString();
    const errorDetails = {
        timestamp,
        name: error.name,
        message: error.message,
        stack: error.stack,
        ...context
    };

    // Log different types of errors with appropriate formatting
    if (error instanceof SpotifyAPIError) {
        console.error(
            '%cSpotify API Error',
            'color: #e22134; font-weight: bold;',
            'nEndpoint:', error.endpoint,
            'nStatus Code:', error.statusCode,
            'nMessage:', error.message,
            'nTimestamp:', timestamp
        );
    } else if (error instanceof AuthenticationError) {
        console.error(
            '%cAuthentication Error',
            'color: #f59b23; font-weight: bold;',
            'nReason:', error.reason,
            'nMessage:', error.message,
            'nTimestamp:', timestamp
        );
    } else if (error instanceof DataProcessingError) {
        console.error(
            '%cData Processing Error',
            'color: #8c4b96; font-weight: bold;',
            'nData Type:', error.dataType,
            'nMessage:', error.message,
            'nTimestamp:', timestamp
        );
    } else {
        console.error(
            '%cUnexpected Error',
            'color: #c41e3a; font-weight: bold;',
            'nType:', error.name,
            'nMessage:', error.message,
            'nTimestamp:', timestamp
        );
    }

    // Log full error details for debugging
    console.debug('Full error details:', errorDetails);
}

// API response handler
const handleApiResponse = async (response, endpoint) => {
    if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new SpotifyAPIError(
            errorData.error?.message || 'API request failed',
            endpoint,
            response.status
        );
    }
    return response;
}

// Authentication error handler
const handleAuthError = (error) => {
    if (error.message.includes('access_token')) {
        throw new AuthenticationError('Authentication token is invalid or expired', 'Invalid Token');
    }
    throw new AuthenticationError('Failed to authenticate with Spotify', 'Unknown');
}

// Data processing error handler
const handleDataProcessingError = (error, dataType) => {
    throw new DataProcessingError(`Failed to process ${dataType} data: ${error.message}`, dataType);
}

// Export error handling utilities
export {
    SpotifyAPIError,
    AuthenticationError,
    DataProcessingError,
    logError,
    handleApiResponse,
    handleAuthError,
    handleDataProcessingError
};

First NextJS project , many errors

i have finished working on my first Next.js project today and tried to deploy it to Vercel, but i got these errors:
The project is working very well on the localhost

./node_modules/next/dist/build/webpack/loaders/next-route-loader/index.js?kind=PAGES&page=%2F_error&preferredRegion=&absolutePagePath=next%2Fdist%2Fpages%2F_error&absoluteAppPath=private-next-pages%2F_app.js&absoluteDocumentPath=next%2Fdist%2Fpages%2F_document&middlewareConfigBase64=e30%3D!

Attempted import error: ‘private-next-pages/_app.js’ does not contain a default export (imported as ‘app’).

✓ Compiled successfully
Linting and checking validity of types …

Failed to compile.
./app/checkout/page.js

3:27 Error: ‘useEffect’ is defined but never used. @typescript-eslint/no-unused-vars
./app/education/page.tsx

54:45 Error: ' can be escaped with &apos;, &lsquo;, &#39;, &rsquo;. react/no-unescaped-entities
./app/educational/page.tsx

54:45 Error: ' can be escaped with &apos;, &lsquo;, &#39;, &rsquo;. react/no-unescaped-entities
./app/landing/page.tsx

51:6 Warning: React Hook useEffect has a missing dependency: ‘TEXTS.length’. Either include it or remove the dependency array. react-hooks/exhaustive-deps
./app/payment/page.js

25:6 Warning: React Hook useEffect has missing dependencies: ‘selectedNetwork’ and ‘selectedSubNetwork’. Either include them or remove the dependency array. react-hooks/exhaustive-deps

Strapi Content Manager Page is Blank

After publishing my Strapi project into production, when trying to access Content Manager with a SuperUser it does not load, it just generates an infinite loading bar, the console log only shows
this enter image description here

my package.json

{
  "name": "my-strapi-project",
  "version": "0.1.0",
  "private": true,
  "description": "A Strapi application",
  "scripts": {
    "build": "strapi build",
    "deploy": "strapi deploy",
    "develop": "strapi develop",
    "start": "strapi start",
    "strapi": "strapi"
  },
  "dependencies": {
    "@strapi/admin": "^5.10.4",
    "@strapi/plugin-cloud": "5.10.4",
    "@strapi/plugin-users-permissions": "5.10.4",
    "@strapi/strapi": "5.10.4",
    "better-sqlite3": "11.3.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "react-router-dom": "^6.0.0",
    "styled-components": "^6.0.0"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "tailwindcss": "^4.0.9",
    "typescript": "^5"
  },
  "engines": {
    "node": ">=18.0.0 <=22.x.x",
    "npm": ">=6.0.0"
  },
  "strapi": {
    "uuid": "dda04996-83ae-4ca0-95ff-cc8ddcd9b232"
  }
}

JS gets initial value for an animating property instead of the current one

This is my html

function getAnimState() {
  document.getElementById("count").innerHTML = getComputedStyle(document.documentElement).getPropertyValue("--opacity")
}
@property --opacity {
  syntax: "<number>";
  initial-value: 9;
  inherits: false;
}

@keyframes breatheFade {
  0%,
  100% {
    --opacity: 25;
  }

  50% {
    --opacity: 100;
  }
}

.count {
  animation-name: breatheFade;
  animation-iteration-count: infinite;
  animation-duration: 1.5s;
  font-size: 20vh;
  opacity: calc(var(--opacity)*1%);
}
<button class="count" id="count" onclick="getAnimState()">Click to see opacity</button>

But when I try to click the button at any frame, I just get the default value no matter what it is and what the animation displays.

Does anyone know why is that the case and how could I fix it? Is there another solution to get the value at the time of the call of the function?

I was expecting to see the current opacity and work with it with further js.

I tried asking chatgpt but it made up errors. Google also didn’t help.

I made a jsfiddle for it at https://jsfiddle.net/h0zyguLr/

Uncaught SyntaxError: Invalid or unexpected token for defined variable [closed]

Uncaught SyntaxError: Invalid or unexpected token for a defined string.

const firstMessage = "I'm learning about functions";

The error begins after the apostrophe. So it only reads “I”. No attempted adjustments mentioned below resolve the issue.

I’ve attempted numerous times to escape the apostrophe from the sentence or remove it altogether. I tried single quotes and backticks. I additionally attempted to concatenate the sentence. I check for extra spaces, characters, and retyped the sentence manually. Adding a period to the end as notated in the comment doesn’t bear any impact. Nothing works. Please advise on what is causing this.

//Create a variable titled ‘firstMessage’ and have it equal “I’m learning about functions.”//
const firstMessage = "I'm learning about functions";

How to correctly unref a V8 substring (“sliced string”) from its source string

I spent a better (or rather worse) part of today hunting a bug that caused Node.js engine to randomly run out of memory when searching a giant log file line by line by regex.

The cause was that I kept the matches in array, which kept hidden references to entire chunks of the original file. The number of matches was not a problem, but the scattering across the file was. For reference, this is the code I use to break binary stream inputs into line strings:

/**
 * 
 * @param {ReadableStreamDefaultReader<Uint8Array>} reader
 * @returns {AsyncGenerator<string>}
 */
export default async function* asyncLineIterator(reader, cancelReader = true) {
  let prefetch = null;
  let wasDone = false;
  try {
    const utf8Decoder = new TextDecoder("utf-8");
    // const response = await fetch(fileURL);
    // const reader = response.body.getReader();
    let { value: binaryChunk, done: readerDone } = await reader.read();
    let chunk = binaryChunk ? utf8Decoder.decode(binaryChunk) : "";

    const newline = /r?n/gm;
    let startIndex = 0;
    let result;

    prefetch = reader.read();

    while (true) {
      const result = newline.exec(chunk);
      if (!result) {
        if (readerDone) {
          break;
        }

        const remainder = chunk.substr(startIndex);
        ({ value: binaryChunk, done: readerDone } = await prefetch);

        if (!readerDone) {
          prefetch = reader.read();
          chunk = remainder + (binaryChunk ? utf8Decoder.decode(binaryChunk, {stream: true}) : "");
        }
        else {
          prefetch = null;
          chunk = remainder;
        }

        startIndex = newline.lastIndex = 0;
        continue;
      }
      yield chunk.substring(startIndex, result.index);
      startIndex = newline.lastIndex;
      if(chunk.length > 10*1024*1024) {
        throw new Error("Line too long, aborting.");
      }
    }

    if (startIndex < chunk.length) {
      // Last line didn't end in a newline char
      yield chunk.substr(startIndex);
    }
    wasDone = readerDone;
  }
  catch (e) {
    console.trace(e);
    throw e;
  }
  finally {
    if(prefetch) {
      await prefetch;
    }
    //console.log("Done reading lines.");
    if(cancelReader && !wasDone) {
      await reader.cancel();
    }
  }
}

What was happening in my code and caused crash:

  • Chunks received were 66kB give or take
  • substr above creates sliced string and keeps ref to the original 66kB chunk
  • RegExp.exec also keeps ref to slice of a slice, retaining the 66kB chunk
  • In a special case, there was a moderate number of matches, far apart
  • Far apart matches kept references to a lot of different 66kB chunks

This is what I saw after I was finally able to obtain a memory snapshot before a crash:
picture shows a match array containing a ref to 66176kB string

This is one of my matches, containing 66kB string, despite being like 60 characters.

My solution for now is the following:

        /**
         * @template TInput
         * @param {TInput} str 
         * @returns {TInput}
         */
        function unrefLine(str) {
            return JSON.parse(JSON.stringify(str));
        }

Which I call with the match array:

const match = myRegex.exec(lineStr);
if(match) {
   myMatches.push({matches: unrefLine([...match]), matchedLine: unrefLine(lineStr)});
}

This solved all crashes. My question here is if there’s a faster and less ugly way than JSON.stringify. The goal is to get V8 to forget that the substring belongs to the original chunk from the file I am reading.

Can you explain these javascript weird thing? [duplicate]

I can’t believe it, please explain me what happen:
Put this in a html file then launch it:

document.write("[] == [] ; return: ");
document.write([] == []);
document.write("<br>");
document.write("3 > 2 ; return: ");
document.write(3 > 2);
document.write(" but 3 > 2 > 1 ; return: ");
document.write(3 > 2 > 1);
document.write("<br>");
document.write("0.1 + 0.2 ; return: ");
document.write(0.1 + 0.2);
document.write("<br>");

How to position cursor 2 lines bellow “blockquote”

I am scratching my head for 2 days trying to fix this very simple thing, it’s a plugin for MYBB forum board that let you quote text and send it to quick reply at the bottom of the page but when the script scroll down to copy the ”block-quote” the cursor is position inside the block-quote and it need to be position second line under block-quote like in the image below.

So if anyone can help me since the mybb forum board is notenter image description here very active anymore i would be very grateful, i need this functionality for my free forum board.

Here the script :

   if (!$me.hasClass('post')) {
$me = $me.parents('.post');
}
if ($me && $me.length) {
pid = $me[0].id.split('_')[1];
if ($('#pid_' + pid + '').has('form').length == 0) {
var selection = window.getSelection();
if (selection.rangeCount > 0) {
var nowselect = selection.getRangeAt(0);
if ($.trim(window.getSelection().toString()) && beforeselect!=nowselect) {
beforeselect = nowselect;
if (elementContainsSelection($me.find('.post_body')[0])) {
range = selection.getRangeAt(0),
rect = range.getBoundingClientRect();
$elm = $('#qr_pid_' + pid + '').show();
$elm.css({
'top': (window.scrollY + rect.top + rect.height + 6) + 'px',
'left': (getposition().left - $elm.outerWidth() + 10) + 'px'
});
hide_reply_btn = false;
}
}
}
}
}
if (hide_reply_btn) {
$('#qr_pid_' + pid + '').hide();
}
});
}
});
// Credits: http://stackoverflow.com/a/8340432
function isOrContains(node, container) {
while (node) {
if (node === container) {
return true;
}
node = node.parentNode;
}
return false;
}
function elementContainsSelection(el) {
    var sel;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount > 0) {
            for (var i = 0; i < sel.rangeCount; ++i) {
                if (!isOrContains(sel.getRangeAt(i).commonAncestorContainer, el)) {
                    return false;
                }
            }
            return true;
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        return isOrContains(sel.createRange().parentElement(), el);
    }
    return false;
}
// Credits: https://stackoverflow.com/a/1589912
function getposition() {
var markerTextChar = "ufeff";
var markerTextCharEntity = "&#xfeff;";
var markerEl, markerId = "sel_" + new Date().getTime() + "_" + Math.random().toString().substr(2);
var position = {};
var sel, range;
if (document.selection && document.selection.createRange) {
// Clone the TextRange and collapse
range = document.selection.createRange().duplicate();
range.collapse(false);
// Create the marker element containing a single invisible character by creating literal HTML and insert it
range.pasteHTML('<span id="' + markerId + '" style="position: relative;">' + markerTextCharEntity + '</span>');
markerEl = document.getElementById(markerId);
} else if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt) {
range = sel.getRangeAt(0).cloneRange();
} else {
// Older WebKit doesn't have getRangeAt
range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
// Handle the case when the selection was selected backwards (from the end to the start in the
// document)
if (range.collapsed !== sel.isCollapsed) {
range.setStart(sel.focusNode, sel.focusOffset);
range.setEnd(sel.anchorNode, sel.anchorOffset);
}
}
range.collapse(false);
// Create the marker element containing a single invisible character using DOM methods and insert it
markerEl = document.createElement("span");
markerEl.id = markerId;
markerEl.appendChild( document.createTextNode(markerTextChar) );
range.insertNode(markerEl);
}
if (markerEl) {
// Find markerEl position http://www.quirksmode.org/js/findpos.html
var obj = markerEl;
var left = 0, top = 0;
do {
left += obj.offsetLeft;
top += obj.offsetTop;
} while (obj = obj.offsetParent);
// Move the button into place.
// Substitute your jQuery stuff in here
position['left'] = left;
position['top'] = top;
markerEl.parentNode.removeChild(markerEl);
return position;
}
}
var beforeselect = null;
function quick_quote(pid, username, dateline) {
if ($('#quick_reply_form').length) {
$('body:not("#pid_' + pid + '")').click(function (e){
if (!$.trim(window.getSelection().toString())){
$('#qr_pid_' + pid + '').hide();
}
});
$('#qr_pid_' + pid + '').click(function (e){
e.preventDefault();
setTimeout(function() {
if (elementContainsSelection(document.getElementById('pid_' + pid + ''))) {
Thread.quickQuote(pid,'' + username + '',dateline);
$('#qr_pid_' + pid + '').hide();
var sel = window.getSelection ? window.getSelection() : document.selection;
if (sel) {
if (sel.removeAllRanges) {
sel.removeAllRanges();
} else if (sel.empty) {
sel.empty();
}
}
}
else {
$('#qr_pid_' + pid + '').hide();
}
},200);
})
}
}
// Credits: http://mods.mybb.com/view/quickquote
Thread.quickQuote = function(pid, username, dateline)
{
if(isWebkit || window.getSelection().toString().trim()) {
var sel = window.getSelection();
var userSelection = sel.getRangeAt(0).cloneContents();
if (parseInt(rinvbquote)) {
var quoteText = "[quote="+username+";"+pid+"]n";
}
else {
var quoteText = "[quote='" + username + "' pid='" + pid + "' dateline='" + dateline + "']n";
}
var parentNode = sel.getRangeAt(0).commonAncestorContainer;
while (typeof parentNode.tagName == "undefined") {
if (parentNode.parentNode) {
parentNode = parentNode.parentNode;
} else break;
}
quoteText += Thread.domToBB(userSelection, MYBB_SMILIES, parentNode, 1);
quoteText += "n[/quote]n";
delete userSelection;
Thread.updateMessageBox(quoteText);
}
}
Thread.updateMessageBox = function(message)
{
MyBBEditor.insert(message);
setTimeout(function() {
offset = $('#quickreply_e').offset().top - 60;
setTimeout(function() {
$('html, body').animate({
scrollTop: offset
}, 700);
},200);
},100);
}
Thread.RGBtoHex = function (R,G,B) {return Thread.toHex(R)+Thread.toHex(G)+Thread.toHex(B)}
Thread.toHex = function(N)
{
if (N==null) return "00";
N=parseInt(N); if (N==0 || isNaN(N)) return "00";
N=Math.max(0,N); N=Math.min(N,255); N=Math.round(N);
return "0123456789ABCDEF".charAt((N-N%16)/16)
+ "0123456789ABCDEF".charAt(N%16);
}
Thread.textNodeSpanToBB = function(spanEl)
{
var openTag = '';
var content = '';
var closeTag = '';
var compStyles = window.getComputedStyle(spanEl, null);
if(compStyles.getPropertyValue("text-decoration") == "underline")
{
openTag = "" + openTag;
closeTag = closeTag + "";
}
if(compStyles.getPropertyValue("font-weight") > 400 || compStyles.getPropertyValue("font-weight") == "bold")
{
openTag = "" + openTag;
closeTag = closeTag + "";
}
if(compStyles.getPropertyValue("font-style") == "italic")
{
openTag = "" + openTag;
closeTag = closeTag + "";
}
var colourVal = Thread.normaliseColour(compStyles.getPropertyValue("color"));
var post_colour = Thread.normaliseColour($('.post_body').css('color'));
if (post_colour != colourVal) {
openTag = "" + openTag;
closeTag = closeTag + "";
}
content = spanEl.childNodes[0].data.replace(/[nt]+/,'');
if (content) {
return openTag + content + closeTag;
} else return '';
}
Thread.normaliseColour = function(colourStr) {
var match;
colourStr = colourStr || '#000';
// rgb(n,n,n);
if ((match = colourStr.match(/rgb((d{1,3}),s*?(d{1,3}),s*?(d{1,3}))/i))) {
return '#' + Thread.RGBtoHex(match[1], match[2], match[3]);
}
// rgba(n,n,n,f.p);
// Strip transparency component (f.p).
if ((match = colourStr.match(/rgba((d{1,3}),s*?(d{1,3}),s*?(d{1,3}),s*?(d*.?d+s*))/i))) {
return '#' + Thread.RGBtoHex(match[1], match[2], match[3]);
}
// expand shorthand
if ((match = colourStr.match(/#([0-f])([0-f])([0-f])s*?$/i))) {
return '#' +
      match[1] + match[1] +
      match[2] + match[2] +
      match[3] + match[3];
}
return colourStr;
}
Thread.domToBB = function(domEl, smilies, parentNode, depth)
{
var output = "";
var childNode;
var openTag;
var content;
var closeTag;
for(var i = 0 ; i < domEl.childNodes.length ; i++)
{
childNode = domEl.childNodes;
openTag = "";
content = "";
closeTag = "";
var clonedNode = null;
var newSpan = null;

if(typeof childNode.tagName == "undefined")
{
switch(childNode.nodeName)
{
case '#text':
if (depth == 1 && typeof parentNode.tagName !== "undefined") {
// Add the cloned text node to the document invisibly under
// its rightful parent node, so that the call to
// window.getComputedStyle() in textNodeSpanToBB() works.
newSpan = document.createElement('span');
clonedNode = childNode.cloneNode();
newSpan.appendChild(clonedNode);
newSpan.style.display = 'none';
parentNode.appendChild(newSpan);
output += Thread.textNodeSpanToBB(newSpan);
newSpan.removeChild(clonedNode);
parentNode.removeChild(newSpan);
} else {
output += childNode.data.replace(/[nt]+/,'');
}
break;
default:
// do nothing
break;
}
}
else
{
switch(childNode.tagName)
{
case "SPAN":
// check style attributes
switch(true)
{
case childNode.style.textDecoration == "underline":
openTag = "";
closeTag = "";
break;
case childNode.style.fontWeight > 0:
case childNode.style.fontWeight == "bold":
openTag = "";
closeTag = "";
break;
case childNode.style.fontStyle == "italic":
openTag = "";
closeTag = "";
break;
case childNode.style.fontFamily != "":
openTag = "";
closeTag = "";
break;
case childNode.style.fontSize != "":
openTag = "";
closeTag = "";
break;
case childNode.style.color != "":
if(childNode.style.color.indexOf('rgb') != -1)
{
var rgb = childNode.style.color.replace("rgb(","").replace(")","").split(",");
var hex = "#"+Thread.RGBtoHex(parseInt(rgb[0]) , parseInt(rgb[1]) , parseInt(rgb[2]));
}
else
{
var hex = childNode.style.color;
}
openTag = "";
closeTag = "";
break;
}
break;
case "STRONG":
case "B":
openTag = "";
closeTag = "";
break;
case "EM":
case "I":
openTag = "";
closeTag = "";
break;
case "U":
openTag = "";
closeTag = "";
break;
case "IMG":
if(smilies[childNode.src])
{
openTag ="";
content = smilies[childNode.src];
closeTag = "";
}
else
{
openTag ="[img]";
content = childNode.src;
closeTag = "[/img]";
}
break;
case "A":
switch(true)
{
case childNode.href.indexOf("mailto:") == 0:
openTag = "";
closeTag = "";
break;
default:
openTag = "";
closeTag = "";
break;
}
break;
case "OL":
openTag = "
"; closeTag = "n
";
break;
case "UL":
openTag = "
"; closeTag = "n
";
break;
case "LI":
openTag = "n
";
closeTag = "";
break;
case "BLOCKQUOTE":
childNode.removeChild(childNode.firstChild);
openTag = "[quote]n";
closeTag = "n[/quote]";
break;
case "DIV":
if(childNode.style.textAlign)
{
openTag = "
n";
closeTag = "n
n";
}

switch(childNode.className)
{
case "codeblock":
openTag = "[code]n";
closeTag = "n[/code]";
childNode.removeChild(childNode.getElementsByTagName("div")[0]);
break;
case "codeblock phpcodeblock":
var codeTag = childNode.getElementsByTagName("code")[0];
childNode.removeChild(childNode.getElementsByTagName("div")[0]);
openTag = "[php]n";
if(codeTag.innerText)
{
content = codeTag.innerText;
}
else
{
//content = codeTag.textContent;
content = codeTag.innerHTML.replace(/<br([^>]*)>/gi,"n").replace(/<([^<]+)>/gi,'').replace(/&nbsp;/gi,' ');
}
closeTag = "n[/php]";
break;
}
break;
case "P":
closeTag = "nn";
break;
case "BR":
closeTag = "n"
break;
}
output += openTag + content;

if(content == "" && childNode.childNodes && childNode.childNodes.length > 0)
{
output += Thread.domToBB(childNode, smilies, parentNode, depth+1);
}

output += closeTag;
}
}


};[/code]