Skip to content

Commit a2e5a8b

Browse files
authored
Update unity-forum-fixer.js
- Fix MouseOver tooltips (now they disappear properly) - Fix Original Poster name under title (if topic had replies from OP)
1 parent 48efcf6 commit a2e5a8b

File tree

1 file changed

+116
-99
lines changed

1 file changed

+116
-99
lines changed

unity-forum-fixer.js

Lines changed: 116 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ==UserScript==
22
// @name UnityForumFixer
33
// @namespace https://unitycoder.com/
4-
// @version 0.72 (28.11.2024)
4+
// @version 0.73 (16.12.2024)
55
// @description Fixes For Unity Forums - https://github.com/unitycoder/UnityForumFixer
66
// @author unitycoder.com
77
// @match https://discussions.unity.com/latest
@@ -179,72 +179,69 @@ function NavBar()
179179

180180
// FORUM VIEW
181181

182-
function TopicsViewShowOriginalPosterInfo()
183-
{
184-
// Select all topic rows
182+
function TopicsViewShowOriginalPosterInfo() {
185183
const topicRows = document.querySelectorAll('tr.topic-list-item');
186184

187185
topicRows.forEach(row => {
188-
// Find the first 'a' element inside the 'posters topic-list-data' cell that does not have the 'latest' class (Original Poster)
189-
let firstPosterLink = row.querySelector('td.posters.topic-list-data a:not(.latest)');
190-
191-
// If there is no such element, it might be a single poster with 'latest single' class
192-
if (!firstPosterLink) {
193-
firstPosterLink = row.querySelector('td.posters.topic-list-data a.latest.single');
194-
}
186+
// Always take the first username in the posters column as the original poster
187+
const postersColumn = row.querySelector('td.posters.topic-list-data');
188+
const firstPosterAvatar = postersColumn ? postersColumn.querySelector('a[data-user-card]') : null;
195189

196-
if (firstPosterLink) {
197-
// Extract the username from the 'data-user-card' attribute for the original poster
198-
const originalPosterUsername = firstPosterLink.getAttribute('data-user-card');
190+
if (firstPosterAvatar) {
191+
const originalPosterUsername = firstPosterAvatar.getAttribute('data-user-card');
199192

200-
// Find the topic creation date from the title attribute in the activity column
193+
// Extract creation date from the activity cell
201194
const activityCell = row.querySelector('td.activity');
202195
const titleText = activityCell ? activityCell.getAttribute('title') : '';
203196
const creationDateMatch = titleText.match(/Created: (.+?)(?:\n|$)/);
204197

205-
let creationDateFormatted = 'Unknown'; // Default to "Unknown" if no date is found
198+
let creationDateFormatted = 'Unknown';
206199
if (creationDateMatch) {
207200
const creationDateStr = creationDateMatch[1];
208201
const creationDate = new Date(creationDateStr);
209202
creationDateFormatted = formatDateString(creationDate);
210203
}
211204

212-
// Find the 'link-bottom-line' element to insert the original poster's name and creation date before it
205+
// Insert the original poster info into the main-link cell
213206
const linkBottomLine = row.querySelector('td.main-link .link-bottom-line');
214207
if (linkBottomLine && !row.querySelector('.original-poster-span')) {
215-
// Create a new span element for the original poster's username and creation date
216208
const originalPosterSpan = document.createElement('span');
217-
originalPosterSpan.textContent = originalPosterUsername+","+creationDateFormatted;
218-
originalPosterSpan.className = 'original-poster-span'; // Adding a class to prevent duplication
219-
originalPosterSpan.style.display = 'block'; // Ensure it's placed as a block element
209+
originalPosterSpan.className = 'original-poster-span';
210+
originalPosterSpan.style.display = 'block';
211+
originalPosterSpan.textContent = `${originalPosterUsername}, ${creationDateFormatted}`;
220212

221-
// Insert the original poster span before the link-bottom-line
213+
// Insert the span before the link-bottom-line
222214
linkBottomLine.parentNode.insertBefore(originalPosterSpan, linkBottomLine);
223215
}
224216
}
225217

226-
// Find the most recent poster (always marked with 'latest')
218+
// Handle the latest poster's info
227219
const latestPosterLink = row.querySelector('td.posters.topic-list-data a.latest');
228220
if (latestPosterLink) {
229-
// Extract the username from the 'data-user-card' attribute
230221
const latestPosterUsername = latestPosterLink.getAttribute('data-user-card');
231-
232-
// Find the 'post-activity' element
233222
const postActivity = row.querySelector('td.activity .post-activity');
223+
234224
if (postActivity && !row.querySelector('.latest-poster-span')) {
235-
// Create a new span element for the latest poster's username
236225
const latestPosterSpan = document.createElement('span');
226+
latestPosterSpan.className = 'latest-poster-span';
227+
latestPosterSpan.style.display = 'block';
237228
latestPosterSpan.textContent = latestPosterUsername;
238-
latestPosterSpan.className = 'latest-poster-span'; // Adding a class to prevent duplication
239-
latestPosterSpan.style.display = 'block'; // Ensure it's placed as a block element
240229

241-
// Insert the latest poster span before the <a> tag, placing it outside the link
230+
// Insert the latest poster span before the activity link
242231
postActivity.parentNode.insertBefore(latestPosterSpan, postActivity);
243232
}
244233
}
245234
});
246235
}
247236

237+
// Utility function to format dates
238+
function formatDateString(date) {
239+
const options = { year: 'numeric', month: 'short', day: 'numeric' };
240+
return date.toLocaleDateString('en-GB', options);
241+
}
242+
243+
244+
248245
function TopicsViewCombineViewAndReplyCounts()
249246
{
250247
// Select all rows in the topic list
@@ -290,8 +287,9 @@ function TopicsViewCombineViewAndReplyCounts()
290287
viewsHeader.remove(); // Remove the "Views" header
291288
}
292289
}
293-
294-
function FixPostActivityTime() {
290+
291+
function FixPostActivityTime()
292+
{
295293
document.querySelectorAll('.relative-date').forEach(function (el) {
296294
const dataTime = parseInt(el.getAttribute('data-time'), 10);
297295
if (!dataTime) return;
@@ -324,86 +322,105 @@ function FixPostActivityTime() {
324322
});
325323
}
326324

327-
let prevTopicId = ''; // Global variable to store the previously fetched topicId
328-
let currentTooltip = null; // Global variable to store the currently visible tooltip
325+
let prevTopicId = ''; // Global variable to store the previously fetched topicId
326+
let currentTooltip = null; // Global variable to store the currently visible tooltip
327+
let tooltipTarget = null; // Track the current element triggering the tooltip
328+
let hoverTimeout = null; // Timeout for delaying tooltip display
329+
330+
// Initialize the mouseover event handler
331+
function OnMouseOverPostPreview() {
332+
document.querySelectorAll('a.title.raw-link.raw-topic-link[data-topic-id]').forEach(function (element) {
333+
const topicId = element.getAttribute('data-topic-id');
334+
335+
// Add mouseover event listener to the <a> elements only
336+
element.addEventListener('mouseover', function (event) {
337+
tooltipTarget = element; // Set the current tooltip target
338+
hoverTimeout = setTimeout(() => {
339+
if (tooltipTarget === element && topicId !== prevTopicId) { // Ensure the mouse is still over the same element
340+
fetchPostDataAndShowTooltip(event, topicId, element);
341+
}
342+
}, 250); // Delay tooltip display by 250ms
343+
});
329344

345+
// Add mouseout event listener to clear timeout and hide tooltip
346+
element.addEventListener('mouseout', function () {
347+
tooltipTarget = null; // Clear the current tooltip target
348+
clearTimeout(hoverTimeout); // Cancel the tooltip display timeout
349+
hideTooltip();
350+
});
351+
});
330352

331-
// Initialize the mouseover event handler
332-
function OnMouseOverPostPreview() {
333-
document.querySelectorAll('a.title.raw-link.raw-topic-link[data-topic-id]').forEach(function (element) {
334-
const topicId = element.getAttribute('data-topic-id');
353+
// Add a global mousemove listener to hide the tooltip if the mouse moves outside
354+
document.addEventListener('mousemove', function (event) {
355+
if (currentTooltip && (!tooltipTarget || !tooltipTarget.contains(event.target))) {
356+
hideTooltip();
357+
}
358+
});
359+
}
335360

336-
// Add mouseover event listener to the <a> elements only
337-
element.addEventListener('mouseover', function (event) {
338-
if (topicId !== prevTopicId) { // Check if the post data was already fetched
339-
fetchPostDataAndShowTooltip(event, topicId, element);
340-
}
341-
});
361+
// Function to fetch data and display tooltip
362+
function fetchPostDataAndShowTooltip(event, topicId, element) {
363+
const url = `https://discussions.unity.com/t/${topicId}.json`;
342364

343-
// Add mouseout event listener to hide tooltip
344-
element.addEventListener('mouseout', function () {hideTooltip();});
365+
fetch(url)
366+
.then(response => response.json())
367+
.then(data => {
368+
// Extract necessary data from JSON (limit to 250 characters)
369+
const rawPostContent = data['post_stream']['posts'][0]['cooked'];
370+
const postContent = rawPostContent.length > 250 ? rawPostContent.substring(0, 250) + "..." : rawPostContent;
371+
const plainText = stripHtmlTags(postContent);
372+
373+
// Update the global variable to store the fetched topicId
374+
prevTopicId = topicId;
375+
376+
// Create and position the tooltip based on the element's position
377+
showTooltip(element, plainText);
378+
})
379+
.catch(error => {
380+
console.error('Error fetching post data:', error);
345381
});
346-
}
382+
}
347383

348-
// Function to fetch data and display tooltip
349-
function fetchPostDataAndShowTooltip(event, topicId, element)
350-
{
351-
const url = `https://discussions.unity.com/t/${topicId}.json`;
352-
353-
fetch(url)
354-
.then(response => response.json())
355-
.then(data => {
356-
// Extract necessary data from JSON (limit to 250 characters)
357-
const rawPostContent = data['post_stream']['posts'][0]['cooked'];
358-
const postContent = rawPostContent.length > 250 ? rawPostContent.substring(0, 250) + "..." : rawPostContent;
359-
const plainText = stripHtmlTags(postContent);
360-
361-
// Update the global variable to store the fetched topicId
362-
prevTopicId = topicId;
363-
364-
// Create and position the tooltip based on the element's position
365-
showTooltip(element, plainText);
366-
})
367-
.catch(error => {
368-
console.error('Error fetching post data:', error);
369-
});
370-
}
384+
// Function to create and show the tooltip
385+
function showTooltip(element, content) {
386+
hideTooltip(); // Ensure any existing tooltip is removed first
371387

372-
// Function to create and show the tooltip
373-
function showTooltip(element, content) {
374-
hideTooltip(); // Ensure any existing tooltip is removed first
388+
// Create a new tooltip element
389+
currentTooltip = createTooltip(content);
375390

376-
// Create a new tooltip element
377-
currentTooltip = createTooltip(content);
391+
// Get the bounding rectangle of the <a> element
392+
const rect = element.getBoundingClientRect();
378393

379-
// Get the bounding rectangle of the <a> element
380-
const rect = element.getBoundingClientRect();
394+
// Position the tooltip relative to the <a> element
395+
currentTooltip.style.top = `${window.scrollY + rect.top - currentTooltip.offsetHeight - 10}px`; // 10px above the element
396+
currentTooltip.style.left = `${window.scrollX + rect.left}px`;
397+
}
381398

382-
// Position the tooltip relative to the <a> element
383-
currentTooltip.style.top = `${window.scrollY + rect.top - currentTooltip.offsetHeight - 10}px`; // 10px above the element
384-
currentTooltip.style.left = `${window.scrollX + rect.left}px`;
385-
386-
currentTooltip.addEventListener('mouseover', function () {
387-
hideTooltip();
388-
});
399+
// Function to hide the tooltip
400+
function hideTooltip() {
401+
if (currentTooltip) {
402+
currentTooltip.remove();
403+
currentTooltip = null;
389404
}
405+
}
406+
407+
// Function to create a tooltip element
408+
function createTooltip(content) {
409+
const tooltip = document.createElement('div');
410+
tooltip.className = 'custom-post-preview'; // Assign the CSS class
411+
tooltip.textContent = content;
412+
document.body.appendChild(tooltip);
413+
return tooltip;
414+
}
415+
416+
// Function to strip HTML tags from a string
417+
function stripHtmlTags(html) {
418+
const tempDiv = document.createElement("div"); // Create a temporary <div> element
419+
tempDiv.innerHTML = html; // Set its inner HTML to the input HTML string
420+
return tempDiv.textContent || tempDiv.innerText || ""; // Return the text content without HTML tags
421+
}
390422

391-
// Function to hide the tooltip
392-
function hideTooltip() {
393-
if (currentTooltip) {
394-
currentTooltip.remove();
395-
currentTooltip = null;
396-
}
397-
}
398423

399-
// Function to create a tooltip element
400-
function createTooltip(content) {
401-
const tooltip = document.createElement('div');
402-
tooltip.className = 'custom-post-preview'; // Assign the CSS class
403-
tooltip.textContent = content;
404-
document.body.appendChild(tooltip);
405-
return tooltip;
406-
}
407424

408425
function stripHtmlTags(html)
409426
{

0 commit comments

Comments
 (0)