PatsFans.com Menu
PatsFans.com - The Hub For New England Patriots Fans
PatsFans.com - The Hub For New England Patriots Fans

Patriots Articles & Archive

Our full article archive from over 25 years.

Patriots Articles & Archive | PatsFans.com

Our full article archive from over 25 years.

Showing 15,408 results (page 36 of 617)

Patriots Expected to Be 'Very Active' in Free Agency

February 05, 2025 | Ian Logue

Coming off a promising season with rookie quarterback Drake Maye, along with the recent hiring of Mike Vrabel, the New England Patriots are clearly facing a pivotal offseason.

PATRIOTS INSIDER: Patriots 'Contemplating' Listening to Potential Trade Offers for Milton

February 05, 2025 | Ian Logue

In the final game of the 2024 regular season, the New England Patriots came out of that game learning that the team appears to potentially have a solid back up quarterback behind Drake Maye.

Maye Talks About His 'Bad Question' At the Pro Bowl

February 05, 2025 | Ian Logue

Drake Maye had a busy week last week, having been a late addition to the Pro Bowl roster and spending time in Orlando with his NFL peers.

Drake Maye Ends the Lebron vs Jordan Debate

February 05, 2025 | Ian Logue

However, when it comes to North Carolina native and New England Patriots quarterback Drake Maye, there's no question in his mind who remains the better player between Lebron James and Michael Jordan.

TRANSCRIPT: Patricia Recalls Junior Seau Patriots Story

February 05, 2025 | Ian Logue

Former New England Patriots head coach Matt Patricia recently appeared on Games with Names with Julian Edelman and recalled a terrific story about Junior Seau's return to the club ahead of a West Coast trip during a 2008 Patriots Schedule that included match-ups against both Seattle and the Oakland Raiders.

TRANSCRIPT: Drake Maye Interview With Kay Adams

February 04, 2025 | Ian Logue

Here's the full transcript from Drake Maye's appearance with Kay Adams on Tuesday.

TRANSCRIPT: Drake Maye Interview on GMFB

February 04, 2025 | Ian Logue

Here's the full transcription from New England Patriots quarterback Drake Maye's interview on Tuesday on Good Morning Football.

TRANSCRIPT: Matt Patricia with Julian Edelman On the Malcolm Butler Interception

February 04, 2025 | Ian Logue

Here's a transcript of the conversation between former New England Patriots coach Matt Patricia and former wideout Julian Edelman on a recent Games With Names podcast talking about the Malcolm Butler interception against the Seattle Seahawks.

NFL Insider on Tee Higgins Coming to Patriots: 'I don't think he'll come here.'

February 04, 2025 | Ian Logue

The New England Patriots head into this offseason with one big need, and the hope is that they'll come out of this offseason with an explosive wideout having been added to their offense.

Rams Kupp Announces He'll Be on Trading Block, Could Patriots End Up as a Possibility?

February 03, 2025 | Ian Logue

Star receiver Cooper Kupp announced on X on Monday night that he's been informed that his days in Los Angeles are coming to an end, with the team set to start exploring trade options for him.

TRANSCRIPT: Phil Perry's Interview with James White

February 03, 2025 | Ian Logue

Here's what former New England Patriots running back James White during Phil Perry of NBC Sports Boston's most recent Next Pats Podcast.

Former Captain Not Returning to Patriots Staff in 2025

February 03, 2025 | Ian Logue

Heading into 2025, it looks like Matthew Slater will not be on hand in New England's locker room.

Patriots Considering Former Receiver For Coaching Role?

February 03, 2025 | Ian Logue

The Patriots coaching staff continues coming together, and it sounds like they could potentially add a former player to the staff at some point to round it out further.

MORSE: Senior Bow Wrap-Up, NFL Dates

February 03, 2025 | Mark Morse

We just should have called this the Jack Bech Bowl.  The TCU WR put together three solid days of practice and was named one of the Practice Players of the week for the American Team. 

Patriots News 2-2, Maye’s Excited To Work With McDaniels/Vrabel

February 02, 2025 | Steve Balestrieri

Coach McDaniels... the success he's had, I'm fortunate to be able to have a guy come in who has done it at a high level and learn from him

MORSE: Senior Bowl Top Practice Players and Potential Patriots Prospects

February 01, 2025 | Mark Morse

The Senior Bowl is this afternoon at 2:30 on NFL Network.  I suspect many of the players will opt out of the actual game itself.  Most of the scouts and team execs have left

MORSE: Wrap Up of the East West All Star Game

February 01, 2025 | Mark Morse

These are the players that were voted as Practice Players of the Week by their peers.  The players they went up against in practice chose these players as the most outstanding during the three days of practice.

TRANSCRIPT: Patriots QB Drake Maye on Sirius XM Radio

January 31, 2025 | Ian Logue

Here's a transcript of Drake Maye's recent appearance in an interview on Sirius XM Radio.

Patriots Maye 'Fortunate' To Work With McDaniels

January 31, 2025 | Ian Logue

Drake Maye has already had a relatively busy offseason after getting engaged, and then finding out he was added to the Pro Bowl roster.

Tough Night For Patriots QB Maye in NFL Skills Challenge

January 31, 2025 | Ian Logue

One of the better parts of the NFL's Pro Bowl has become the fact that it's more about the players having a good time, with fans getting to see the lighter side of things while being entertained by their favorite players.

MORSE: Who to Watch in East West Shrine Game, Day 2 Senior Bowl Practice

January 30, 2025 | Mark Morse

Here's a look at who I am watching at the Wast West Shrine Game Tonight.  The game will be aired on NFL Network starting at 8 EST.  

MORSE: Senior Bowl Day One Practice Report

January 30, 2025 | Mark Morse

The Patriots sent quite the contingent to Mobile for the Senior Bowl practices.  Led by Head Coach Mike Vrabel they also have EVP for Player Personnel Eliot Wolf, VP of Player Personnel Ryan Cowden, Director of Player Personnel Matt Groh and Director of College Scouting Camren Williams. 

Vrabel May Call Defensive Plays 'At Least to Start' in 2025

January 29, 2025 | Ian Logue

Mike Vrabel talked quite a bit after he was hired about not only developing players, but also developing coaches given the road he took himself along the way before becoming a head coach.

Former Chiefs Coach Was Apparently Patriots' 'Top Candidate' Ahead of McDaniels

January 29, 2025 | Ian Logue

New Patriots head coach Mike Vrabel has taken his time putting his coaching staff together, doing some due diligence all-around ahead of the team filling its top two key positions.

Maye's Pro-Bowl Appearance a Nice Bonus After Tough Year

January 29, 2025 | Ian Logue

For a season that was definitely one of the tougher ones we've seen in recent memory, first-year quarterback Drake Maye was at least one of the few bright spots that the New England Patriots took away from a tumultuous 2024 campaign.

