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 34 of 617)

MORSE: NFL Combine Reacap

March 06, 2025 | Mark Morse

The interviews are the most important aspect of the NFL Draft Combine.  Head Coach Mike Vrabel and EVP of Player Personnel Eliot Wolf were busy every night they were in Indianapolis conducting those interviews. 

MORSE: Thoughts On the Patriots and NFL Free Agency

March 05, 2025 | Mark Morse

Free Agency starts in a few days with legal tampering allowed starting March 10th at 12 noon.  Teams had to designate players as Franchise tagged by 4PM on March 4th.

Patriots Insider Hears Team is Set to Make 'Aggressive' Pursuit of Free Agent Tackle

March 04, 2025 | Ian Logue

It's no secret that offensive tackle was a significant problem for the New England Patriots, and it sounds like they may already have a plan in place to address it ahead of free agency.

Patriots News 3-02, The Combine and Free Agency

March 02, 2025 | Steve Balestrieri

I have been able to do that and moved Ben [McAdoo] over to defense to help Terrell and his staff just look at it from an offensive perspective

Former Patriots QB to Coach at His Alma Mater

February 28, 2025 | Ian Logue

We didn't see much of Trace McSorley here in New England, and it appears that after a couple of stints elsewhere, he's heading back to where he started.

Patriots Losing Key Talent Evaluator to College Ranks

February 28, 2025 | Ian Logue

For a team that's in the middle of a significant rebuild, it looks like one key Patriots personnel executive may not be a part of things moving forward.

Former Patriots Player Talks About Hanging Up on Belichick in Crazy Story

February 27, 2025 | Ian Logue

Former New England Patriots running back Stevan Ridley apparently got off on the wrong foot with the clubduring his rookie year after arriving to the facility above the playing weight Bill Belichick had in mind for him, and one mistake he later made apparently nearly saw him let go from the team.

Former Patriots RB Explains His Experience Going Through 'Fat Camp'

February 27, 2025 | Ian Logue

Former New England Patriots running back Stevan Ridley recently appeared on the Games With Names podcast with Julian Edelman, with Ridley recounting a story from when he first arrived in Foxboro after he was drafted in the third round during the 2011 Patriots Draft.

Patriots Give Defensive Starter Permission To Seek Trade

February 27, 2025 | Ian Logue

According to NFL reporter Ian Rapoport, defensive tackle Davon Godchaux has been given permission by the team to seek a trade. This news comes after Godchaux signed an extension last offseason, signing a two-year deal back on July 31, 2024. 

Jets' NFLPA Report Card Reveals What Most Already Knew: Problems Start at the Top

February 27, 2025 | Ian Logue

While the D- owner Robert Kraft received in the Patriots 2025 NFLPA Report Card was a big point of discussion on Wednesday, what's going down in New Jersey should make all of that pale in comparison.

Wolf on Patriots' Personnel Decisions: "It's My Final Say"

February 26, 2025 | Ian Logue

One of the biggest questions being asked since Mike Vrabel was hired last month has been where things will stand when it comes to personnel for the New England Patriots.

Eliot Wolf Breaks Silence on Patriots 4-13 Season: 'The Roster Was On Me'

February 26, 2025 | Ian Logue

It's been a while since Eliot Wolf spoke to the media, and the Patriots personnel man took a long look back at last season and came pointing the finger at himself after a tough finish.

TRANSCRIPT: Eliot Wolf's Press Conference 2/26

February 26, 2025 | Ian Logue

Here's what New England Patriots Executive Vice President of Player Personnel Eliot Wolf had to say during his press conference on Wednesday down at the NFL Scouting Combine in Indianapolis.

Patriots Score Poorly Again in NFLPA 2025 Report Card

February 26, 2025 | Ian Logue

The NFLPA released its report card for 2025, and once again, the Patriots found themselves at the bottom of the rankings compared to their peers.

Patriots' Roster Decisions: Mike Vrabel Takes the Lead in 2025 Rebuild

February 26, 2025 | Ian Logue

Mike Vrabel is taking charge of the New England Patriots' roster decisions heading into the 2025 offseason. With a clear vision for free agency and the draft, Vrabel is working alongside Eliot Wolf and Ryan Cowden to reshape the team. Find out how the new power structure is influencing the Patriots’ offseason strategy, potential free-agent targets, and Vrabel’s approach to building a competitive roster.

TRANSCRIPT: Mike Vrabel Q&A with Local Media 2/25

February 25, 2025 | Ian Logue

Mike Vrabel held his press conference earlier this morning, and he sat down for another Q&A session with the local media later in the day.  Here's a full transcript from that session.

Vrabel on Patriots Free Agency: 'We Want to Be Aggressive'

February 25, 2025 | Ian Logue

With NFL Free Agency just weeks away, the big question is going to be just how much of a splash the Patriots are able to make coming off of two straight 4-13 seasons.

TRANSCRIPT: Mike Vrabel's Press Conference 2/25

February 25, 2025 | Ian Logue

Here's the full transcript from New England Patriots head coach, Mike Vrabel's press conference on Tuesday down in Indianapolis.

MORSE: Potential Patriots Prospects to Watch at the NFL Draft Combine

February 25, 2025 | Mark Morse

The NFL Combine is held at Lucas Oil Stadium In Indianapolis and is an opportunity for the top NFL draft prospects in this year’s class to go through measurements, medical physicals, and various on-field drills, some of which are specific to their position group, others measure the player’s overall athleticism.

Mock Draft 1.0 Pre-Free Agency: Best Player Available

February 24, 2025 | Steve Balestrieri

Matt Miller said on “X”: “For me, it's Abdul Carter 1st overall and then there's a gap to every other prospect in this class.”

Patriots News 2/23: Will the Patriots Explore Moving Stevenson?

February 23, 2025 | Ian Logue

This is certainly a big week coming up for the New England Patriots, with the NFL Scouting Combine set to begin on February 27th, allowing for a first glimpse of some of the candidates that will be available in this year's upcoming NFL Draft. In this morning's Patriots' news article, we look at what's ahead for Mike Vrabel, along with potential Patriots trade rumors that could see Rhamondre Stevenson's future in question heading into 2025.

Maye on Patriots' Red Throwbacks: "Those reds are sweet"

February 21, 2025 | Ian Logue

Drake Maye hasn't been in New England very long, but he seems to have one thing in common with the fans here locally. He's a big fan of the red old-school throwback uniforms.

Maye Looking For Big Things From Two Patriots Players in 2025

February 21, 2025 | Ian Logue

Patriots quarterback Drake Maye appeared with Chris Long on the "Green Light" podcast recently, and was asked about which players he expected to potentially step up in 2025.

Patriots QB Maye Reveals His 'Welcome to the NFL Moment'

February 21, 2025 | Ian Logue

Rookie quarterback Drake Maye talked about what life was like for him in his first season with the New England Patriots during a recent appearance with Chris Long on the Green Light podcast.

TRANSCRIPT: Drake Maye On the Green Light Podcast With Chris Long

February 21, 2025 | Ian Logue

New England Patriots quarterback Drake Maye recently appeared on the Green Light podcast with Chris Long, with Maye talking about his rookie season along with giving some good insight on some other stuff as well.

(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(/