User not logged in - login - register
Home Calendar Books School Tool Photo Gallery Message Boards Users Statistics Advertise Site Info
go to bottom | |
 Message Boards » » Modernize TWW Replies! Page [1]  
CaelNCSU
All American
7384 Posts
user info
edit post

Here is a tampermonkey script to make @ users easier with quote inserts, had to break it into two posts:

Mentions (@username):

When you type @ in a text box (with class post-box), it shows a quick list of matching usernames.

Quote Insertion (\q):

Whenever you type \q, the script automatically replaces it with TWW Quote Tags.

Start copying into tampermokey below:

// ==UserScript==
// @name TWW Fuzzy Search Mention + Quote Insert
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Fuzzy search user mentions on thewolfweb.com and insert quotes
// @match *://thewolfweb.com/*
// @grant none
// ==/UserScript==

(function() {
'use strict';

// ----- Configuration: Adjust your user list here -----
const USERS = [
"joe17669", "erice85", "synapse", "El Nachó", "BubbleBobble", "BigMan157",
"kiljadn", "TreeTwista10", "FroshKiller", "dropdeadkate", "EMCE", "qntmfred",
"NCSUStinger", "DROD900", "Kitty B", "Sweden", "chocolatervh"
];

// ----- Minimal CSS for the suggestion box -----
const suggestionBoxStyle = `
position: absolute;
background-color: #fff;
border: 1px solid #ccc;
font-size: 14px;
z-index: 9999;
width: 200px;
max-height: 150px;
overflow-y: auto;
`;
const suggestionItemStyle = `
padding: 4px 8px;
cursor: pointer;
`;
const suggestionItemHoverStyle = `
background-color: #efefef;
`;

// Create a container for suggestions (hidden by default)
let suggestionBox = document.createElement('div');
suggestionBox.id = 'tww-fuzzy-suggestions';
suggestionBox.setAttribute('style', suggestionBoxStyle + 'display: none;');
document.body.appendChild(suggestionBox);

let highlightedIndex = -1; // which suggestion is highlighted
let currentSuggestions = []; // current list of matched suggestions
let mentionStartPos = -1; // position in the textarea where '@' began
let activeTextArea = null; // reference to the active .post-box

// Utility: Simple fuzzy/substring search
function findMatches(input) {
const lower = input.toLowerCase();
return USERS.filter(u => u.toLowerCase().includes(lower));
}

// Utility: highlight an item in the suggestion box
function highlightSuggestion(index) {
// Clear existing highlight
[...suggestionBox.children].forEach((child, i) => {
child.style.backgroundColor = (i === index) ? '#efefef' : '';
});
}

// Utility: show suggestions in the suggestionBox
function showSuggestions(suggestions) {
suggestionBox.innerHTML = '';
suggestions.forEach((s, index) => {
let item = document.createElement('div');
item.textContent = s;
item.setAttribute('style', suggestionItemStyle);
item.addEventListener('mouseover', () => {
highlightedIndex = index;
highlightSuggestion(index);
});
item.addEventListener('mousedown', (e) => {
e.preventDefault();
insertMention(s);
});
suggestionBox.appendChild(item);
});
suggestionBox.style.display = 'block';
highlightedIndex = -1;
}

// Utility: hide the suggestion box
function hideSuggestions() {
suggestionBox.style.display = 'none';
suggestionBox.innerHTML = '';
highlightedIndex = -1;
currentSuggestions = [];
mentionStartPos = -1;
}

// Utility: Insert mention at the current cursor position
function insertMention(username) {
if (!activeTextArea) return;

const ta = activeTextArea;
// If mentionStartPos was stored, we remove everything from @ to cursor.
const start = ta.selectionStart;
const textBefore = ta.value.substring(0, mentionStartPos);
const textAfter = ta.value.substring(start);
// Insert username
const mentionText = `[user]${username}[/user]`;
ta.value = textBefore + mentionText + textAfter;

// Move caret to just after the inserted mention
const newPos = textBefore.length + mentionText.length;
ta.selectionStart = newPos;
ta.selectionEnd = newPos;
ta.focus();

hideSuggestions();
}

// Utility: Insert
Quote :
""
with caret in the middle
function insertQuote() {
if (!activeTextArea) return;
const ta = activeTextArea;
const start = ta.selectionStart;
const end = ta.selectionEnd;

// We'll replace the "\q" with
Quote :
""
, placing caret in between
const textBefore = ta.value.substring(0, start - 2); // remove \q
const textAfter = ta.value.substring(end);
const quoteText = `
Quote :
""
`;
ta.value = textBefore + quoteText + textAfter;

// position caret between the tags:
//
Quote :
"|"

const newPos = textBefore.length + `[quote]`.length;
ta.selectionStart = newPos;
ta.selectionEnd = newPos;
ta.focus();
}

// Reposition the suggestion box near the bottom-left corner of the cursor in the textarea
// This is approximate (since textareas don't provide exact per-character coords).
// We do a simpler approach: position the box near the textarea's offset plus line height.
function positionSuggestionBox() {
if (!activeTextArea) return;
// Get bounding box of the textarea
const rect = activeTextArea.getBoundingClientRect();
suggestionBox.style.top = (window.scrollY + rect.top + 30) + 'px';
suggestionBox.style.left = (window.scrollX + rect.left + 10) + 'px';
}

// Handler for keydown to intercept arrow keys and enter
function onKeyDown(e) {
if (suggestionBox.style.display === 'block' && currentSuggestions.length > 0) {
if (e.key === 'ArrowDown') {
e.preventDefault();
highlightedIndex = (highlightedIndex + 1) % currentSuggestions.length;
highlightSuggestion(highlightedIndex);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
highlightedIndex = (highlightedIndex - 1 + currentSuggestions.length) % currentSuggestions.length;
highlightSuggestion(highlightedIndex);
} else if (e.key === 'Enter') {
if (highlightedIndex >= 0 && highlightedIndex < currentSuggestions.length) {
e.preventDefault();
const selectedUser = currentSuggestions[highlightedIndex];
insertMention(selectedUser);
}
} else if (e.key === 'Escape') {
hideSuggestions();
}
} else {
// If user typed backslash 'q' => Possibly handle \q
// We'll do this check on keydown if the typed character is 'q' or
// we can do it on input. Let's do it on input for exact substring matching.
}
}

[Edited on January 23, 2025 at 10:51 AM. Reason : next post ]

[Edited on January 23, 2025 at 10:51 AM. Reason : a]

1/23/2025 10:50:29 AM

CaelNCSU
All American
7384 Posts
user info
edit post

// Handler for input (detecting \q, or building @ mention)
function onInput(e) {
const ta = e.target;
activeTextArea = ta;

const value = ta.value;
const pos = ta.selectionStart;

// 1. Check if we just typed "\q"
// If the user typed \q (two chars), we see if the text before the cursor ends with '\q'.
if (value.substring(pos - 2, pos) === '\\q') {
// Replace it with
Quote :
""
, place caret
insertQuote();
hideSuggestions();
return;
}

// 2. Check if we are in an '@mention' scenario
// Find the last '@' before the cursor (if any)
let lastAtPos = value.lastIndexOf('@', pos - 1);
if (lastAtPos === -1) {
hideSuggestions();
return;
}

// Make sure there's a space or line start before '@' or it is indeed a new mention
// (Otherwise, it could be in the middle of text not intended as mention)
// But for simplicity, let's skip that check and assume any "@" is a mention attempt.

// The text typed since '@'
const typedSinceAt = value.substring(lastAtPos + 1, pos);

// If typedSinceAt includes a space or bracket, it means user ended mention
if (/\s|\[|\]/.test(typedSinceAt)) {
hideSuggestions();
return;
}

// We have a partial mention
mentionStartPos = lastAtPos; // store for insertion
const partial = typedSinceAt.trim();
if (!partial) {
// user just typed "@", so show entire list maybe
currentSuggestions = USERS.slice(0, 15); // limit results
} else {
// do fuzzy search
currentSuggestions = findMatches(partial).slice(0, 15);
}

if (currentSuggestions.length > 0) {
showSuggestions(currentSuggestions);
positionSuggestionBox();
} else {
hideSuggestions();
}
}

// Attach event listeners to any textarea with class "post-box" that appears
function attachToPostBoxes() {
const postBoxes = document.querySelectorAll('textarea.post-box');
postBoxes.forEach(box => {
if (!box.dataset.twwListener) {
box.addEventListener('input', onInput);
box.addEventListener('keydown', onKeyDown);
box.dataset.twwListener = "true";
}
});
}

// Run once on load
attachToPostBoxes();

// If the site dynamically loads new textareas, we can watch for DOM changes
const observer = new MutationObserver(() => {
attachToPostBoxes();
});
observer.observe(document.body, { childList: true, subtree: true });
})();