(function() { document.addEventListener('DOMContentLoaded', function() { var mediaBtn = document.getElementById('pfeai-media-button'); if (mediaBtn) { mediaBtn.addEventListener('click', function(e) { e.preventDefault(); if (window.PFEditorialAI) { PFEditorialAI.togglePanel(); } }); } }); if (typeof tinymce === 'undefined') { return; } tinymce.create('tinymce.plugins.PFEditorialAI', { init: function(editor, url) { editor.on('init', function() { PFEditorialAI.createPanel(editor); PFEditorialAI.bindTooltipEvents(); PFEditorialAI.preloadSavedData(); }); }, getInfo: function() { return { longname: 'PF Editorial AI', author: 'PatsFans + AI', version: '5.2.7' }; } }); tinymce.PluginManager.add('pfeai_plugin', tinymce.plugins.PFEditorialAI); window.PFEditorialAI = { panelCreated: false, panelEl: null, loadingEl: null, analysisEl: null, titlesEl: null, ideasEl: null, transcriptEl: null, socialEl: null, savedEl: null, factsEl: null, linksEl: null, isMinimized: false, // Cache window tracking cacheWindowStart: null, cacheWindowTimer: null, cacheWindowDuration: 4 * 60 * 1000, // 4 minutes (conservative estimate) tasksInWindow: 0, /** * SECURITY: Escape HTML to prevent XSS * This function must be used for ALL user/API data inserted into the DOM */ escapeHtml: function(str) { if (str === null || str === undefined) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }, /** * SECURITY: Sanitize for textarea/input values */ escapeAttr: function(str) { if (str === null || str === undefined) return ''; return String(str) .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, ''') .replace(//g, '>'); }, /** * Cache Window Indicator - shows when implicit caching is active */ startCacheWindow: function(fromCache) { var self = this; // If result was from app cache, don't start/extend the API cache window if (fromCache) return; this.tasksInWindow++; // Start or extend the cache window this.cacheWindowStart = Date.now(); // Clear existing timer if (this.cacheWindowTimer) { clearInterval(this.cacheWindowTimer); } // Update indicator immediately this.updateCacheIndicator(); // Update every second this.cacheWindowTimer = setInterval(function() { self.updateCacheIndicator(); }, 1000); }, updateCacheIndicator: function() { var indicator = document.getElementById('pfeai-cache-indicator'); if (!indicator) return; if (!this.cacheWindowStart) { indicator.style.display = 'none'; return; } var elapsed = Date.now() - this.cacheWindowStart; var remaining = this.cacheWindowDuration - elapsed; if (remaining <= 0) { // Cache window expired indicator.style.display = 'none'; this.cacheWindowStart = null; this.tasksInWindow = 0; if (this.cacheWindowTimer) { clearInterval(this.cacheWindowTimer); this.cacheWindowTimer = null; } return; } // Show indicator indicator.style.display = 'block'; var minutes = Math.floor(remaining / 60000); var seconds = Math.floor((remaining % 60000) / 1000); var timeStr = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; var savingsText = this.tasksInWindow > 1 ? ' · ' + (this.tasksInWindow - 1) + ' task' + (this.tasksInWindow > 2 ? 's' : '') + ' cached' : ''; indicator.innerHTML = '⚡ Cache active: ' + timeStr + ' remaining' + savingsText + '
Run more tasks now to save tokens'; }, getProvider: function() { var select = document.getElementById('pfeai-provider-select'); return select ? select.value : 'gemini'; }, getSafeContent: function(editor) { try { if (editor && !editor.isHidden()) return editor.getContent({ format: 'raw' }); var textArea = document.getElementById('content'); return textArea ? textArea.value : ''; } catch (e) { return ''; } }, getSafeText: function(editor) { try { if (editor && !editor.isHidden()) return editor.getContent({ format: 'text' }); var textArea = document.getElementById('content'); return textArea ? textArea.value : ''; } catch (e) { return ''; } }, createPanel: function(editor) { if (this.panelCreated) return; // FIX: Inject Layout Push Styles directly to ensure they work var style = document.createElement('style'); style.innerHTML = 'body.pfeai-panel-open #wpbody-content { margin-right: 360px !important; transition: margin-right 0.3s ease; } body.pfeai-panel-open .pfeai-panel { right: 0; box-shadow: -2px 0 5px rgba(0,0,0,0.1); }'; document.head.appendChild(style); var panel = document.createElement('div'); panel.id = 'pfeai-panel'; var html = ''; html += '
'; html += '
'; html += '
'; html += '
'; html += ' '; html += '
'; html += '
PF Editorial AI
'; html += '
'; html += ' '; html += '
'; html += '
'; html += '
'; html += '
'; html += ' '; html += ' '; html += '
'; html += '
'; html += '
Click to expand
'; html += '
'; html += ' '; html += '
'; html += '
'; if (PFEAI.saved_data.clipboard) { html += '
'; html += ''; html += ''; html += '
'; } html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; // Only show Clear Topics button if there's cached ideas data if (PFEAI.saved_data.ideas && PFEAI.saved_data.ideas.ideas) { html += '
'; // FIX: Set both to flex:1 and width:auto to make them equal size html += ''; html += ''; html += '
'; } else { html += ' '; } html += ' '; html += '
'; html += '
'; html += ' '; html += '
'; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; if (PFEAI.saved_data.clipboard) { html += ' '; } html += '
'; html += '
'; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += ' '; html += '
'; html += '
'; html += '
'; panel.innerHTML = html; document.body.appendChild(panel); this.panelEl = panel; this.loadingEl = panel.querySelector('.pfeai-loading'); this.analysisEl = { highlights: panel.querySelector('.pfeai-tab-content[data-tab="highlights"]'), suggestions: panel.querySelector('.pfeai-tab-content[data-tab="suggestions"]'), gaps: panel.querySelector('.pfeai-tab-content[data-tab="gaps"]') }; this.titlesEl = panel.querySelector('.pfeai-tab-content[data-tab="titles"]'); this.seoEl = panel.querySelector('.pfeai-tab-content[data-tab="seo"]'); this.ideasEl = panel.querySelector('.pfeai-tab-content[data-tab="ideas"]'); this.transcriptEl = panel.querySelector('.pfeai-tab-content[data-tab="transcript"]'); this.socialEl = panel.querySelector('.pfeai-tab-content[data-tab="social"]'); this.savedEl = panel.querySelector('.pfeai-tab-content[data-tab="saved"]'); this.factsEl = panel.querySelector('.pfeai-tab-content[data-tab="facts"]'); this.linksEl = panel.querySelector('.pfeai-tab-content[data-tab="links"]'); this.faqEl = panel.querySelector('.pfeai-tab-content[data-tab="faq"]'); panel.querySelector('.pfeai-faq-btn').addEventListener('click', function() { self.runFAQ(editor); }); panel.querySelector('#btn-internal-links').addEventListener('click', function() { var btnText = this.innerText; if (btnText.indexOf('View Links') !== -1) { self.showResults('links'); } else { self.runInternalLinks(editor); } }); // Add this new listener for the Search button panel.querySelector('#btn-search').addEventListener('click', function() { self.showResults('search'); }); var self = this; panel.querySelector('.pfeai-close').addEventListener('click', function(e) { e.stopPropagation(); self.togglePanel(false); }); panel.querySelector('.pfeai-minimize-btn').addEventListener('click', function(e) { e.stopPropagation(); self.toggleMinimize(); }); panel.querySelector('.pfeai-back-btn').addEventListener('click', function(e) { e.stopPropagation(); self.showMenu(); }); var viewSavedBtn = document.getElementById('btn-view-saved'); if (viewSavedBtn) { viewSavedBtn.addEventListener('click', function() { self.showResults('saved'); }); } var clearSavedBtn = document.getElementById('btn-clear-saved'); if (clearSavedBtn) { clearSavedBtn.addEventListener('click', function() { if (confirm('Are you sure you want to clear your saved draft? This cannot be undone.')) { self.clearClipboard(); } }); } panel.querySelector('#btn-scan').addEventListener('click', function() { var btnText = this.innerText; if (btnText.indexOf('View Analysis') !== -1) { self.showResults('highlights'); } else { self.runAnalysis(editor); } }); panel.querySelector('#btn-social').addEventListener('click', function() { var btnText = this.innerText; if (btnText.indexOf('View Social') !== -1) { self.showResults('social'); } else { self.runSocialPack(editor); } }); panel.querySelector('#btn-transcript').addEventListener('click', function() { var btnText = this.innerText; if (btnText.indexOf('View Transcript') !== -1) { self.showResults('transcript'); } else { self.runTranscriptScan(editor); } }); panel.querySelector('#btn-titles').addEventListener('click', function() { var btnText = this.innerText; if (btnText.indexOf('View Titles') !== -1) { self.showResults('titles'); } else { self.runTitles(editor); } }); panel.querySelector('#btn-ideas').addEventListener('click', function() { var btnText = this.innerText; if (btnText.indexOf('View Topics') !== -1) { self.showResults('ideas'); } else { self.runArticleIdeas(editor); } }); // Clear Ideas Cache button (only exists if there's cached data) var clearIdeasBtn = panel.querySelector('#btn-clear-ideas'); if (clearIdeasBtn) { clearIdeasBtn.addEventListener('click', function() { if (confirm('Clear cached Topic Ideas? This will reset the Topics tab.')) { self.clearIdeasCache(); } }); } panel.querySelector('.pfeai-tagline-btn').addEventListener('click', function() { self.runTagline(editor); }); panel.querySelector('.pfeai-tags-btn').addEventListener('click', function() { self.runTagSuggestions(editor); }); panel.querySelector('.pfeai-meta-btn').addEventListener('click', function() { self.runMetaDescFromMenu(editor); }); panel.querySelectorAll('.pfeai-tab').forEach(function(btn) { btn.addEventListener('click', function() { self.switchTab(this.getAttribute('data-tab')); }); }); this.panelCreated = true; }, runSocialPack: function(editor, forceRefresh) { var self = this; var contentHtml = this.getSafeContent(editor); var title = (document.getElementById('title') || {}).value || ''; if (!title) { alert('Please enter a title.'); return; } this.showResults('social'); this.setLoading(true); fetch(PFEAI.rest_url + 'social-pack?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ content: contentHtml, title: title, post_id: PFEAI.post_id, provider: this.getProvider(), force_refresh: forceRefresh || false }) }) .then(function(r){return r.json()}).then(function(json){ self.setLoading(false); if(json.tweets) { self.renderSocialPack(json); var timestamp = json.generated_at || Date.now()/1000; self.setCachedState('btn-social', 'View Social Posts', 'social', timestamp, json.from_cache); self.startCacheWindow(json.from_cache); } else if (json.code) { // Handle error response self.socialEl.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

'; } }).catch(function(e) { self.setLoading(false); console.error(e); }); }, renderSocialPack: function(data) { var self = this; this.socialEl.innerHTML = '

Social Posts

'; (data.tweets || []).forEach(function(t, idx) { self.createSocialCard('Twitter / X ('+(idx+1)+')', t, self.socialEl); }); if (data.facebook) self.createSocialCard('Facebook', data.facebook, self.socialEl); if (data.meta_desc) self.createSocialCard('Meta Description (SEO)', data.meta_desc, self.socialEl); }, /** * SECURITY FIX: Use textContent instead of innerHTML for user data */ createSocialCard: function(label, text, container) { var card = document.createElement('div'); card.className = 'pfeai-card'; card.style.background = '#f5f5f5'; var labelEl = document.createElement('strong'); labelEl.textContent = label; card.appendChild(labelEl); var textEl = document.createElement('p'); textEl.style.fontSize = '12px'; textEl.style.marginBottom = '5px'; textEl.textContent = text; // SECURITY: textContent escapes HTML card.appendChild(textEl); var copyBtn = document.createElement('button'); copyBtn.className = 'button button-small'; copyBtn.textContent = 'Copy'; copyBtn.onclick = function() { navigator.clipboard.writeText(text); copyBtn.textContent = 'Copied!'; setTimeout(function(){ copyBtn.textContent = 'Copy'; }, 1500); }; card.appendChild(copyBtn); container.appendChild(card); }, runAnalysis: function(editor, forceRefresh) { var self = this; var contentHtml = this.getSafeContent(editor); var title = (document.getElementById('title') || {}).value || ''; if (!title) { alert('Please enter a title.'); return; } this.showResults('highlights'); this.setLoading(true); fetch(PFEAI.rest_url + 'analyze?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ content: contentHtml, title: title, post_id: PFEAI.post_id, provider: this.getProvider(), force_refresh: forceRefresh || false }) }) .then(function(r){return r.json()}).then(function(json){ self.setLoading(false); if(json.highlights) { self.renderAnalysis(json); var timestamp = json.generated_at || Date.now()/1000; self.setCachedState('btn-scan', 'View Analysis Results', 'highlights', timestamp, json.from_cache); self.startCacheWindow(json.from_cache); } else if (json.code) { self.analysisEl.highlights.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

'; } }).catch(function(e) { self.setLoading(false); console.error(e); }); }, runTitles: function(editor, forceRefresh) { var self = this; var contentHtml = this.getSafeContent(editor); var title = (document.getElementById('title') || {}).value || ''; this.showResults('titles'); this.setLoading(true); fetch(PFEAI.rest_url + 'seo-titles?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ content: contentHtml, title: title, post_id: PFEAI.post_id, provider: this.getProvider(), force_refresh: forceRefresh || false }) }) .then(function(r){return r.json()}).then(function(json){ self.setLoading(false); if(json.titles) { self.renderTitles(json); var timestamp = json.generated_at || Date.now()/1000; self.setCachedState('btn-titles', 'View Titles', 'titles', timestamp, json.from_cache); self.startCacheWindow(json.from_cache); } else if (json.code) { self.titlesEl.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

'; } }); }, runTranscriptScan: function(editor, forceRefresh) { var self = this; var contentText = this.getSafeText(editor); if (contentText.length < 50) { alert('Please paste transcript.'); return; } this.showResults('transcript'); this.setLoading(true); fetch(PFEAI.rest_url + 'transcript-scan?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ content: contentText, post_id: PFEAI.post_id, provider: this.getProvider(), force_refresh: forceRefresh || false }) }) .then(function(r){return r.json()}) .then(function(json){ self.setLoading(false); console.log('Transcript scan response:', json); // Handle the response - could be {suggestions: [...]} or a single object or bad cache var suggestions = null; if (json.suggestions && Array.isArray(json.suggestions)) { // Correct format: {suggestions: [{...}, {...}]} suggestions = json.suggestions; } else if (json.title && json.context) { // Bad format: single object at root (legacy cache issue) // Wrap it in an array suggestions = [json]; console.warn('Transcript response was single object, wrapped in array. Consider clearing cache.'); } if (suggestions && suggestions.length > 0) { self.renderTranscriptSuggestions(suggestions, contentText); var timestamp = json.generated_at || Date.now()/1000; self.setCachedState('btn-transcript', 'View Transcript Results', 'transcript', timestamp, json.from_cache); self.startCacheWindow(json.from_cache); } else if (json.code) { self.transcriptEl.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

'; } else { console.error('Unexpected transcript response:', json); self.transcriptEl.innerHTML = '

Unexpected response format. Please click "Rescan" to refresh.

'; } }) .catch(function(err) { self.setLoading(false); console.error('Transcript scan error:', err); self.transcriptEl.innerHTML = '

Request failed: ' + self.escapeHtml(err.message) + '

'; }); }, /** * IMPROVED: Added better error handling and timeout management for transcript outline */ runTranscriptOutline: function(title, content, clickedCard) { var self = this; var resDiv = document.getElementById('pfeai-transcript-result'); // Show loading state with timeout warning resDiv.innerHTML = '

Generating outline...

This may take up to 30 seconds for longer transcripts.

'; // Validate inputs if (!title) { resDiv.innerHTML = '

Error: No headline selected. Please click a headline above.

'; return; } if (!content || content.length < 50) { resDiv.innerHTML = '

Error: Transcript content is too short or missing.

'; return; } // Create abort controller for timeout var controller = new AbortController(); var timeoutId = setTimeout(function() { controller.abort(); }, 180000); // 3 minute timeout (matching server timeout) fetch(PFEAI.rest_url + 'transcript-outline?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ selected_title: title, content: content, provider: this.getProvider() }), signal: controller.signal }) .then(function(r) { clearTimeout(timeoutId); if (!r.ok) { throw new Error('Server returned status ' + r.status); } return r.json(); }) .then(function(json) { console.log('Transcript outline response:', json); // Check for error response if (json.code) { resDiv.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

Try selecting a different headline or click Rescan.

'; return; } // Check for required field (blurb) if (json.blurb) { self.renderTranscriptOutlineResult(json, title); } else { console.error('Missing blurb in response:', json); resDiv.innerHTML = '

AI response was incomplete. Please try again.

Response received but missing required content.

'; } }) .catch(function(err) { clearTimeout(timeoutId); console.error('Transcript outline error:', err); var errorMsg = 'Error generating outline.'; if (err.name === 'AbortError') { errorMsg = 'Request timed out. The transcript may be too long.'; } else if (err.message) { errorMsg = err.message; } resDiv.innerHTML = '

' + self.escapeHtml(errorMsg) + '

Try again or select a different headline.

'; }); }, runArticleIdeas: function(editor, forceRefresh) { var self = this; var title = (document.getElementById('title') || {}).value || ''; var contentText = this.getSafeText(editor).trim(); // Only block if not forcing refresh AND there's content if (!forceRefresh && (title.length > 0 || contentText.length > 5)) { alert('To generate fresh Topic Inspiration, please clear the Title and Content editor first.'); return; } this.showResults('ideas'); this.setLoading(true); // Function to fetch new ideas var fetchIdeas = function() { fetch(PFEAI.rest_url + 'article-ideas?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ title: '', post_id: PFEAI.post_id, provider: self.getProvider(), force_refresh: forceRefresh || false }) }) .then(function(r){return r.json()}).then(function(json){ self.setLoading(false); if(json.ideas) { self.renderIdeasInteractive(json); self.setCachedState('btn-ideas', 'View Topics', 'ideas', Date.now()/1000); } else if (json.code) { self.ideasEl.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

'; } }).catch(function(e) { self.setLoading(false); console.error(e); }); }; // If forcing refresh, clear cache first, then fetch new ideas if (forceRefresh) { fetch(PFEAI.rest_url + 'clear-ideas-cache?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ post_id: PFEAI.post_id }) }) .then(function() { fetchIdeas(); }) .catch(function() { fetchIdeas(); }); // Still try to fetch even if clear fails } else { fetchIdeas(); } }, /** * IMPROVED: Added better error handling for idea outline */ runIdeaOutline: function(title, context) { var self = this; var resDiv = document.getElementById('pfeai-idea-result'); if(!resDiv) return; resDiv.innerHTML = '

Drafting outline...

This may take up to 30 seconds.

'; // Create abort controller for timeout var controller = new AbortController(); var timeoutId = setTimeout(function() { controller.abort(); }, 180000); // 3 minute timeout fetch(PFEAI.rest_url + 'idea-outline?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ idea_title: title, context: context, provider: this.getProvider() }), signal: controller.signal }) .then(function(r) { clearTimeout(timeoutId); if (!r.ok) { throw new Error('Server returned status ' + r.status); } return r.json(); }) .then(function(json) { console.log('Idea outline response:', json); if (json.code) { resDiv.innerHTML = '

Error: ' + self.escapeHtml(json.message) + '

'; return; } if(json.blurb) { self.renderIdeaOutlineResult(json, title); } else { resDiv.innerHTML = '

AI response was incomplete. Please try again.

'; } }) .catch(function(err) { clearTimeout(timeoutId); console.error('Idea outline error:', err); var errorMsg = 'Error generating outline.'; if (err.name === 'AbortError') { errorMsg = 'Request timed out. Please try again.'; } else if (err.message) { errorMsg = err.message; } resDiv.innerHTML = '

' + self.escapeHtml(errorMsg) + '

'; }); }, runTagline: function(editor) { var self = this; var c = this.getSafeContent(editor); var t = (document.getElementById('title') || {}).value || ''; if (!t) { alert('Enter title first'); return; } // Debug logging console.log('PFEAI Tagline: Content length = ' + c.length); // Show loading state on button var btn = this.panelEl.querySelector('.pfeai-tagline-btn'); var originalText = btn ? btn.textContent : ''; if (btn) { btn.disabled = true; btn.innerHTML = ' Thinking...'; } fetch(PFEAI.rest_url + 'generate-tagline?_t=' + Date.now(), { method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':PFEAI.nonce}, body:JSON.stringify({content:c, title:t, provider: this.getProvider()}) }) .then(function(r){ console.log('PFEAI Tagline: Response status = ' + r.status); return r.json(); }) .then(function(j){ console.log('PFEAI Tagline: Response = ', j); // Restore button state if (btn) { btn.disabled = false; btn.textContent = originalText; } if(j.tagline) { document.getElementById('pftwt_tagline').value = j.tagline; self.startCacheWindow(false); // API call was made } else if (j.code) { alert('Error: ' + j.message); } else { console.warn('PFEAI Tagline: No tagline in response', j); alert('No tagline was generated. Please try again.'); } }) .catch(function(e) { console.error('PFEAI Tagline: Error = ', e); // Restore button state on error if (btn) { btn.disabled = false; btn.textContent = originalText; } alert('Error: ' + e.message); }); }, runTagSuggestions: function(editor) { var self = this; var c = this.getSafeContent(editor); var t = (document.getElementById('title') || {}).value || ''; // Debug: log content length console.log('PFEAI Tags: Content length = ' + c.length + ', Title = ' + t); if (!c || c.length < 50) { alert('Please add more content before generating tags.'); return; } // Show loading state on button var btn = this.panelEl.querySelector('.pfeai-tags-btn'); var originalText = btn ? btn.textContent : ''; if (btn) { btn.disabled = true; btn.innerHTML = ' Thinking...'; } fetch(PFEAI.rest_url + 'suggest-tags?_t=' + Date.now(), { method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':PFEAI.nonce}, body:JSON.stringify({content:c, title:t, provider: this.getProvider()}) }) .then(function(r){ console.log('PFEAI Tags: Response status = ' + r.status); return r.json(); }) .then(function(j){ console.log('PFEAI Tags: Response = ', j); // Restore button state if (btn) { btn.disabled = false; btn.textContent = originalText; } // Check for error response if (j.code) { alert('Error generating tags: ' + (j.message || j.code)); return; } if(j.tags && j.tags.length > 0 && jQuery) { var $ = jQuery; var tagsAdded = 0; var tagBox = $('#post_tag'); // Method 1: Use WordPress tagBox API if available if (window.tagBox && typeof window.tagBox.flushTags === 'function') { var $input = $('#new-tag-post_tag'); j.tags.forEach(function(tag) { $input.val(tag); // Trigger the tag addition window.tagBox.flushTags(tagBox, false, 1); tagsAdded++; }); } else { // Method 2: Manually add tags and trigger events var $input = $('#new-tag-post_tag'); var $addBtn = $('#post_tag .tagadd'); j.tags.forEach(function(tag, index) { // Small delay between additions to let WordPress process each one setTimeout(function() { $input.val(tag); $addBtn.trigger('click'); // Also try triggering the keypress enter event as backup var e = $.Event('keypress'); e.which = 13; // Enter key $input.trigger(e); tagsAdded++; // On last tag, show success message if (index === j.tags.length - 1) { setTimeout(function() { // Force refresh the tag cloud display if (window.tagBox && typeof window.tagBox.get === 'function') { window.tagBox.get('post_tag'); } console.log('PFEAI Tags: Added ' + j.tags.length + ' tags'); }, 100); } }, index * 50); // 50ms delay between each tag }); } self.startCacheWindow(false); // API call was made // Show feedback to user if (btn) { btn.textContent = 'Added ' + j.tags.length + ' tags!'; setTimeout(function() { btn.textContent = originalText; }, 2000); } console.log('PFEAI Tags: Added ' + j.tags.length + ' tags'); } else { console.warn('PFEAI Tags: No tags returned or empty array', j); alert('No tags were generated. Please try again.'); } }) .catch(function(e) { console.error('PFEAI Tags: Error = ', e); // Restore button state on error if (btn) { btn.disabled = false; btn.textContent = originalText; } alert('Error: ' + e.message); }); }, /** * Run Meta Description from the main menu button * Shows loading on the menu button and navigates to SEO tab with result */ runMetaDescFromMenu: function(editor) { var self = this; var title = (document.getElementById('title') || {}).value || ''; var content = this.getSafeContent(editor); if (!title && !content) { alert('Please add a title or content first.'); return; } // Show loading state on the menu button var btn = this.panelEl.querySelector('.pfeai-meta-btn'); var originalText = btn ? btn.textContent : ''; if (btn) { btn.disabled = true; btn.innerHTML = ' Thinking...'; } fetch(PFEAI.rest_url + 'generate-meta-desc?_t=' + Date.now(), { method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':PFEAI.nonce}, body: JSON.stringify({content: content, title: title, provider: this.getProvider()}) }) .then(function(r){return r.json()}) .then(function(j){ // Restore button state if (btn) { btn.disabled = false; btn.textContent = originalText; } if(j.meta_description) { // Navigate to SEO tab and show result self.showResults('seo'); // Fill in the meta description textarea var textarea = document.getElementById('pfeai-meta-description'); if (textarea) { textarea.value = j.meta_description; self.updateSerpPreview(); } self.startCacheWindow(false); // API call was made // Show success feedback on button briefly if (btn) { btn.textContent = 'Generated!'; setTimeout(function() { btn.textContent = originalText; }, 2000); } } else if (j.code) { alert('Error: ' + j.message); } else { alert('No meta description was generated. Please try again.'); } }) .catch(function(e) { // Restore button state on error if (btn) { btn.disabled = false; btn.textContent = originalText; } console.error('PFEAI Meta Desc: Error = ', e); alert('Error: ' + e.message); }); }, runMetaDescription: function() { var self = this; var title = (document.getElementById('title') || {}).value || ''; var content = this.getSafeContent(tinymce.activeEditor); if (!title && !content) { alert('Please add a title or content first.'); return; } var btn = document.getElementById('pfeai-generate-meta-btn'); if (btn) { btn.textContent = 'Generating...'; btn.disabled = true; } fetch(PFEAI.rest_url + 'generate-meta-desc?_t=' + Date.now(), { method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':PFEAI.nonce}, body: JSON.stringify({content: content, title: title, provider: this.getProvider()}) }) .then(function(r){return r.json()}) .then(function(j){ if (btn) { btn.innerHTML = '✨ Generate New Meta Description'; btn.disabled = false; } if(j.meta_description) { var textarea = document.getElementById('pfeai-meta-description'); if (textarea) { textarea.value = j.meta_description; self.updateSerpPreview(); } self.startCacheWindow(false); // API call was made } else if (j.code) { alert('Error: ' + j.message); } }) .catch(function(e) { if (btn) { btn.innerHTML = '✨ Generate New Meta Description'; btn.disabled = false; } console.error(e); }); }, runFAQ: function(editor) { var self = this; var c = this.getSafeContent(editor); var t = (document.getElementById('title') || {}).value || ''; if (!c || c.length < 100) { alert('Please write or paste the article content first so the AI can generate accurate FAQs.'); return; } this.showResults('faq'); this.setLoading(true); fetch(PFEAI.rest_url + 'generate-faq?_t=' + Date.now(), { method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':PFEAI.nonce}, body: JSON.stringify({content: c, title: t, provider: this.getProvider()}) }) .then(function(r){ return r.json(); }) .then(function(j){ self.setLoading(false); if(j.faq && j.faq.length > 0) { self.renderFAQ(j.faq, editor); self.startCacheWindow(false); } else if (j.code) { self.faqEl.innerHTML = '

Error: ' + self.escapeHtml(j.message) + '

'; } else { self.faqEl.innerHTML = '

Failed to generate FAQ. Please try again.

'; } }) .catch(function(e) { self.setLoading(false); self.faqEl.innerHTML = '

Error: ' + self.escapeHtml(e.message) + '

'; }); }, renderFAQ: function(faqData, editor) { var self = this; if (!this.faqEl) return; this.faqEl.innerHTML = ''; var header = document.createElement('h4'); header.style.margin = '0 0 10px 0'; header.textContent = 'Review & Edit FAQ'; this.faqEl.appendChild(header); var instruction = document.createElement('p'); instruction.style.fontSize = '11px'; instruction.style.color = '#666'; instruction.textContent = 'Review your FAQs below. Clicking the button will send them down to the "Article FAQ & Schema" box at the bottom of your post screen. Remember to click the WordPress Update/Publish button to save them!'; this.faqEl.appendChild(instruction); var faqContainer = document.createElement('div'); faqContainer.id = 'pfeai-faq-inputs-container'; // Build editable inputs for each Q&A in the sidebar faqData.forEach(function(item, index) { var box = document.createElement('div'); box.style.background = '#fff'; box.style.padding = '10px'; box.style.marginBottom = '10px'; box.style.border = '1px solid #ddd'; box.style.borderRadius = '4px'; var qLabel = document.createElement('strong'); qLabel.style.fontSize = '11px'; qLabel.textContent = 'Question ' + (index + 1); box.appendChild(qLabel); var qInput = document.createElement('input'); qInput.type = 'text'; qInput.className = 'pfeai-faq-q'; qInput.style.width = '100%'; qInput.style.marginBottom = '8px'; qInput.value = item.question || ''; box.appendChild(qInput); var aLabel = document.createElement('strong'); aLabel.style.fontSize = '11px'; aLabel.textContent = 'Answer ' + (index + 1); box.appendChild(aLabel); var aInput = document.createElement('textarea'); aInput.className = 'pfeai-faq-a'; aInput.style.width = '100%'; aInput.style.height = '50px'; aInput.style.fontSize = '12px'; aInput.value = item.answer || ''; box.appendChild(aInput); faqContainer.appendChild(box); }); this.faqEl.appendChild(faqContainer); // The Button to transfer text to the Meta Box var insertBtn = document.createElement('button'); insertBtn.type = 'button'; insertBtn.className = 'button button-primary'; insertBtn.style.width = '100%'; insertBtn.innerHTML = '↓ Send to FAQ Meta Box'; insertBtn.onclick = function() { var qInputs = faqContainer.querySelectorAll('.pfeai-faq-q'); var aInputs = faqContainer.querySelectorAll('.pfeai-faq-a'); var transferCount = 0; for (var i = 0; i < qInputs.length; i++) { var qText = qInputs[i].value.trim(); var aText = aInputs[i].value.trim(); // Locate the PHP Meta Box inputs on the main post screen var metaQ = document.getElementById('pfeai_meta_q_' + i); var metaA = document.getElementById('pfeai_meta_a_' + i); if (metaQ && metaA) { metaQ.value = qText; metaA.value = aText; if (qText && aText) transferCount++; } } if (transferCount === 0) { alert('Could not locate the FAQ Meta Box on the screen. Please ensure "Article FAQ & Schema" is checked in your Screen Options at the top right.'); return; } insertBtn.innerHTML = '✓ Sent to Meta Box!'; setTimeout(function() { insertBtn.innerHTML = '↓ Send to FAQ Meta Box'; }, 2000); }; this.faqEl.appendChild(insertBtn); }, updateSerpPreview: function(selectedTitle) { var titleEl = document.getElementById('pfeai-serp-title'); var descEl = document.getElementById('pfeai-serp-desc'); var charCount = document.getElementById('pfeai-meta-char-count'); var titleCharNote = document.getElementById('pfeai-title-char-note'); var metaTextarea = document.getElementById('pfeai-meta-description'); // Update title if provided if (selectedTitle && titleEl) { titleEl.textContent = selectedTitle; // Truncate if too long (Google truncates around 60 chars) if (selectedTitle.length > 60) { titleEl.textContent = selectedTitle.substring(0, 57) + '...'; } } // Update title character count if (titleCharNote && titleEl) { var currentTitle = selectedTitle || titleEl.textContent; if (currentTitle === '(Select a title above)') currentTitle = ''; var titleLen = currentTitle.length; var titleColor = titleLen <= 60 ? '#2e7d32' : '#c62828'; titleCharNote.innerHTML = 'Title: ' + titleLen + '/60 chars ' + (titleLen <= 60 ? '✓' : '(may be truncated)'); } // Update description if (metaTextarea && descEl) { var desc = metaTextarea.value; if (desc) { descEl.textContent = desc.length > 155 ? desc.substring(0, 152) + '...' : desc; } else { descEl.textContent = '(Enter a meta description above)'; } } // Update character count with color coding if (metaTextarea && charCount) { var len = metaTextarea.value.length; var color = '#666'; var status = ''; if (len === 0) { color = '#666'; } else if (len < 120) { color = '#e65100'; // Orange - too short status = ' (too short)'; } else if (len >= 120 && len <= 155) { color = '#2e7d32'; // Green - optimal status = ' ✓ optimal'; } else if (len > 155 && len <= 160) { color = '#e65100'; // Orange - slightly long status = ' (slightly long)'; } else { color = '#c62828'; // Red - too long status = ' (will be truncated)'; } charCount.innerHTML = '' + len + ' / 155 characters' + status + ''; } }, saveToClipboard: function(title, html, redirectToNew) { fetch(PFEAI.rest_url + 'save-clipboard?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ title: title, content: html }) }) .then(function(){ if (redirectToNew) { // Redirect to new post page window.open('/patriots/blog/wp-admin/post-new.php', '_blank'); } else { alert('Saved! You can now access this draft in the "Saved" tab on any new post.'); location.reload(); } }); }, clearClipboard: function() { fetch(PFEAI.rest_url + 'clear-clipboard?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce } }) .then(function(r) { return r.json(); }) .then(function(json) { if (json.success) { alert('Saved draft cleared.'); location.reload(); } }) .catch(function(e) { console.error(e); }); }, clearIdeasCache: function() { fetch(PFEAI.rest_url + 'clear-ideas-cache?_t=' + Date.now(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': PFEAI.nonce }, body: JSON.stringify({ post_id: PFEAI.post_id }) }) .then(function(r) { return r.json(); }) .then(function(json) { if (json.success) { alert('Topics cache cleared.'); location.reload(); } }) .catch(function(e) { console.error(e); }); }, togglePanel: function(forceState) { if (!this.panelEl) return; var open = this.panelEl.classList.contains('pfeai-open'); var shouldOpen = (typeof forceState === 'boolean') ? forceState : !open; if (shouldOpen) { this.panelEl.classList.add('pfeai-open'); document.body.classList.add('pfeai-panel-open'); this.updateTranscriptVisibility(); if (this.isMinimized) document.body.classList.add('pfeai-panel-minimized'); else document.body.classList.remove('pfeai-panel-minimized'); } else { this.panelEl.classList.remove('pfeai-open'); document.body.classList.remove('pfeai-panel-open'); } }, showMenu: function() { this.panelEl.classList.remove('pfeai-show-results'); }, showResults: function(activeTab) { this.panelEl.classList.add('pfeai-show-results'); if (activeTab) this.switchTab(activeTab); }, updateTranscriptVisibility: function() { var isTranscript = false; var labels = document.querySelectorAll('.categorychecklist label'); for (var i = 0; i < labels.length; i++) { if (labels[i].innerText.indexOf('Patriots Transcripts') !== -1) { var input = labels[i].querySelector('input[type="checkbox"]'); if (input && input.checked) isTranscript = true; } } var btn = document.getElementById('btn-transcript'); var tab = document.getElementById('tab-transcript'); if (isTranscript) { if (btn) btn.style.display = 'block'; if (tab) tab.style.display = 'inline-block'; } else { if (btn) btn.style.display = 'none'; if (tab) tab.style.display = 'none'; } }, preloadSavedData: function() { if (!PFEAI.saved_data) return; if (PFEAI.saved_data.transcript && PFEAI.saved_data.transcript.suggestions) { this.renderTranscriptSuggestions(PFEAI.saved_data.transcript.suggestions, ''); this.setCachedState('btn-transcript', 'View Transcript Results', 'transcript', PFEAI.saved_data.transcript.generated_at); } if (PFEAI.saved_data.analysis && PFEAI.saved_data.analysis.highlights) { this.renderAnalysis(PFEAI.saved_data.analysis); this.setCachedState('btn-scan', 'View Analysis Results', 'highlights', PFEAI.saved_data.analysis.generated_at); } if (PFEAI.saved_data.titles && PFEAI.saved_data.titles.titles) { this.renderTitles(PFEAI.saved_data.titles); this.setCachedState('btn-titles', 'View Titles', 'titles', PFEAI.saved_data.titles.generated_at); } if (PFEAI.saved_data.ideas && PFEAI.saved_data.ideas.ideas) { this.renderIdeasInteractive(PFEAI.saved_data.ideas); this.setCachedState('btn-ideas', 'View Topics', 'ideas', PFEAI.saved_data.ideas.generated_at); } if (PFEAI.saved_data.social && PFEAI.saved_data.social.tweets) { this.renderSocialPack(PFEAI.saved_data.social); this.setCachedState('btn-social', 'View Social Posts', 'social', PFEAI.saved_data.social.generated_at); } if (PFEAI.saved_data.clipboard && this.savedEl) { this.renderSavedDraft(PFEAI.saved_data.clipboard); } if (PFEAI.saved_data.fact_check && PFEAI.saved_data.fact_check.factChecks) { this.renderFacts(PFEAI.saved_data.fact_check.factChecks); this.setCachedState('btn-fact-check', 'View Fact Check', 'facts', PFEAI.saved_data.fact_check.generated_at); } if (PFEAI.saved_data.links && PFEAI.saved_data.links.links) { this.renderInternalLinks(PFEAI.saved_data.links, null); this.setCachedState('btn-internal-links', '\uD83D\uDD17 View Links', 'links', PFEAI.saved_data.links.generated_at); } // Always render the SEO tab this.renderSeoTab(); // Render the Search Insights data this.renderGSCPanel(); }, setCachedState: function(btnId, newText, tabName, timestamp) { var btn = document.getElementById(btnId); if (btn) { if (btnId === 'btn-transcript' || btnId === 'btn-ideas' || btnId === 'btn-social') { btn.innerHTML = '' + this.escapeHtml(newText) + '
(Click to see saved)'; } else { btn.textContent = newText; } } var container = this.panelEl.querySelector('.pfeai-tab-content[data-tab="' + tabName + '"]'); if (container) { var existingHeader = container.querySelector('.pfeai-cache-header'); if (existingHeader) existingHeader.remove(); var dateStr = timestamp ? new Date(timestamp * 1000).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}) : ''; var header = document.createElement('div'); header.className = 'pfeai-cache-header'; header.style.background = '#e1f5fe'; header.style.padding = '8px'; header.style.marginBottom = '10px'; header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.borderRadius = '4px'; var savedSpan = document.createElement('span'); savedSpan.style.fontSize = '11px'; savedSpan.style.color = '#0277bd'; savedSpan.textContent = 'Saved ' + dateStr; header.appendChild(savedSpan); var rescanBtn = document.createElement('button'); rescanBtn.className = 'button button-small'; rescanBtn.textContent = 'Rescan / Start Over'; (function(name) { rescanBtn.onclick = function() { if (name === 'highlights') window.PFEditorialAI.runAnalysis(tinymce.activeEditor, true); if (name === 'transcript') window.PFEditorialAI.runTranscriptScan(tinymce.activeEditor, true); if (name === 'titles') window.PFEditorialAI.runTitles(tinymce.activeEditor, true); if (name === 'ideas') window.PFEditorialAI.runArticleIdeas(tinymce.activeEditor, true); if (name === 'social') window.PFEditorialAI.runSocialPack(tinymce.activeEditor, true); if (name === 'facts') window.PFEditorialAI.runFactCheck(tinymce.activeEditor, true); if (name === 'links') window.PFEditorialAI.runInternalLinks(tinymce.activeEditor, true); }; })(tabName); header.appendChild(rescanBtn); container.insertBefore(header, container.firstChild); } }, /** * SECURITY FIX: renderAnalysis now uses proper escaping */ renderAnalysis: function(data) { var self = this; var hEl = this.analysisEl.highlights; hEl.innerHTML = ''; (data.highlights || []).forEach(function(h) { var c = document.createElement('div'); c.className = 'pfeai-card'; var typeEl = document.createElement('strong'); typeEl.textContent = (h.type || '').toUpperCase(); c.appendChild(typeEl); var msgEl = document.createElement('p'); msgEl.textContent = h.message || ''; c.appendChild(msgEl); var quoteEl = document.createElement('div'); quoteEl.className = 'pfeai-card-quote'; quoteEl.textContent = '"' + (h.quote || '') + '"'; c.appendChild(quoteEl); var suggEl = document.createElement('div'); suggEl.className = 'pfeai-card-suggestion'; suggEl.textContent = h.suggestion || ''; c.appendChild(suggEl); hEl.appendChild(c); }); var sEl = this.analysisEl.suggestions; sEl.innerHTML = ''; var ulS = document.createElement('ul'); ulS.className = 'pfeai-list'; (data.summarySuggestions || []).forEach(function(s){ var li = document.createElement('li'); li.textContent = s; // SECURITY: textContent escapes HTML ulS.appendChild(li); }); sEl.appendChild(ulS); var gEl = this.analysisEl.gaps; gEl.innerHTML = ''; var ulG = document.createElement('ul'); ulG.className = 'pfeai-list'; (data.contentGaps || []).forEach(function(g){ var li = document.createElement('li'); li.textContent = g; ulG.appendChild(li); }); gEl.appendChild(ulG); // === FACT CHECKS === var fEl = self.factsEl; if (fEl) { fEl.innerHTML = ''; var checks = data.factChecks || []; var factsHeading = document.createElement('h4'); factsHeading.style.margin = '0 0 4px 0'; factsHeading.style.color = '#1A2635'; factsHeading.textContent = 'Stats & Data Verification'; fEl.appendChild(factsHeading); var factsNote = document.createElement('p'); factsNote.style.fontSize = '11px'; factsNote.style.color = '#666'; factsNote.style.margin = '0 0 12px 0'; factsNote.textContent = 'AI-reviewed claims found in the article. Always cross-check flagged items before publishing.'; fEl.appendChild(factsNote); if (checks.length === 0) { var noFacts = document.createElement('p'); noFacts.style.color = '#666'; noFacts.style.fontSize = '13px'; noFacts.textContent = 'No specific stats or data claims detected in this article.'; fEl.appendChild(noFacts); } else { // Summary badge row var counts = { 'Likely Accurate': 0, 'Needs Verification': 0, 'Potentially Incorrect': 0, 'May Be Outdated': 0 }; checks.forEach(function(fc) { if (counts[fc.verdict] !== undefined) counts[fc.verdict]++; }); var badgeRow = document.createElement('div'); badgeRow.style.cssText = 'display:flex; flex-wrap:wrap; gap:6px; margin-bottom:12px;'; var badgeDefs = [ { key: 'Likely Accurate', bg: '#e8f5e9', color: '#2e7d32', icon: '✓' }, { key: 'Needs Verification', bg: '#fff8e1', color: '#f57c00', icon: 'âš ' }, { key: 'May Be Outdated', bg: '#e3f2fd', color: '#1565c0', icon: '📅' }, { key: 'Potentially Incorrect', bg: '#ffebee', color: '#c62828', icon: '✗' } ]; badgeDefs.forEach(function(bd) { if (!counts[bd.key]) return; var badge = document.createElement('span'); badge.style.cssText = 'background:' + bd.bg + '; color:' + bd.color + '; border:1px solid ' + bd.color + '; border-radius:12px; padding:2px 8px; font-size:11px; font-weight:700;'; badge.textContent = bd.icon + ' ' + counts[bd.key] + ' ' + bd.key; badgeRow.appendChild(badge); }); fEl.appendChild(badgeRow); var styleMap = { 'Likely Accurate': { border: '#4caf50', bg: '#f9fbe7', tagBg: '#e8f5e9', tagColor: '#2e7d32', icon: '✓' }, 'Needs Verification': { border: '#ff9800', bg: '#fffde7', tagBg: '#fff3e0', tagColor: '#e65100', icon: 'âš ' }, 'May Be Outdated': { border: '#2196f3', bg: '#e8f4fd', tagBg: '#e3f2fd', tagColor: '#1565c0', icon: '📅' }, 'Potentially Incorrect': { border: '#f44336', bg: '#fff5f5', tagBg: '#ffebee', tagColor: '#c62828', icon: '✗' } }; checks.forEach(function(fc) { var verdict = fc.verdict || 'Needs Verification'; var s = styleMap[verdict] || styleMap['Needs Verification']; var card = document.createElement('div'); card.className = 'pfeai-fact-card'; card.style.cssText = 'background:' + s.bg + '; border-left:4px solid ' + s.border + '; border-radius:3px; padding:8px 10px; margin-bottom:8px;'; var tag = document.createElement('span'); tag.style.cssText = 'display:inline-block; background:' + s.tagBg + '; color:' + s.tagColor + '; border:1px solid ' + s.border + '; border-radius:10px; padding:1px 7px; font-size:10px; font-weight:700; margin-bottom:5px;'; tag.textContent = s.icon + ' ' + verdict; card.appendChild(tag); var statEl = document.createElement('div'); statEl.style.cssText = 'font-size:13px; font-weight:600; color:#1A2635; font-style:italic; margin-bottom:4px;'; statEl.textContent = '\u201c' + (fc.stat || '') + '\u201d'; card.appendChild(statEl); var noteEl = document.createElement('div'); noteEl.style.cssText = 'font-size:12px; color:#444; line-height:1.4;'; noteEl.textContent = fc.note || ''; card.appendChild(noteEl); fEl.appendChild(card); }); } // Update the Facts tab label with a warning badge count var flagged = checks.filter(function(fc) { return fc.verdict === 'Needs Verification' || fc.verdict === 'Potentially Incorrect' || fc.verdict === 'May Be Outdated'; }).length; var factsTab = self.panelEl ? self.panelEl.querySelector('.pfeai-tab[data-tab="facts"]') : null; if (factsTab) { factsTab.innerHTML = flagged > 0 ? '🔍 Facts ' + flagged + '' : '🔍 Facts ✓'; } } }, /** * SECURITY FIX: renderTitles now uses proper escaping */ renderTitles: function(data) { var self = this; this.titlesEl.innerHTML = ''; (data.titles || []).forEach(function(t) { var c = document.createElement('div'); c.className = 'pfeai-title-card'; c.style.display = 'flex'; c.style.flexDirection = 'column'; c.style.gap = '8px'; var td = document.createElement('div'); td.className = 'pfeai-title-text'; td.textContent = t.title; // SECURITY: textContent escapes HTML c.appendChild(td); var sr = document.createElement('div'); sr.className = 'pfeai-score-row'; var ctrSpan = document.createElement('span'); ctrSpan.innerHTML = 'CTR: ' + parseInt(t.ctr_score || 0, 10) + ''; sr.appendChild(ctrSpan); var discSpan = document.createElement('span'); discSpan.innerHTML = 'Disc: ' + parseInt(t.discover_score || 0, 10) + ''; sr.appendChild(discSpan); c.appendChild(sr); var btn = document.createElement('button'); btn.className = 'button button-small'; btn.textContent = 'Use This Title'; btn.style.alignSelf = 'flex-start'; (function(titleText) { btn.onclick = function() { var wt = document.getElementById('title'); if(wt) { wt.value = titleText; wt.focus(); var pt = document.getElementById('title-prompt-text'); if(pt) pt.classList.add('screen-reader-text'); btn.textContent = 'Updated!'; setTimeout(function(){ btn.textContent = 'Use This Title'; }, 2000); } // Update SERP preview with selected title self.updateSerpPreview(titleText); }; })(t.title); c.appendChild(btn); self.titlesEl.appendChild(c); }); }, /** * Render the SEO Meta Description tab */ renderSeoTab: function() { var self = this; if (!this.seoEl) return; this.seoEl.innerHTML = ''; // Get current title from the editor var currentTitle = (document.getElementById('title') || {}).value || ''; var serpHeader = document.createElement('h4'); serpHeader.style.margin = '0 0 15px 0'; serpHeader.style.color = '#1A2635'; serpHeader.textContent = 'SEO Meta Description'; this.seoEl.appendChild(serpHeader); var generateBtn = document.createElement('button'); generateBtn.type = 'button'; generateBtn.className = 'button button-primary'; generateBtn.id = 'pfeai-generate-meta-btn'; generateBtn.innerHTML = '✨ Generate New Meta Description'; generateBtn.style.marginBottom = '15px'; generateBtn.style.width = '100%'; generateBtn.onclick = function() { self.runMetaDescription(); }; this.seoEl.appendChild(generateBtn); var metaTextarea = document.createElement('textarea'); metaTextarea.id = 'pfeai-meta-description'; metaTextarea.style.width = '100%'; metaTextarea.style.height = '80px'; metaTextarea.style.fontSize = '12px'; metaTextarea.style.marginBottom = '5px'; metaTextarea.placeholder = 'Enter or generate a meta description (150-155 characters ideal)...'; metaTextarea.oninput = function() { self.updateSerpPreview(); }; this.seoEl.appendChild(metaTextarea); var charCount = document.createElement('div'); charCount.id = 'pfeai-meta-char-count'; charCount.style.fontSize = '11px'; charCount.style.marginBottom = '10px'; charCount.style.color = '#666'; charCount.textContent = '0 / 155 characters'; this.seoEl.appendChild(charCount); // Insert into Excerpt button var insertExcerptBtn = document.createElement('button'); insertExcerptBtn.type = 'button'; insertExcerptBtn.className = 'button'; insertExcerptBtn.id = 'pfeai-insert-excerpt-btn'; insertExcerptBtn.innerHTML = '→ Transfer to Article Excerpt Block'; insertExcerptBtn.style.marginBottom = '20px'; insertExcerptBtn.style.width = '100%'; insertExcerptBtn.onclick = function() { var metaTextarea = document.getElementById('pfeai-meta-description'); var excerptBox = document.getElementById('excerpt'); if (!metaTextarea || !metaTextarea.value.trim()) { alert('Please generate or enter a meta description first.'); return; } if (excerptBox) { excerptBox.value = metaTextarea.value; insertExcerptBtn.innerHTML = '✓ Inserted!'; setTimeout(function() { insertExcerptBtn.innerHTML = '→ Insert into Excerpt'; }, 2000); } else { alert('Excerpt box not found. Make sure the Excerpt panel is enabled in Screen Options.'); } }; this.seoEl.appendChild(insertExcerptBtn); // SERP Preview section var previewLabel = document.createElement('div'); previewLabel.style.fontSize = '11px'; previewLabel.style.color = '#666'; previewLabel.style.marginBottom = '5px'; previewLabel.textContent = 'Google Search Preview:'; this.seoEl.appendChild(previewLabel); var serpPreview = document.createElement('div'); serpPreview.id = 'pfeai-serp-preview'; serpPreview.style.background = '#fff'; serpPreview.style.border = '1px solid #ddd'; serpPreview.style.borderRadius = '8px'; serpPreview.style.padding = '12px'; serpPreview.style.fontFamily = 'Arial, sans-serif'; var serpTitle = document.createElement('div'); serpTitle.id = 'pfeai-serp-title'; serpTitle.style.color = '#1a0dab'; serpTitle.style.fontSize = '18px'; serpTitle.style.lineHeight = '1.3'; serpTitle.style.marginBottom = '3px'; serpTitle.style.cursor = 'pointer'; serpTitle.textContent = currentTitle || '(No title set)'; serpPreview.appendChild(serpTitle); var serpUrl = document.createElement('div'); serpUrl.style.color = '#006621'; serpUrl.style.fontSize = '13px'; serpUrl.style.marginBottom = '3px'; serpUrl.innerHTML = 'www.patsfans.com › patriots › blog'; serpPreview.appendChild(serpUrl); var serpDesc = document.createElement('div'); serpDesc.id = 'pfeai-serp-desc'; serpDesc.style.color = '#545454'; serpDesc.style.fontSize = '13px'; serpDesc.style.lineHeight = '1.4'; serpDesc.textContent = '(Enter a meta description above)'; serpPreview.appendChild(serpDesc); this.seoEl.appendChild(serpPreview); // Title character count var titleCharNote = document.createElement('div'); titleCharNote.id = 'pfeai-title-char-note'; titleCharNote.style.fontSize = '10px'; titleCharNote.style.color = '#666'; titleCharNote.style.marginTop = '8px'; this.seoEl.appendChild(titleCharNote); // Update the preview with current title this.updateSerpPreview(currentTitle); }, /** * SECURITY FIX: renderTranscriptSuggestions now uses proper escaping */ renderTranscriptSuggestions: function(suggestions, contentText) { var self = this; this.transcriptEl.innerHTML = ''; var h3 = document.createElement('h3'); h3.textContent = 'Suggested Angles'; this.transcriptEl.appendChild(h3); var p = document.createElement('p'); p.style.fontSize = '11px'; p.style.marginBottom = '10px'; p.textContent = 'Click to generate outline.'; this.transcriptEl.appendChild(p); suggestions.forEach(function(item) { var c = document.createElement('div'); c.className = 'pfeai-card pfeai-clickable'; c.style.cursor = 'pointer'; c.style.borderLeftColor = '#e67e22'; var h = document.createElement('h4'); h.style.margin = '0 0 4px 0'; h.style.color = '#d35400'; h.textContent = item.title; // SECURITY: textContent escapes HTML c.appendChild(h); var pEl = document.createElement('p'); pEl.style.fontSize = '12px'; pEl.textContent = item.context; // SECURITY: textContent escapes HTML c.appendChild(pEl); if (item.ctr_score || item.discover_score) { var sr = document.createElement('div'); sr.className = 'pfeai-score-row'; sr.style.marginTop = '6px'; sr.style.borderTop = '1px solid #eee'; sr.style.paddingTop = '4px'; sr.innerHTML = 'CTR: ' + parseInt(item.ctr_score || 0, 10) + 'Disc: ' + parseInt(item.discover_score || 0, 10) + ''; c.appendChild(sr); } (function(title, content) { c.addEventListener('click', function() { self.runTranscriptOutline(title, content, c); }); })(item.title, contentText); self.transcriptEl.appendChild(c); }); var rd = document.createElement('div'); rd.id = 'pfeai-transcript-result'; rd.style.marginTop = '15px'; rd.style.borderTop = '1px solid #ccc'; rd.style.paddingTop = '10px'; this.transcriptEl.appendChild(rd); }, /** * SECURITY FIX: renderTranscriptOutlineResult now uses proper escaping * ENHANCED: Now displays key quotes section */ renderTranscriptOutlineResult: function(data, title) { var self = this; var resDiv = document.getElementById('pfeai-transcript-result'); // Build DOM elements instead of innerHTML for safety var container = document.createElement('div'); container.style.background = '#fff'; container.style.padding = '10px'; container.style.border = '1px solid #ddd'; container.style.marginTop = '10px'; var h3 = document.createElement('h3'); h3.style.marginTop = '0'; h3.textContent = title; // SECURITY: textContent escapes HTML container.appendChild(h3); var blurbLabel = document.createElement('strong'); blurbLabel.textContent = 'Blurb:'; container.appendChild(blurbLabel); var blurbP = document.createElement('p'); blurbP.textContent = data.blurb || ''; // SECURITY: textContent escapes HTML container.appendChild(blurbP); // KEY QUOTES SECTION if (data.key_quotes && data.key_quotes.length > 0) { var quotesDiv = document.createElement('div'); quotesDiv.style.marginTop = '12px'; quotesDiv.style.marginBottom = '12px'; quotesDiv.style.padding = '10px'; quotesDiv.style.background = '#fff8e1'; quotesDiv.style.borderLeft = '4px solid #ffc107'; quotesDiv.style.borderRadius = '4px'; var quotesLabel = document.createElement('strong'); quotesLabel.style.display = 'block'; quotesLabel.style.marginBottom = '8px'; quotesLabel.style.color = '#f57c00'; quotesLabel.innerHTML = '💬 Key Quotes:'; quotesDiv.appendChild(quotesLabel); var quotesList = document.createElement('ul'); quotesList.style.margin = '0'; quotesList.style.paddingLeft = '0'; quotesList.style.listStyleType = 'none'; (data.key_quotes || []).forEach(function(quote) { var li = document.createElement('li'); li.style.marginBottom = '10px'; li.style.fontSize = '12px'; li.style.fontStyle = 'italic'; li.style.color = '#333'; li.style.paddingLeft = '20px'; li.style.position = 'relative'; li.style.lineHeight = '1.4'; // Opening quote mark var openQuote = document.createElement('span'); openQuote.style.position = 'absolute'; openQuote.style.left = '0'; openQuote.style.top = '0'; openQuote.style.color = '#ffc107'; openQuote.style.fontSize = '16px'; openQuote.style.fontWeight = 'bold'; openQuote.style.fontStyle = 'normal'; openQuote.innerHTML = '“'; li.appendChild(openQuote); var quoteText = document.createElement('span'); quoteText.textContent = quote; // SECURITY: textContent escapes HTML li.appendChild(quoteText); var closeQuote = document.createElement('span'); closeQuote.style.color = '#ffc107'; closeQuote.style.fontSize = '16px'; closeQuote.style.fontWeight = 'bold'; closeQuote.style.fontStyle = 'normal'; closeQuote.innerHTML = '”'; li.appendChild(closeQuote); quotesList.appendChild(li); }); quotesDiv.appendChild(quotesList); // Copy all quotes button var copyQuotesBtn = document.createElement('button'); copyQuotesBtn.type = 'button'; copyQuotesBtn.className = 'button button-small'; copyQuotesBtn.style.marginTop = '8px'; copyQuotesBtn.style.background = '#fff3e0'; copyQuotesBtn.style.color = '#e65100'; copyQuotesBtn.style.borderColor = '#ffb74d'; copyQuotesBtn.textContent = 'Copy All Quotes'; copyQuotesBtn.onclick = function() { var allQuotes = (data.key_quotes || []).map(function(q) { return '"' + q + '"'; }).join('\n\n'); navigator.clipboard.writeText(allQuotes); copyQuotesBtn.textContent = 'Copied!'; setTimeout(function() { copyQuotesBtn.textContent = 'Copy All Quotes'; }, 1500); }; quotesDiv.appendChild(copyQuotesBtn); container.appendChild(quotesDiv); } // DRAFT PARAGRAPHS SECTION if (data.draft_paragraphs && data.draft_paragraphs.length > 0) { var draftsDiv = document.createElement('div'); draftsDiv.style.marginTop = '12px'; draftsDiv.style.marginBottom = '12px'; draftsDiv.style.padding = '10px'; draftsDiv.style.background = '#e3f2fd'; draftsDiv.style.borderLeft = '4px solid #2196f3'; draftsDiv.style.borderRadius = '4px'; var draftsLabel = document.createElement('strong'); draftsLabel.style.display = 'block'; draftsLabel.style.marginBottom = '8px'; draftsLabel.style.color = '#1565c0'; draftsLabel.innerHTML = '✍ Draft Paragraphs:'; draftsDiv.appendChild(draftsLabel); var paragraphLabels = ['Opening', 'Quote/Context', 'Analysis']; (data.draft_paragraphs || []).forEach(function(para, idx) { var paraContainer = document.createElement('div'); paraContainer.style.marginBottom = '12px'; paraContainer.style.padding = '8px'; paraContainer.style.background = '#fff'; paraContainer.style.borderRadius = '4px'; paraContainer.style.border = '1px solid #bbdefb'; var paraLabel = document.createElement('div'); paraLabel.style.fontSize = '10px'; paraLabel.style.color = '#1976d2'; paraLabel.style.marginBottom = '4px'; paraLabel.style.fontWeight = 'bold'; paraLabel.textContent = (paragraphLabels[idx] || 'Paragraph ' + (idx + 1)) + ':'; paraContainer.appendChild(paraLabel); var paraText = document.createElement('p'); paraText.style.margin = '0'; paraText.style.fontSize = '12px'; paraText.style.lineHeight = '1.5'; paraText.style.color = '#333'; paraText.textContent = para; paraContainer.appendChild(paraText); // Copy button for individual paragraph var copyParaBtn = document.createElement('button'); copyParaBtn.type = 'button'; copyParaBtn.className = 'button button-small'; copyParaBtn.style.marginTop = '6px'; copyParaBtn.style.fontSize = '10px'; copyParaBtn.textContent = 'Copy'; (function(text, btn) { btn.onclick = function() { navigator.clipboard.writeText(text); btn.textContent = 'Copied!'; setTimeout(function() { btn.textContent = 'Copy'; }, 1500); }; })(para, copyParaBtn); paraContainer.appendChild(copyParaBtn); draftsDiv.appendChild(paraContainer); }); // Insert all drafts button var insertDraftsBtn = document.createElement('button'); insertDraftsBtn.type = 'button'; insertDraftsBtn.className = 'button button-small button-primary'; insertDraftsBtn.style.marginTop = '8px'; insertDraftsBtn.style.marginRight = '8px'; insertDraftsBtn.textContent = 'Insert All Drafts into Editor'; insertDraftsBtn.onclick = function() { if(typeof tinymce !== 'undefined' && tinymce.activeEditor) { var html = (data.draft_paragraphs || []).map(function(p) { return '

' + self.escapeHtml(p) + '

'; }).join('\n'); tinymce.activeEditor.insertContent(html); insertDraftsBtn.textContent = 'Inserted!'; setTimeout(function() { insertDraftsBtn.textContent = 'Insert All Drafts into Editor'; }, 1500); } }; draftsDiv.appendChild(insertDraftsBtn); // Copy all drafts button var copyDraftsBtn = document.createElement('button'); copyDraftsBtn.type = 'button'; copyDraftsBtn.className = 'button button-small'; copyDraftsBtn.style.marginTop = '8px'; copyDraftsBtn.textContent = 'Copy All Drafts'; copyDraftsBtn.onclick = function() { var allDrafts = (data.draft_paragraphs || []).join('\n\n'); navigator.clipboard.writeText(allDrafts); copyDraftsBtn.textContent = 'Copied!'; setTimeout(function() { copyDraftsBtn.textContent = 'Copy All Drafts'; }, 1500); }; draftsDiv.appendChild(copyDraftsBtn); container.appendChild(draftsDiv); } var kpLabel = document.createElement('strong'); kpLabel.textContent = 'Key Points:'; container.appendChild(kpLabel); var kpList = document.createElement('ul'); kpList.className = 'pfeai-list'; (data.key_points || []).forEach(function(pt) { var li = document.createElement('li'); li.textContent = pt; // SECURITY: textContent escapes HTML kpList.appendChild(li); }); container.appendChild(kpList); var sparkDiv = document.createElement('div'); sparkDiv.style.marginTop = '10px'; sparkDiv.style.borderTop = '1px solid #eee'; sparkDiv.style.paddingTop = '10px'; var sparkLabel = document.createElement('strong'); sparkLabel.textContent = 'Spark Sentences:'; sparkDiv.appendChild(sparkLabel); var sparkText = (data.spark_sentences || []).join('\n\n'); var textarea = document.createElement('textarea'); textarea.id = 'pfeai-spark-text'; textarea.style.width = '100%'; textarea.style.height = '100px'; textarea.style.fontSize = '12px'; textarea.style.marginTop = '5px'; textarea.value = sparkText; // SECURITY: .value doesn't execute HTML sparkDiv.appendChild(textarea); var btnDiv = document.createElement('div'); btnDiv.style.marginTop = '8px'; btnDiv.style.display = 'flex'; btnDiv.style.gap = '10px'; var insertBtn = document.createElement('button'); insertBtn.type = 'button'; insertBtn.className = 'button button-small button-primary'; insertBtn.id = 'pfeai-insert-spark'; insertBtn.textContent = 'Insert into Editor'; btnDiv.appendChild(insertBtn); var saveBtn = document.createElement('button'); saveBtn.type = 'button'; saveBtn.className = 'button button-small'; saveBtn.style.background = '#e8f5e9'; saveBtn.style.color = '#2e7d32'; saveBtn.style.borderColor = '#66bb6a'; saveBtn.id = 'pfeai-save-clipboard'; saveBtn.innerHTML = '📝 Start New Post →'; btnDiv.appendChild(saveBtn); sparkDiv.appendChild(btnDiv); container.appendChild(sparkDiv); resDiv.innerHTML = ''; resDiv.appendChild(container); resDiv.scrollIntoView({behavior:'smooth'}); // Bind events insertBtn.onclick = function(){ var wt = document.getElementById('title'); if(wt) { wt.value = title; wt.focus(); var pt = document.getElementById('title-prompt-text'); if(pt) pt.classList.add('screen-reader-text'); } if(typeof tinymce !== 'undefined' && tinymce.activeEditor) { var t = document.getElementById('pfeai-spark-text').value; tinymce.activeEditor.insertContent('

' + self.escapeHtml(t).replace(/\n\n/g, '

') + '

'); } }; saveBtn.onclick = function(){ var fullHTML = ''; if(data.blurb) fullHTML += '

Blurb: ' + self.escapeHtml(data.blurb) + '

'; // Include key quotes in saved draft if(data.key_quotes && data.key_quotes.length) { fullHTML += '

Key Quotes:

'; } // Include draft paragraphs in saved draft if(data.draft_paragraphs && data.draft_paragraphs.length) { fullHTML += '

Draft Paragraphs:

'; data.draft_paragraphs.forEach(function(p){ fullHTML += '

' + self.escapeHtml(p) + '

'; }); } if(data.key_points && data.key_points.length) { fullHTML += '

Key Points:

'; } var sparkVal = document.getElementById('pfeai-spark-text').value; fullHTML += '

' + self.escapeHtml(sparkVal).replace(/\n\n/g, '

') + '

'; // Save and redirect to new post self.saveToClipboard(title, fullHTML, true); }; }, /** * SECURITY FIX: renderIdeasInteractive now uses proper escaping */ renderIdeasInteractive: function(data) { var self = this; this.ideasEl.innerHTML = ''; var h3 = document.createElement('h3'); h3.textContent = 'Topic Inspiration'; this.ideasEl.appendChild(h3); var p = document.createElement('p'); p.style.fontSize = '11px'; p.style.marginBottom = '10px'; p.textContent = 'Click a topic to generate an outline.'; this.ideasEl.appendChild(p); (data.ideas || []).forEach(function(i) { var c = document.createElement('div'); c.className = 'pfeai-card pfeai-clickable'; c.style.cursor = 'pointer'; c.style.borderLeftColor = '#8e44ad'; var h = document.createElement('h4'); h.style.margin = '0 0 4px 0'; h.style.color = '#8e44ad'; h.textContent = i.title; // SECURITY: textContent escapes HTML c.appendChild(h); var pEl = document.createElement('p'); pEl.style.fontSize = '12px'; pEl.textContent = i.context || i.angle || ''; // SECURITY: textContent escapes HTML c.appendChild(pEl); var sr = document.createElement('div'); sr.className = 'pfeai-score-row'; sr.style.marginTop = '6px'; sr.style.borderTop = '1px solid #eee'; sr.style.paddingTop = '4px'; sr.innerHTML = 'Trend: ' + parseInt(i.trend_score || i.ctr_score || 0, 10) + 'Disc: ' + parseInt(i.discover_score || 0, 10) + ''; c.appendChild(sr); (function(title, context) { c.addEventListener('click', function() { self.runIdeaOutline(title, context); }); })(i.title, i.context); self.ideasEl.appendChild(c); }); var rd = document.createElement('div'); rd.id = 'pfeai-idea-result'; rd.style.marginTop = '15px'; rd.style.borderTop = '1px solid #ccc'; rd.style.paddingTop = '10px'; this.ideasEl.appendChild(rd); }, /** * SECURITY FIX: renderIdeaOutlineResult now uses proper escaping */ renderIdeaOutlineResult: function(data, title) { var self = this; var resDiv = document.getElementById('pfeai-idea-result'); // Build DOM elements instead of innerHTML for safety var container = document.createElement('div'); container.style.background = '#fff'; container.style.padding = '10px'; container.style.border = '1px solid #ddd'; container.style.marginTop = '10px'; var h3 = document.createElement('h3'); h3.style.marginTop = '0'; h3.style.color = '#8e44ad'; h3.textContent = title; // SECURITY: textContent escapes HTML container.appendChild(h3); var blurbLabel = document.createElement('strong'); blurbLabel.textContent = 'Blurb:'; container.appendChild(blurbLabel); var blurbP = document.createElement('p'); blurbP.textContent = data.blurb || ''; // SECURITY: textContent escapes HTML container.appendChild(blurbP); // DRAFT PARAGRAPHS SECTION if (data.draft_paragraphs && data.draft_paragraphs.length > 0) { var draftsDiv = document.createElement('div'); draftsDiv.style.marginTop = '12px'; draftsDiv.style.marginBottom = '12px'; draftsDiv.style.padding = '10px'; draftsDiv.style.background = '#f3e5f5'; draftsDiv.style.borderLeft = '4px solid #9c27b0'; draftsDiv.style.borderRadius = '4px'; var draftsLabel = document.createElement('strong'); draftsLabel.style.display = 'block'; draftsLabel.style.marginBottom = '8px'; draftsLabel.style.color = '#7b1fa2'; draftsLabel.innerHTML = '✍ Draft Paragraphs:'; draftsDiv.appendChild(draftsLabel); var paragraphLabels = ['Opening', 'Middle/Analysis', 'Context/Closing']; (data.draft_paragraphs || []).forEach(function(para, idx) { var paraContainer = document.createElement('div'); paraContainer.style.marginBottom = '12px'; paraContainer.style.padding = '8px'; paraContainer.style.background = '#fff'; paraContainer.style.borderRadius = '4px'; paraContainer.style.border = '1px solid #e1bee7'; var paraLabel = document.createElement('div'); paraLabel.style.fontSize = '10px'; paraLabel.style.color = '#8e44ad'; paraLabel.style.marginBottom = '4px'; paraLabel.style.fontWeight = 'bold'; paraLabel.textContent = (paragraphLabels[idx] || 'Paragraph ' + (idx + 1)) + ':'; paraContainer.appendChild(paraLabel); var paraText = document.createElement('p'); paraText.style.margin = '0'; paraText.style.fontSize = '12px'; paraText.style.lineHeight = '1.5'; paraText.style.color = '#333'; paraText.textContent = para; paraContainer.appendChild(paraText); // Copy button for individual paragraph var copyParaBtn = document.createElement('button'); copyParaBtn.type = 'button'; copyParaBtn.className = 'button button-small'; copyParaBtn.style.marginTop = '6px'; copyParaBtn.style.fontSize = '10px'; copyParaBtn.textContent = 'Copy'; (function(text, btn) { btn.onclick = function() { navigator.clipboard.writeText(text); btn.textContent = 'Copied!'; setTimeout(function() { btn.textContent = 'Copy'; }, 1500); }; })(para, copyParaBtn); paraContainer.appendChild(copyParaBtn); draftsDiv.appendChild(paraContainer); }); // Insert all drafts button var insertDraftsBtn = document.createElement('button'); insertDraftsBtn.type = 'button'; insertDraftsBtn.className = 'button button-small button-primary'; insertDraftsBtn.style.marginTop = '8px'; insertDraftsBtn.style.marginRight = '8px'; insertDraftsBtn.textContent = 'Insert All Drafts into Editor'; insertDraftsBtn.onclick = function() { if(typeof tinymce !== 'undefined' && tinymce.activeEditor) { var html = (data.draft_paragraphs || []).map(function(p) { return '

' + self.escapeHtml(p) + '

'; }).join('\n'); tinymce.activeEditor.insertContent(html); insertDraftsBtn.textContent = 'Inserted!'; setTimeout(function() { insertDraftsBtn.textContent = 'Insert All Drafts into Editor'; }, 1500); } }; draftsDiv.appendChild(insertDraftsBtn); // Copy all drafts button var copyDraftsBtn = document.createElement('button'); copyDraftsBtn.type = 'button'; copyDraftsBtn.className = 'button button-small'; copyDraftsBtn.style.marginTop = '8px'; copyDraftsBtn.textContent = 'Copy All Drafts'; copyDraftsBtn.onclick = function() { var allDrafts = (data.draft_paragraphs || []).join('\n\n'); navigator.clipboard.writeText(allDrafts); copyDraftsBtn.textContent = 'Copied!'; setTimeout(function() { copyDraftsBtn.textContent = 'Copy All Drafts'; }, 1500); }; draftsDiv.appendChild(copyDraftsBtn); container.appendChild(draftsDiv); } var kpLabel = document.createElement('strong'); kpLabel.textContent = 'Key Points:'; container.appendChild(kpLabel); var kpList = document.createElement('ul'); kpList.className = 'pfeai-list'; (data.key_points || []).forEach(function(pt) { var li = document.createElement('li'); li.textContent = pt; // SECURITY: textContent escapes HTML kpList.appendChild(li); }); container.appendChild(kpList); var sparkDiv = document.createElement('div'); sparkDiv.style.marginTop = '10px'; sparkDiv.style.borderTop = '1px solid #eee'; sparkDiv.style.paddingTop = '10px'; var sparkLabel = document.createElement('strong'); sparkLabel.textContent = 'Spark Sentences:'; sparkDiv.appendChild(sparkLabel); var sparkText = (data.spark_sentences || []).join('\n\n'); var textarea = document.createElement('textarea'); textarea.id = 'pfeai-idea-spark-text'; textarea.style.width = '100%'; textarea.style.height = '100px'; textarea.style.fontSize = '12px'; textarea.style.marginTop = '5px'; textarea.value = sparkText; // SECURITY: .value doesn't execute HTML sparkDiv.appendChild(textarea); var btnDiv = document.createElement('div'); btnDiv.style.marginTop = '8px'; btnDiv.style.display = 'flex'; btnDiv.style.gap = '10px'; var insertBtn = document.createElement('button'); insertBtn.type = 'button'; insertBtn.className = 'button button-small button-primary'; insertBtn.id = 'pfeai-insert-idea-spark'; insertBtn.textContent = 'Insert into Editor'; btnDiv.appendChild(insertBtn); var saveBtn = document.createElement('button'); saveBtn.type = 'button'; saveBtn.className = 'button button-small'; saveBtn.style.background = '#e8f5e9'; saveBtn.style.color = '#2e7d32'; saveBtn.style.borderColor = '#66bb6a'; saveBtn.id = 'pfeai-save-idea-clipboard'; saveBtn.innerHTML = '📝 Start New Post →'; btnDiv.appendChild(saveBtn); sparkDiv.appendChild(btnDiv); container.appendChild(sparkDiv); resDiv.innerHTML = ''; resDiv.appendChild(container); resDiv.scrollIntoView({behavior:'smooth'}); // Bind events insertBtn.onclick = function(){ var wt = document.getElementById('title'); if(wt) { wt.value = title; wt.focus(); var pt = document.getElementById('title-prompt-text'); if(pt) pt.classList.add('screen-reader-text'); } if(typeof tinymce !== 'undefined' && tinymce.activeEditor) { var t = document.getElementById('pfeai-idea-spark-text').value; tinymce.activeEditor.insertContent('

' + self.escapeHtml(t).replace(/\n\n/g, '

') + '

'); } }; saveBtn.onclick = function(){ var fullHTML = ''; if(data.blurb) fullHTML += '

Blurb: ' + self.escapeHtml(data.blurb) + '

'; // Include draft paragraphs in saved draft if(data.draft_paragraphs && data.draft_paragraphs.length) { fullHTML += '

Draft Paragraphs:

'; data.draft_paragraphs.forEach(function(p){ fullHTML += '

' + self.escapeHtml(p) + '

'; }); } if(data.key_points && data.key_points.length) { fullHTML += '

Key Points:

'; } var sparkVal = document.getElementById('pfeai-idea-spark-text').value; fullHTML += '

' + self.escapeHtml(sparkVal).replace(/\n\n/g, '

') + '

'; // Save and redirect to new post self.saveToClipboard(title, fullHTML, true); }; }, /** * SECURITY FIX: renderSavedDraft now uses proper escaping */ renderSavedDraft: function(clip) { var self = this; if(!this.savedEl) return; var cleanText = (clip.content || '') .replace(/