1/23/2025 10:51:20 AM

qntmfred
retired
41011 Posts
user info
edit post

maybe a gist would work better

[Edited on January 23, 2025 at 11:09 AM. Reason : or turn it into an extension. froshkiller did something like this back in the day]

1/23/2025 11:06:05 AM

justinh524
Sprots Talk Mod
28649 Posts
user info
edit post

No I don't think I will

1/23/2025 12:16:52 PM

The Coz
Tempus Fugitive
26951 Posts
user info
edit post

^

1/23/2025 12:55:42 PM

CaelNCSU
All American
7384 Posts
user info
edit post

^
I did it for The Coz!

1/24/2025 9:28:48 AM

Walter
All American
7960 Posts
user info
edit post

Where is The Porn Guy?

1/24/2025 9:36:36 AM

FroshKiller
All American
51924 Posts
user info
edit post

Don't forget to special-case spaced guy.

1/24/2025 9:49:24 AM

kiljadn
All American
44691 Posts
user info
edit post

oh shit i made it into the script

1/24/2025 11:11:16 AM

 Message Boards » Chit Chat » Modernize TWW Replies! Page [1]  
go to top | |
Admin Options : move topic | lock topic

© 2025 by The Wolf Web - All Rights Reserved.
The material located at this site is not endorsed, sponsored or provided by or on behalf of North Carolina State University.
Powered by CrazyWeb v2.39 - our disclaimer.