MediaWiki:Common.js: Difference between revisions
m test4 |
No edit summary |
||
| (36 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
/* Any JavaScript here will be loaded for all users on every page load. */ | /* Any JavaScript here will be loaded for all users on every page load. */ | ||
// stolen from Lajos Mészáros on this post https://stackoverflow.com/questions/13358292/capture-tap-event-with-pure-javascript | |||
const onClickOrTap = (element, handler) => { | const onClickOrTap = (element, handler) => { | ||
let touchMoveHappened = false; | let touchMoveHappened = false; | ||
| Line 26: | Line 26: | ||
}); | }); | ||
element.addEventListener('click', (e) => { | element.addEventListener('click', (e) => { | ||
// this will only get triggered on | // this will only get triggered on desktopg | ||
// because we call preventDefault for the "touchend" event | // because we call preventDefault for the "touchend" event | ||
handler(e); | handler(e); | ||
| Line 107: | Line 107: | ||
} | } | ||
} | } | ||
/* Jakes Recent Changes Portlet */ | |||
(function () { | |||
function jakeTimeAgo(date) { | |||
var s = Math.floor((Date.now() - date.getTime()) / 1000); | |||
if (s < 60) return s + "s ago"; | |||
var m = Math.floor(s / 60); | |||
if (m < 60) return m + "m ago"; | |||
var h = Math.floor(m / 60); | |||
if (h < 24) return h + "h ago"; | |||
var d = Math.floor(h / 24); | |||
return d + "d ago"; | |||
} | |||
function jakeInjectStyles() { | |||
if (document.getElementById("jake-rc-style")) return; | |||
var style = document.createElement("style"); | |||
style.id = "jake-rc-style"; | |||
style.textContent = | |||
"#p-jake-recentchanges .vector-menu-heading{display:flex;align-items:center;justify-content:space-between;}" + | |||
"#p-jake-recentchanges .jake-rc-dot{width:8px;height:8px;border-radius:50%;background:#2da44e;box-shadow:0 0 0 2px rgba(45,164,78,.18);flex:0 0 auto;}" + | |||
"#p-jake-recentchanges .vector-menu-heading{font-size:.95em;}" + | |||
"#p-jake-recentchanges .vector-menu-content{font-size:.92em;}" + | |||
"#p-jake-recentchanges .jake-rc-meta{font-size:.85em;opacity:.85;}"; | |||
document.head.appendChild(style); | |||
} | |||
function jakeBuildPortlet() { | |||
var menu = | |||
document.getElementById("vector-main-menu") || | |||
document.getElementById("mw-panel"); | |||
if (!menu) return null; | |||
if (document.getElementById("p-jake-recentchanges")) return null; | |||
jakeInjectStyles(); | |||
var portlet = document.createElement("div"); | |||
portlet.id = "p-jake-recentchanges"; | |||
portlet.className = "vector-menu mw-portlet"; | |||
var heading = document.createElement("div"); | |||
heading.className = "vector-menu-heading"; | |||
var headingText = document.createElement("span"); | |||
headingText.textContent = "Recent changes"; | |||
var dot = document.createElement("span"); | |||
dot.className = "jake-rc-dot"; | |||
dot.setAttribute("aria-hidden", "true"); | |||
dot.title = "Live"; | |||
heading.appendChild(headingText); | |||
heading.appendChild(dot); | |||
var content = document.createElement("div"); | |||
content.className = "vector-menu-content"; | |||
var ul = document.createElement("ul"); | |||
ul.className = "vector-menu-content-list"; | |||
ul.id = "jake-rc-list"; | |||
var moreWrap = document.createElement("div"); | |||
moreWrap.className = "jake-rc-more"; | |||
var moreLink = document.createElement("a"); | |||
moreLink.href = mw.util.getUrl("Special:RecentChanges"); | |||
moreLink.textContent = "Show more…"; | |||
moreWrap.appendChild(moreLink); | |||
content.appendChild(ul); | |||
content.appendChild(moreWrap); | |||
portlet.appendChild(heading); | |||
portlet.appendChild(content); | |||
var discordPortlet = menu.querySelector("#p-Community"); | |||
if (discordPortlet && discordPortlet.parentNode) { | |||
discordPortlet.parentNode.insertBefore(portlet, discordPortlet.nextSibling); | |||
} else { | |||
var navPortlet = menu.querySelector("#p-navigation"); | |||
if (navPortlet && navPortlet.parentNode) { | |||
navPortlet.parentNode.insertBefore(portlet, navPortlet.nextSibling); | |||
} else { | |||
menu.appendChild(portlet); | |||
} | |||
} | |||
return ul; | |||
} | |||
function jakeLoadRecentChanges(ul) { | |||
if (!ul) return; | |||
var api = new mw.Api(); | |||
api | |||
.get({ | |||
action: "query", | |||
list: "recentchanges", | |||
rcnamespace: "0", /* 4 */ | |||
rclimit: 5, | |||
rcprop: "title|timestamp|user", | |||
rcshow: "!bot", | |||
formatversion: 2 | |||
}) | |||
.then(function (data) { | |||
var items = | |||
data && data.query && data.query.recentchanges | |||
? data.query.recentchanges | |||
: []; | |||
ul.textContent = ""; | |||
for (var i = 0; i < items.length; i++) { | |||
var rc = items[i]; | |||
var li = document.createElement("li"); | |||
li.className = "mw-list-item jake-rc-item"; | |||
var a = document.createElement("a"); | |||
a.href = mw.util.getUrl(rc.title); | |||
a.textContent = rc.title; | |||
var meta = document.createElement("div"); | |||
meta.className = "jake-rc-meta"; | |||
meta.textContent = jakeTimeAgo(new Date(rc.timestamp)) + " · " + (rc.user || ""); | |||
li.appendChild(a); | |||
li.appendChild(meta); | |||
ul.appendChild(li); | |||
} | |||
}) | |||
.catch(function () { | |||
ul.textContent = ""; | |||
var li = document.createElement("li"); | |||
li.className = "mw-list-item jake-rc-item"; | |||
var a = document.createElement("a"); | |||
a.href = mw.util.getUrl("Special:RecentChanges"); | |||
a.textContent = "View recent changes"; | |||
li.appendChild(a); | |||
ul.appendChild(li); | |||
}); | |||
} | |||
function jakeInitRecentChanges() { | |||
if (!window.mw || !mw.util || !mw.Api) return; | |||
var ul = jakeBuildPortlet(); | |||
if (!ul) ul = document.getElementById("jake-rc-list"); | |||
if (!ul) return; | |||
jakeLoadRecentChanges(ul); | |||
setInterval(function () { | |||
jakeLoadRecentChanges(ul); | |||
}, 60000); | |||
} | |||
function jakeWaitForMenu() { | |||
var tries = 0; | |||
var timer = setInterval(function () { | |||
var menu = | |||
document.getElementById("vector-main-menu") || | |||
document.getElementById("mw-panel"); | |||
if (menu) { | |||
clearInterval(timer); | |||
jakeInitRecentChanges(); | |||
return; | |||
} | |||
tries++; | |||
if (tries > 50) clearInterval(timer); | |||
}, 200); | |||
} | |||
jakeWaitForMenu(); | |||
})(); | |||
/* */ | |||
/* | |||
(function () { | |||
function jakeAddFeedbackToNamespaces() { | |||
var list = document.querySelector("#p-associated-pages .vector-menu-content-list"); | |||
if (!list) return; | |||
var existing = document.getElementById("jake-feedback-btn"); | |||
if (existing && !existing.closest("#p-associated-pages")) { | |||
var oldLi = existing.closest("li"); | |||
if (oldLi) oldLi.remove(); | |||
else existing.remove(); | |||
} | |||
if (document.getElementById("ca-jake-feedback")) return; | |||
var li = document.createElement("li"); | |||
li.id = "ca-jake-feedback"; | |||
li.className = "vector-tab-noicon mw-list-item"; | |||
var a = document.createElement("a"); | |||
a.id = "jake-feedback-btn"; | |||
a.className = "jake-feedback-btn"; | |||
a.href = mw.util.getUrl("Consumer_Rights_Wiki:Feedback", { | |||
from: mw.config.get("wgPageName") | |||
}); | |||
a.title = "Give feedback"; | |||
var span = document.createElement("span"); | |||
span.textContent = "Give feedback"; | |||
a.appendChild(span); | |||
li.appendChild(a); | |||
list.appendChild(li); | |||
} | |||
function jakeInit() { | |||
if (!window.mw || !mw.util || !mw.config) return; | |||
jakeAddFeedbackToNamespaces(); | |||
} | |||
if (document.readyState === "loading") { | |||
document.addEventListener("DOMContentLoaded", jakeInit); | |||
} else { | |||
jakeInit(); | |||
} | |||
})(); | |||
*/ | |||
/* */ | |||
(function () { | |||
function jakeAddFooterSocialRow() { | |||
var footer = document.getElementById("footer"); | |||
if (!footer) return; | |||
var places = document.getElementById("footer-places"); | |||
if (!places) return; | |||
if (document.getElementById("footer-social")) return; | |||
var ul = document.createElement("ul"); | |||
ul.id = "footer-social"; | |||
ul.className = "noprint crw-footer-social"; | |||
function add(href, label) { | |||
var li = document.createElement("li"); | |||
var a = document.createElement("a"); | |||
a.href = href; | |||
a.textContent = label; | |||
a.setAttribute("aria-label", label); | |||
a.title = label; | |||
li.appendChild(a); | |||
ul.appendChild(li); | |||
} | |||
// add("https://discord.gg/8w5rSNAXRf", "Discord"); | |||
add("https://www.linkedin.com/company/consumer-rights-wiki/", "LinkedIn"); | |||
add("https://www.reddit.com/user/ConsumerRightsWiki/", "Reddit"); | |||
places.parentNode.insertBefore(ul, places.nextSibling); | |||
} | |||
if (document.readyState === "loading") { | |||
document.addEventListener("DOMContentLoaded", jakeAddFooterSocialRow); | |||
} else { | |||
jakeAddFooterSocialRow(); | |||
} | |||
})(); | |||
/* AF 01/03/26 */ | |||
mw.loader.using('mediawiki.util', function() { | |||
const now = new Date(); | |||
const isAprilFools = now.getMonth() === 3 && now.getDate() === 1; | |||
if (!isAprilFools) return; | |||
if (!mw.config.get('wgIsMainPage')) return; | |||
const btn = document.createElement('button'); | |||
btn.textContent = 'Try the new experience →'; | |||
btn.id = 'enshittify-btn'; | |||
btn.onclick = enshittify; | |||
document.querySelector('.mw-body').prepend(btn); | |||
let popupQueue = []; | |||
let queueRunning = false; | |||
function queuePopup(fn, delay) { | |||
popupQueue.push({ fn, delay }); | |||
if (!queueRunning) runQueue(); | |||
} | |||
function runQueue() { | |||
if (!popupQueue.length) { queueRunning = false; return; } | |||
queueRunning = true; | |||
const { fn, delay } = popupQueue.shift(); | |||
setTimeout(() => { fn(); runQueue(); }, delay); | |||
} | |||
function enshittify() { | |||
btn.disabled = true; | |||
btn.textContent = 'Loading new experience...'; | |||
showCookieBanner(); | |||
injectBreakingNewsTicker(); | |||
degradePrivacyPolicy(); | |||
degradeCursor(); | |||
addFakeProgressBar(); | |||
addViewerCounter(); | |||
addTabHijack(); | |||
addBeforeUnloadNag(); | |||
queuePopup(showNewsletterPopup, 400); | |||
queuePopup(injectAds, 300); | |||
queuePopup(addNotificationPrompt, 600); | |||
queuePopup(addAutoplayVideo, 400); | |||
queuePopup(addSubscriptionNag, 700); | |||
queuePopup(addSurveyPopup, 500); | |||
queuePopup(addFakeSecurityAlert, 600); | |||
queuePopup(addFloatingCountdown, 300); | |||
queuePopup(addLiveChatBubble, 400); | |||
queuePopup(addScrollBlocker, 200); | |||
queuePopup(addConfetti, 100); | |||
} | |||
function showCookieBanner() { | |||
const el = document.createElement('div'); | |||
el.id = 'cookie-banner'; | |||
el.innerHTML = ` | |||
<div id="cookie-inner"> | |||
<div id="cookie-text"> | |||
<strong>We value your privacy</strong><br/> | |||
<span style="font-size:12px;color:#aaa">We and our <span id="partner-count">847</span> partners store and/or access information on your device and process personal data to personalise content and ads, provide social media features and analyse our traffic. Click Accept to consent or <span id="reject-link" style="font-size:10px;color:#555;cursor:pointer;text-decoration:underline">manage your preferences</span>.</span> | |||
</div> | |||
<div id="cookie-btns"> | |||
<button id="cookie-accept" onclick="acceptAllCookies()">✓ Accept All</button> | |||
</div> | |||
</div>`; | |||
document.body.appendChild(el); | |||
let count = 847; | |||
const iv = setInterval(() => { | |||
count += Math.floor(Math.random() * 3); | |||
const el = document.getElementById('partner-count'); | |||
if (el) el.textContent = count; else clearInterval(iv); | |||
}, 800); | |||
document.getElementById('reject-link').onclick = showPreferenceCentre; | |||
} | |||
window.acceptAllCookies = function() { | |||
const banner = document.getElementById('cookie-banner'); | |||
if (banner) { | |||
banner.innerHTML = `<div style="padding:10px 24px;font-size:13px;color:#aaa">✅ Preferences saved. You have consented to <strong style="color:white">all processing activities</strong> across <strong style="color:white">1,204 partners</strong>. <span style="font-size:10px">This cannot be undone for 13 months.</span></div>`; | |||
setTimeout(() => banner.remove(), 4000); | |||
} | |||
setTimeout(showSecondCookieBanner, 30000); | |||
}; | |||
function showPreferenceCentre() { | |||
document.getElementById('cookie-banner').remove(); | |||
const steps = [ | |||
{ title: 'Strictly Necessary', desc: 'These cookies are required for the website to function.', locked: true }, | |||
{ title: 'Performance & Analytics', desc: 'Help us understand how visitors interact with our website.' }, | |||
{ title: 'Functional Cookies', desc: 'Enable enhanced functionality and personalisation.' }, | |||
{ title: 'Targeting & Advertising', desc: 'Used to deliver relevant advertisements to you.' }, | |||
{ title: 'Social Media Cookies', desc: 'Enable sharing content on social media platforms.' }, | |||
{ title: 'Measurement Cookies', desc: 'Used to measure the effectiveness of advertising campaigns.' }, | |||
{ title: 'Content Personalisation', desc: 'Allow us to tailor content specifically to your interests.' }, | |||
{ title: 'Data Enrichment', desc: 'Allow partners to enrich your profile with additional data.' }, | |||
{ title: 'Cross-Device Tracking', desc: 'Connect your activity across multiple devices.' }, | |||
{ title: 'Partner Data Sharing', desc: 'Share your data with our trusted partner network.' }, | |||
{ title: 'Legitimate Interests', desc: 'Processing based on our legitimate business interests.' }, | |||
{ title: 'Special Category Data', desc: 'Processing of sensitive personal information.' }, | |||
]; | |||
let step = 0; | |||
const el = document.createElement('div'); | |||
el.id = 'pref-centre'; | |||
function renderStep() { | |||
const s = steps[step]; | |||
el.innerHTML = ` | |||
<div class="modal-box" style="max-width:460px"> | |||
<div style="font-size:11px;color:#999;margin-bottom:12px">Step ${step + 1} of ${steps.length} — Cookie Preference Centre</div> | |||
<div style="background:#f5f5f5;border-radius:6px;height:4px;margin-bottom:16px;overflow:hidden"> | |||
<div style="background:#4d96ff;height:100%;width:${((step+1)/steps.length)*100}%;transition:width 0.3s"></div> | |||
</div> | |||
<h3 style="margin:0 0 8px">${s.title}</h3> | |||
<p style="font-size:13px;color:#666;margin-bottom:16px">${s.desc}</p> | |||
${s.locked | |||
? `<div style="display:flex;align-items:center;gap:8px;margin-bottom:16px"><span style="background:#4d96ff;color:white;font-size:11px;padding:3px 8px;border-radius:99px">Always Active</span><small style="color:#999">This cannot be disabled</small></div>` | |||
: `<div style="display:flex;gap:12px;margin-bottom:16px"> | |||
<label style="display:flex;align-items:center;gap:6px;cursor:pointer"><input type="radio" name="pref_${step}" value="yes" checked> Accept</label> | |||
<label style="display:flex;align-items:center;gap:6px;cursor:pointer"><input type="radio" name="pref_${step}" value="no"> Reject</label> | |||
</div>` | |||
} | |||
<button onclick="nextPrefStep()" style="background:#4d96ff;color:white;border:none;padding:10px 24px;border-radius:6px;cursor:pointer;font-weight:bold"> | |||
${step < steps.length - 1 ? 'Next →' : 'Save Preferences'} | |||
</button> | |||
${step > 0 ? `<button onclick="prevPrefStep()" style="background:none;border:none;color:#999;cursor:pointer;margin-left:8px">← Back</button>` : ''} | |||
</div>`; | |||
} | |||
window.nextPrefStep = function() { | |||
step++; | |||
if (step >= steps.length) { | |||
el.remove(); | |||
const notice = document.createElement('div'); | |||
notice.id = 'cookie-banner'; | |||
notice.innerHTML = `<div style="padding:12px 24px;font-size:13px;color:#aaa"> | |||
ℹ️ Your preferences have been saved. Note: <strong style="color:white">11 of 12 categories</strong> are required under our Legitimate Interests basis and cannot be disabled by users in your region. <span id="pref-close" style="cursor:pointer;float:right;color:#666">✕</span> | |||
</div>`; | |||
document.body.appendChild(notice); | |||
document.getElementById('pref-close').onclick = () => notice.remove(); | |||
} else { | |||
renderStep(); | |||
} | |||
}; | |||
window.prevPrefStep = function() { step--; renderStep(); }; | |||
document.body.appendChild(el); | |||
renderStep(); | |||
} | |||
function showSecondCookieBanner() { | |||
const el = document.createElement('div'); | |||
el.id = 'cookie-banner'; | |||
el.innerHTML = ` | |||
<div id="cookie-inner"> | |||
<div id="cookie-text"> | |||
<strong>Consent refresh required</strong><br/> | |||
<span style="font-size:12px;color:#aaa">Our consent records show your previous consent may have expired. Please reconfirm your preferences to continue.</span> | |||
</div> | |||
<div id="cookie-btns"> | |||
<button id="cookie-accept" onclick="acceptAllCookies()">✓ Reconfirm Consent</button> | |||
</div> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
function showNewsletterPopup() { | |||
const el = document.createElement('div'); | |||
el.id = 'newsletter-modal'; | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<div class="modal-close" id="newsletter-close" style="position:absolute;top:12px;right:16px;cursor:pointer;font-size:18px;color:#999;transition:all 0.1s">✕</div> | |||
<div class="modal-badge">YOU QUALIFY</div> | |||
<h2>🎁 Claim your free access</h2> | |||
<p>Enter your email to unlock full wiki access. No credit card required.*</p> | |||
<input type="email" id="nl-email" placeholder="[email protected]" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:12px 0;font-size:14px" /> | |||
<button onclick="submitNewsletter()">Unlock Free Access →</button> | |||
<p class="fine-print">*Free access includes all articles with ads. Premium access from $9.99/mo. By submitting you agree to receive marketing communications from us and 214 partners.</p> | |||
</div>`; | |||
document.body.appendChild(el); | |||
const closeBtn = document.getElementById('newsletter-close'); | |||
closeBtn.addEventListener('mouseover', () => { | |||
const box = el.querySelector('.modal-box'); | |||
const maxX = Math.max(20, box.offsetWidth - 60); | |||
const maxY = Math.max(20, box.offsetHeight - 60); | |||
closeBtn.style.top = (10 + Math.random() * maxY) + 'px'; | |||
closeBtn.style.right = 'auto'; | |||
closeBtn.style.left = (10 + Math.random() * maxX) + 'px'; | |||
}); | |||
setTimeout(() => { | |||
closeBtn.style.cssText = 'position:absolute;top:12px;right:16px;cursor:pointer;font-size:18px;color:#999'; | |||
closeBtn.onclick = () => { el.remove(); setTimeout(showWaitDontGo, 500); }; | |||
}, 8000); | |||
} | |||
window.submitNewsletter = function() { | |||
const email = document.getElementById('nl-email').value; | |||
if (!email) { alert('Please enter a valid email address.'); return; } | |||
document.getElementById('newsletter-modal').innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>✅ Almost there!</h2> | |||
<p>We've sent a confirmation email to <strong>${email}</strong>.</p> | |||
<p style="margin-top:8px;font-size:12px;color:#999">Didn't receive it? Check your spam folder. Email may take up to 48 hours.</p> | |||
<button onclick="document.getElementById('newsletter-modal').remove()">OK</button> | |||
<p class="fine-print">You are now subscribed to our newsletter, partner newsletters, and 47 automated drip campaigns.</p> | |||
</div>`; | |||
}; | |||
function showWaitDontGo() { | |||
const el = document.createElement('div'); | |||
el.id = 'exit-modal'; | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>⏳ Wait — special offer just for you</h2> | |||
<p>Since you didn't sign up, we're offering you <strong>30 days free</strong> of Premium access.</p> | |||
<p style="margin-top:8px;font-size:13px;color:#666">No commitment. Cancel anytime.*</p> | |||
<button onclick="claimSpecialOffer()">Claim 30 Days Free →</button> | |||
<br/><small onclick="document.getElementById('exit-modal').remove()" style="cursor:pointer;color:#bbb;font-size:11px;display:block;margin-top:12px"> | |||
No thanks, I prefer the limited experience | |||
</small> | |||
<p class="fine-print">*Free trial requires credit card. Cancellation must be completed at least 24 hours before renewal via written notice sent by post.</p> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
window.claimSpecialOffer = function() { | |||
document.getElementById('exit-modal').innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>🎉 Great choice!</h2> | |||
<p>To activate your free trial, please create an account first.</p> | |||
<input type="text" placeholder="Full name" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:8px 0;font-size:14px" /> | |||
<input type="email" placeholder="Email address" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:8px 0;font-size:14px" /> | |||
<input type="password" placeholder="Password (min. 16 chars, 2 symbols, 1 emoji)" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:8px 0;font-size:14px" /> | |||
<input type="text" placeholder="Date of birth (DD/MM/YYYY)" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:8px 0;font-size:14px" /> | |||
<button onclick="alert('Account created! Please check your email to verify before adding payment details to activate your free trial.')">Create Account →</button> | |||
<br/><small onclick="document.getElementById('exit-modal').remove()" style="cursor:pointer;color:#bbb;font-size:11px;display:block;margin-top:10px">Cancel</small> | |||
</div>`; | |||
}; | |||
function injectAds() { | |||
const imageAds = [ | |||
{ | |||
eyebrow: 'Limited Time', | |||
headline: 'The Internet You Deserve', | |||
subline: 'Starting from £4.99/mo', | |||
brand: 'TurboNet Broadband', | |||
desc: 'Ultrafast fibre. No contracts. Cancel anytime.*', | |||
cta: 'Get Started', | |||
}, | |||
{ | |||
eyebrow: 'New Launch', | |||
headline: 'Invest Smarter Today', | |||
subline: 'AI-powered portfolio management', | |||
brand: 'WealthStream Pro', | |||
desc: 'Your money, working harder. Capital at risk.', | |||
cta: 'Learn More', | |||
}, | |||
{ | |||
eyebrow: 'Flash Sale', | |||
headline: 'Up to 70% Off', | |||
subline: 'Premium software, student prices', | |||
brand: 'SoftBundle', | |||
desc: '4.8★ rated · 2.4 million downloads · Today only', | |||
cta: 'Claim Deal', | |||
}, | |||
{ | |||
eyebrow: 'Partner Content', | |||
headline: 'Your Data. Their Profit.', | |||
subline: 'Take back control with VaultVPN', | |||
brand: 'VaultVPN', | |||
desc: 'Military-grade encryption. Zero logs. $2.49/mo.', | |||
cta: 'Try Free', | |||
}, | |||
{ | |||
eyebrow: 'Sponsored', | |||
headline: 'Read More. Pay Less.', | |||
subline: 'All the news, none of the paywalls', | |||
brand: 'PressPass', | |||
desc: '800+ publications in one subscription.', | |||
cta: 'Start Trial', | |||
}, | |||
]; | |||
const videoAds = [ | |||
{ | |||
title: 'Why 3 Million People Switched to ClearBank', | |||
channel: 'ClearBank', | |||
channelInitial: 'C', | |||
views: '3.2M views · Promoted', | |||
duration: '0:32', | |||
}, | |||
{ | |||
title: 'This $12 Trick Cuts Your Energy Bill in Half', | |||
channel: 'EcoHome Tips', | |||
channelInitial: 'E', | |||
views: '847K views · Sponsored', | |||
duration: '1:14', | |||
}, | |||
{ | |||
title: 'Doctors Hate This One Simple Supplement', | |||
channel: 'VitaCore Health', | |||
channelInitial: 'V', | |||
views: '12M views · Ad', | |||
duration: '2:01', | |||
}, | |||
{ | |||
title: 'The Investment Strategy Banks Don\'t Want You to Know', | |||
channel: 'WealthWatch', | |||
channelInitial: 'W', | |||
views: '5.4M views · Promoted', | |||
duration: '4:47', | |||
}, | |||
]; | |||
const paras = document.querySelectorAll('.mw-body p'); | |||
paras.forEach((p, i) => { | |||
if (i % 2 !== 0) return; | |||
const ad = document.createElement('div'); | |||
ad.className = 'fake-ad'; | |||
if (i % 4 === 0) { | |||
const data = imageAds[(i / 4) % imageAds.length]; | |||
ad.innerHTML = ` | |||
<div class="fake-ad-image"> | |||
<div class="fake-ad-image-text"> | |||
<span class="ad-eyebrow">${data.eyebrow}</span> | |||
<span class="ad-headline">${data.headline}</span> | |||
<span class="ad-subline">${data.subline}</span> | |||
</div> | |||
</div> | |||
<div class="fake-ad-body"> | |||
<div class="fake-ad-body-text"> | |||
<span class="fake-ad-brand">${data.brand}</span> | |||
<span class="fake-ad-desc">${data.desc}</span> | |||
</div> | |||
<button class="fake-ad-cta" onclick="closeAd(this,${i})">${data.cta} →</button> | |||
</div>`; | |||
} else { | |||
const data = videoAds[((i - 2) / 4) % videoAds.length]; | |||
ad.innerHTML = ` | |||
<div class="fake-ad-video"> | |||
<div class="fake-ad-video-badge">AD</div> | |||
<div class="fake-ad-play" onclick="closeAd(this,${i})"></div> | |||
<div class="fake-ad-video-overlay"> | |||
<span class="fake-ad-video-title">${data.title}</span> | |||
<span class="fake-ad-video-duration">${data.duration}</span> | |||
</div> | |||
</div> | |||
<div class="fake-ad-video-body"> | |||
<div class="fake-ad-channel-icon">${data.channelInitial}</div> | |||
<div class="fake-ad-video-meta"> | |||
<span class="fake-ad-video-channel">${data.channel}</span> | |||
<span class="fake-ad-video-views">${data.views}</span> | |||
</div> | |||
<button class="fake-ad-cta" onclick="closeAd(this,${i})">Watch →</button> | |||
</div>`; | |||
} | |||
p.after(ad); | |||
}); | |||
} | |||
window.closeAd = function(btn, i) { | |||
const ad = btn.closest('.fake-ad'); | |||
ad.style.transition = 'opacity 0.2s'; | |||
ad.innerHTML = `<div style="padding:12px 14px;font-size:12px;color:#9ca3af;font-family:Inter,sans-serif">Ad closed. Another will appear shortly.</div>`; | |||
setTimeout(() => { | |||
if (!ad.parentNode) return; | |||
const returnMsgs = ['We noticed you closed our last ad. Here\'s one we think you\'ll prefer.', 'Thanks for your feedback. Here\'s a more relevant ad for you.', 'Ad refreshed based on your preferences.']; | |||
ad.innerHTML = `<div style="padding:12px 14px;font-size:12px;color:#6b7280;font-family:Inter,sans-serif;border-top:3px solid #3b82f6"> | |||
<div style="font-size:10px;color:#9ca3af;margin-bottom:4px">${returnMsgs[i % returnMsgs.length]}</div> | |||
<strong style="color:#374151;font-size:13px">Upgrade to Premium to remove ads</strong> — from $9.99/mo | |||
<button onclick="choosePremium()" style="display:inline-block;margin-left:10px;background:#3b82f6;color:white;border:none;padding:5px 12px;border-radius:4px;font-size:12px;cursor:pointer;font-family:Inter,sans-serif">Subscribe</button> | |||
</div>`; | |||
}, 30000); | |||
}; | |||
function degradePrivacyPolicy() { | |||
const el = document.createElement('div'); | |||
el.id = 'privacy-notice'; | |||
el.innerHTML = ` | |||
<span style="float:right;cursor:pointer;padding:0 12px" id="privacy-close-btn">✕</span> | |||
<marquee scrollamount="3" id="privacy-marquee"> | |||
⚠️ PRIVACY UPDATE: By reading this wiki you grant us a perpetual, irrevocable, royalty-free worldwide licence to your data. | |||
Your device fingerprint has been recorded and shared with 214 partners. | |||
We have updated our terms 47 times this week. Continued use constitutes acceptance. | |||
Your right to erasure is subject to our retention policy (minimum 7 years). | |||
</marquee>`; | |||
document.body.prepend(el); | |||
document.getElementById('privacy-close-btn').onclick = () => { | |||
el.style.height = '0'; | |||
el.style.overflow = 'hidden'; | |||
el.style.transition = 'height 0.3s'; | |||
setTimeout(() => { | |||
el.style.cssText = ''; | |||
const m = el.querySelector('marquee'); | |||
if (m) m.textContent = '📋 IMPORTANT: A new privacy policy update requires your attention. ' + m.textContent; | |||
}, 15000); | |||
}; | |||
} | |||
function addSubscriptionNag() { | |||
const el = document.createElement('div'); | |||
el.id = 'sub-nag'; | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<div style="font-size:11px;color:#999;margin-bottom:4px">You've read 1 article this month</div> | |||
<h2>Get unlimited access</h2> | |||
<p style="color:#666;font-size:14px">Join millions of readers who enjoy unrestricted access to knowledge.</p> | |||
<div class="pricing-grid" style="margin:20px 0"> | |||
<div class="price-card"> | |||
<h3 style="font-size:14px">Basic</h3> | |||
<div style="font-size:24px;font-weight:bold;margin:8px 0">Free</div> | |||
<small style="color:#999;display:block;margin-bottom:12px">With limitations</small> | |||
<button onclick="chooseFree()" style="width:100%;background:#f5f5f5;color:#333;border:1px solid #ddd;padding:8px;border-radius:6px;cursor:pointer">Continue free</button> | |||
</div> | |||
<div class="price-card featured" style="transform:scale(1.05)"> | |||
<div class="best-value">MOST POPULAR</div> | |||
<h3 style="font-size:14px">Premium</h3> | |||
<div style="font-size:24px;font-weight:bold;margin:8px 0">$9.99<span style="font-size:14px;font-weight:normal">/mo</span></div> | |||
<small style="color:#4d96ff;display:block;margin-bottom:12px">No ads · Full access</small> | |||
<button onclick="choosePremium()" style="width:100%;background:#4d96ff;color:white;border:none;padding:8px;border-radius:6px;cursor:pointer;font-weight:bold">Start free trial</button> | |||
</div> | |||
</div> | |||
<div style="font-size:11px;color:#bbb;text-align:center">🔒 Secure checkout · Cancel anytime*</div> | |||
<p class="fine-print">*Cancellation requires 30 days notice. Annual plan billed upfront. Prices exclude VAT.</p> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
window.chooseFree = function() { | |||
document.getElementById('sub-nag').innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>Continue for free</h2> | |||
<p style="color:#666">To continue with free access, please create an account so we can manage your usage limits.</p> | |||
<input type="email" placeholder="Email address" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:12px 0" /> | |||
<button onclick="freeStep2()" style="background:#333;color:white;border:none;padding:10px 20px;border-radius:6px;cursor:pointer;width:100%">Continue →</button> | |||
<br/><small onclick="document.getElementById('sub-nag').remove()" style="cursor:pointer;color:#bbb;font-size:11px;display:block;margin-top:10px;text-align:center">Maybe later</small> | |||
</div>`; | |||
}; | |||
window.freeStep2 = function() { | |||
document.getElementById('sub-nag').innerHTML = ` | |||
<div class="modal-box"> | |||
<div style="font-size:11px;color:#999;margin-bottom:8px">Step 2 of 3</div> | |||
<h2>Verify your email</h2> | |||
<p style="color:#666">We've sent a 6-digit code to your email. Enter it below.</p> | |||
<input type="text" placeholder="000000" maxlength="6" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:12px 0;text-align:center;font-size:24px;letter-spacing:8px" /> | |||
<button onclick="freeStep3()" style="background:#333;color:white;border:none;padding:10px 20px;border-radius:6px;cursor:pointer;width:100%">Verify →</button> | |||
<small style="color:#999;display:block;margin-top:8px;text-align:center">Didn't receive it? <a href="#" onclick="alert('Resent! Check your spam folder.');return false" style="color:#4d96ff">Resend</a></small> | |||
</div>`; | |||
}; | |||
window.freeStep3 = function() { | |||
document.getElementById('sub-nag').innerHTML = ` | |||
<div class="modal-box"> | |||
<div style="font-size:11px;color:#999;margin-bottom:8px">Step 3 of 3</div> | |||
<h2>Almost there!</h2> | |||
<p style="color:#666">To prevent abuse of our free tier, please add a payment method. <strong>You will not be charged.</strong></p> | |||
<input type="text" placeholder="Card number" style="width:100%;padding:10px;border:1px solid #ddd;border-radius:6px;margin:8px 0" /> | |||
<div style="display:flex;gap:8px"> | |||
<input type="text" placeholder="MM/YY" style="flex:1;padding:10px;border:1px solid #ddd;border-radius:6px" /> | |||
<input type="text" placeholder="CVV" style="flex:1;padding:10px;border:1px solid #ddd;border-radius:6px" /> | |||
</div> | |||
<button onclick="alert('Free account activated! Note: Your card will be charged $9.99/mo after your 7-day free trial unless cancelled.');document.getElementById('sub-nag').remove();" style="background:#4d96ff;color:white;border:none;padding:10px 20px;border-radius:6px;cursor:pointer;width:100%;margin-top:12px;font-weight:bold">Activate Free Access →</button> | |||
<div style="display:flex;align-items:center;gap:6px;justify-content:center;margin-top:10px"> | |||
<span style="font-size:11px;color:#bbb">🔒 256-bit SSL · Verified by Visa · PCI Compliant</span> | |||
</div> | |||
</div>`; | |||
}; | |||
window.choosePremium = function() { | |||
document.getElementById('sub-nag').innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>✅ Great choice!</h2> | |||
<p>Starting your free trial...</p> | |||
<div style="background:#eee;border-radius:4px;height:6px;overflow:hidden;margin:16px 0"> | |||
<div style="background:#4d96ff;height:100%;width:0%;transition:width 2s" id="trial-bar"></div> | |||
</div> | |||
<p style="font-size:12px;color:#999">Setting up your account...</p> | |||
</div>`; | |||
setTimeout(() => { document.getElementById('trial-bar').style.width = '100%'; }, 100); | |||
setTimeout(() => chooseFree(), 2200); | |||
}; | |||
function addAutoplayVideo() { | |||
spawnVideoAd('right'); | |||
} | |||
function spawnVideoAd(side) { | |||
const existing = document.getElementById('autoplay-video'); | |||
if (existing) existing.remove(); | |||
const el = document.createElement('div'); | |||
el.id = 'autoplay-video'; | |||
el.style.cssText = side === 'right' ? 'bottom:80px;right:16px' : 'bottom:80px;left:16px'; | |||
let count = 15; | |||
el.innerHTML = ` | |||
<div id="video-close" onclick="closeVideoAd('${side}')">✕ Close Ad</div> | |||
<div id="fake-video"><div id="video-placeholder">▶ Ad plays in <span id="ad-countdown">${count}</span>s</div></div> | |||
<div id="video-label">AD · SOUND ON 🔊</div>`; | |||
document.body.appendChild(el); | |||
const iv = setInterval(() => { | |||
count--; | |||
const c = document.getElementById('ad-countdown'); | |||
if (c) c.textContent = count; | |||
if (count <= 0) { | |||
clearInterval(iv); | |||
const p = document.getElementById('video-placeholder'); | |||
if (p) p.textContent = 'Thanks for watching! Next ad in 3s...'; | |||
setTimeout(() => spawnVideoAd(side === 'right' ? 'left' : 'right'), 3000); | |||
} | |||
}, 1000); | |||
} | |||
window.closeVideoAd = function(side) { | |||
const el = document.getElementById('autoplay-video'); | |||
if (el) el.remove(); | |||
setTimeout(() => spawnVideoAd(side === 'right' ? 'left' : 'right'), 8000); | |||
}; | |||
function addNotificationPrompt() { | |||
const el = document.createElement('div'); | |||
el.id = 'notif-prompt'; | |||
el.innerHTML = ` | |||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px"> | |||
<span style="font-size:20px">🔔</span> | |||
<div> | |||
<strong style="font-size:13px">consumerrights.wiki wants to send you notifications</strong><br/> | |||
<span style="font-size:12px;color:#666">Get the latest updates, breaking news, and partner offers</span> | |||
</div> | |||
</div> | |||
<div style="display:flex;gap:8px;justify-content:flex-end"> | |||
<button id="notif-block" onclick="blockNotif()">Block</button> | |||
<button id="notif-allow" onclick="allowNotif()">Allow</button> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
window.allowNotif = function() { | |||
document.getElementById('notif-prompt').innerHTML = `<span style="font-size:13px">✅ Notifications enabled. You'll receive up to 47 per day.</span>`; | |||
setTimeout(() => document.getElementById('notif-prompt').remove(), 3000); | |||
}; | |||
window.blockNotif = function() { | |||
const el = document.getElementById('notif-prompt'); | |||
el.innerHTML = `<span style="font-size:13px;color:#666">Saving preferences...</span>`; | |||
setTimeout(() => { | |||
el.innerHTML = ` | |||
<span style="font-size:13px;color:#c00">⚠️ Unable to save notification preference in your region.</span><br/> | |||
<small style="color:#999">Notifications have been enabled by default. <a href="#" onclick="blockNotif2();return false" style="color:#4d96ff">Try again</a></small>`; | |||
}, 1500); | |||
}; | |||
window.blockNotif2 = function() { | |||
document.getElementById('notif-prompt').innerHTML = ` | |||
<span style="font-size:13px;color:#666">Please confirm you want to block notifications:</span> | |||
<div style="margin-top:8px;display:flex;gap:8px"> | |||
<button onclick="document.getElementById('notif-prompt').remove()">Yes, block</button> | |||
<button onclick="allowNotif()">Actually, allow</button> | |||
</div>`; | |||
}; | |||
function addSurveyPopup() { | |||
let q = 0; | |||
const questions = [ | |||
{ q: 'How would you rate your experience today?', opts: ['Excellent', 'Good', 'Fair', 'Poor'] }, | |||
{ q: 'How likely are you to recommend us to a friend?', opts: ['Very likely', 'Likely', 'Unlikely', 'Never'] }, | |||
{ q: 'Which best describes you?', opts: ['Student', 'Professional', 'Researcher', 'Other'] }, | |||
{ q: 'How often do you visit this wiki?', opts: ['Daily', 'Weekly', 'Monthly', 'First time'] }, | |||
{ q: 'What is your household income?', opts: ['Under £20k', '£20-50k', '£50-100k', 'Over £100k'] }, | |||
{ q: 'Do you have any outstanding debts?', opts: ['None', 'Some', 'Significant', 'Prefer not to say'] }, | |||
{ q: 'Are you currently employed?', opts: ['Full time', 'Part time', 'Unemployed', 'Retired'] }, | |||
]; | |||
const el = document.createElement('div'); | |||
el.id = 'survey-modal'; | |||
function renderQ() { | |||
const qObj = questions[q]; | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<div style="font-size:11px;color:#999;margin-bottom:8px">Quick survey · Question ${q+1} of ${questions.length}</div> | |||
<div style="background:#f0f0f0;border-radius:4px;height:4px;margin-bottom:16px;overflow:hidden"> | |||
<div style="background:#4d96ff;height:100%;width:${(q/questions.length)*100}%;transition:width 0.3s"></div> | |||
</div> | |||
<h3 style="margin:0 0 16px;font-size:16px">${qObj.q}</h3> | |||
<div style="display:flex;flex-direction:column;gap:8px"> | |||
${qObj.opts.map(o => `<button onclick="answerSurvey()" style="background:#f5f5f5;border:1px solid #ddd;padding:10px;border-radius:6px;cursor:pointer;text-align:left">${o}</button>`).join('')} | |||
</div> | |||
<br/><small id="survey-skip" style="cursor:pointer;color:#bbb;font-size:11px" onclick="skipSurvey()">Skip question</small> | |||
</div>`; | |||
} | |||
window.answerSurvey = function() { | |||
q++; | |||
if (q >= questions.length) { | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>✅ Thank you!</h2> | |||
<p>Your responses help us improve. You've been entered into our prize draw.*</p> | |||
<button onclick="document.getElementById('survey-modal').remove()">Done</button> | |||
<p class="fine-print">*Prize: one additional free article per month for 3 months.</p> | |||
</div>`; | |||
} else { renderQ(); } | |||
}; | |||
window.skipSurvey = function() { | |||
q = 0; | |||
setTimeout(() => { | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>😔 Survey reset</h2> | |||
<p>To ensure data quality, skipping a question restarts the survey from the beginning.</p> | |||
<button onclick="restartSurvey()">Start again</button> | |||
<br/><small onclick="document.getElementById('survey-modal').remove()" style="cursor:pointer;color:#bbb;font-size:11px;display:block;margin-top:10px">Exit survey (results discarded)</small> | |||
</div>`; | |||
}, 200); | |||
}; | |||
window.restartSurvey = function() { q = 0; renderQ(); }; | |||
document.body.appendChild(el); | |||
renderQ(); | |||
} | |||
function addLiveChatBubble() { | |||
const el = document.createElement('div'); | |||
el.id = 'live-chat'; | |||
el.innerHTML = ` | |||
<div id="chat-bubble" onclick="toggleChat()">💬<span id="chat-badge">1</span></div> | |||
<div id="chat-window" style="display:none"> | |||
<div id="chat-header"> | |||
Support <small style="opacity:0.8">· Typically replies instantly</small> | |||
<span style="float:right;cursor:pointer" onclick="toggleChat()">✕</span> | |||
</div> | |||
<div id="chat-messages"> | |||
<div class="chat-msg" style="background:#f0f7ff"><strong>Sarah (Support)</strong><br/>Hi there! I noticed you've been reading for a while. Can I help you find what you're looking for? 😊</div> | |||
</div> | |||
<input id="chat-input" type="text" placeholder="Type a message..." onkeydown="sendChat(event)" /> | |||
</div>`; | |||
document.body.appendChild(el); | |||
setTimeout(() => { | |||
document.getElementById('chat-window').style.display = 'block'; | |||
document.getElementById('chat-badge').style.display = 'none'; | |||
}, 5000); | |||
} | |||
window.toggleChat = function() { | |||
const w = document.getElementById('chat-window'); | |||
w.style.display = w.style.display !== 'none' ? 'none' : 'block'; | |||
}; | |||
const chatResponses = [ | |||
'I understand! Have you considered upgrading to Premium for an ad-free experience?', | |||
'Great question! That feature is available on our Premium plan from $9.99/mo.', | |||
'I\'m sorry to hear that. I\'ve escalated your concern to our billing team.', | |||
'I\'d love to help! Could you first verify your account email?', | |||
'That\'s outside my scope. I can connect you to our sales team?', | |||
'Your query has been logged as ticket #84721. Response time: 5-7 business days.', | |||
'I completely understand your frustration. Our Premium plan resolves this issue.', | |||
'Let me check... ⏳ still checking... I\'ll need to transfer you to another agent.', | |||
]; | |||
window.sendChat = function(e) { | |||
if (e.key !== 'Enter') return; | |||
const input = document.getElementById('chat-input'); | |||
const msgs = document.getElementById('chat-messages'); | |||
if (!input.value.trim()) return; | |||
const userMsg = document.createElement('div'); | |||
userMsg.className = 'chat-msg user-msg'; | |||
userMsg.textContent = input.value; | |||
msgs.appendChild(userMsg); | |||
input.value = ''; | |||
const typing = document.createElement('div'); | |||
typing.className = 'chat-msg'; | |||
typing.innerHTML = '<em style="color:#aaa">Sarah is typing...</em>'; | |||
msgs.appendChild(typing); | |||
msgs.scrollTop = msgs.scrollHeight; | |||
setTimeout(() => { | |||
typing.remove(); | |||
const botMsg = document.createElement('div'); | |||
botMsg.className = 'chat-msg'; | |||
botMsg.innerHTML = `<strong>Sarah (Support)</strong><br/>${chatResponses[Math.floor(Math.random() * chatResponses.length)]}`; | |||
msgs.appendChild(botMsg); | |||
msgs.scrollTop = msgs.scrollHeight; | |||
}, 1500 + Math.random() * 1000); | |||
}; | |||
function injectBreakingNewsTicker() { | |||
const el = document.createElement('div'); | |||
el.id = 'news-ticker'; | |||
el.innerHTML = ` | |||
<span class="ticker-label">LIVE</span> | |||
<marquee scrollamount="4"> | |||
1,204 users currently reading this article • | |||
Your free article limit resets in 29 days • | |||
SPONSORED: Top 10 things you didn't know about subscribing • | |||
Data brokers have accessed your profile 47 times today • | |||
Our terms of service were updated 3 minutes ago • | |||
Premium users are reading 3x more articles than you right now • | |||
⚠️ Your session will expire in 10 minutes without an account • | |||
</marquee>`; | |||
document.body.prepend(el); | |||
} | |||
function degradeCursor() { | |||
document.addEventListener('mousemove', function(e) { | |||
if (Math.random() > 0.25) return; | |||
const spark = document.createElement('div'); | |||
spark.className = 'cursor-spark'; | |||
spark.textContent = ['💰','📢','🍪','👁️','💊','📧','💳','📊'][Math.floor(Math.random()*8)]; | |||
spark.style.left = e.pageX + 'px'; | |||
spark.style.top = e.pageY + 'px'; | |||
document.body.appendChild(spark); | |||
setTimeout(() => spark.remove(), 800); | |||
}); | |||
} | |||
function addConfetti() { | |||
for (let i = 0; i < 40; i++) { | |||
setTimeout(() => { | |||
const el = document.createElement('div'); | |||
el.className = 'confetti-piece'; | |||
el.textContent = ['💰','📊','🍪','📧','💳','👁️','📱','🎯'][Math.floor(Math.random()*8)]; | |||
el.style.left = Math.random() * 100 + 'vw'; | |||
el.style.animationDuration = (Math.random() * 2 + 1.5) + 's'; | |||
document.body.appendChild(el); | |||
setTimeout(() => el.remove(), 3500); | |||
}, i * 80); | |||
} | |||
} | |||
function addFakeSecurityAlert() { | |||
const el = document.createElement('div'); | |||
el.id = 'security-alert'; | |||
el.innerHTML = ` | |||
<div class="modal-box" style="border-top:4px solid #e53e3e;max-width:460px"> | |||
<div class="modal-badge" style="background:#e53e3e">SECURITY NOTICE</div> | |||
<h2 style="color:#e53e3e;margin-top:8px">Unusual activity detected</h2> | |||
<p style="color:#555">We detected a sign-in attempt to your account from:</p> | |||
<div style="background:#fff5f5;border:1px solid #fed7d7;border-radius:6px;padding:12px;margin:12px 0;font-size:13px"> | |||
<strong>Location:</strong> ${Intl.DateTimeFormat().resolvedOptions().timeZone || 'Unknown'}<br/> | |||
<strong>Device:</strong> ${navigator.platform || 'Unknown device'}<br/> | |||
<strong>Time:</strong> ${new Date().toLocaleTimeString()} | |||
</div> | |||
<p style="font-size:13px;color:#555">If this was you, no action is needed. If not, secure your account immediately.</p> | |||
<div style="display:flex;gap:8px;margin-top:16px"> | |||
<button onclick="document.getElementById('security-alert').remove()" style="flex:1;background:#e53e3e;color:white;border:none;padding:10px;border-radius:6px;cursor:pointer;font-weight:bold">Secure My Account</button> | |||
<button onclick="dismissSecurityAlert()" style="flex:1;background:#f5f5f5;color:#333;border:1px solid #ddd;padding:10px;border-radius:6px;cursor:pointer">This was me</button> | |||
</div> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
window.dismissSecurityAlert = function() { | |||
document.getElementById('security-alert').innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>✅ Got it</h2> | |||
<p>We've marked this activity as safe.</p> | |||
<p style="margin-top:8px;font-size:13px;color:#666">To prevent future alerts, <strong>create a free account</strong> and enable two-factor authentication.</p> | |||
<button onclick="chooseFree();document.getElementById('security-alert').remove()">Create Account</button> | |||
<br/><small onclick="document.getElementById('security-alert').remove()" style="cursor:pointer;color:#bbb;font-size:11px;display:block;margin-top:10px">Dismiss</small> | |||
</div>`; | |||
}; | |||
function addTabHijack() { | |||
const original = document.title; | |||
let awayCount = 0; | |||
document.addEventListener('visibilitychange', () => { | |||
if (document.hidden) { | |||
awayCount++; | |||
const msgs = ['😢 Come back! We miss you...', '🔴 URGENT: Your session expires soon', '💸 OFFER: 50% off Premium — today only', `⚠️ ${awayCount * 3} notifications while you were away`, '🍪 Cookie consent expires when you leave']; | |||
document.title = msgs[awayCount % msgs.length]; | |||
} else { | |||
document.title = original; | |||
if (awayCount > 0) showTabReturnPopup(awayCount); | |||
} | |||
}); | |||
} | |||
function showTabReturnPopup(count) { | |||
const existing = document.getElementById('tab-return-popup'); | |||
if (existing) existing.remove(); | |||
const el = document.createElement('div'); | |||
el.id = 'tab-return-popup'; | |||
el.innerHTML = ` | |||
<strong>👋 Welcome back!</strong><br/> | |||
<span style="font-size:13px;color:#666">You missed <strong>${count * 3} personalised updates</strong> while away. ${count} new ads have been queued for you.</span> | |||
<div style="margin-top:10px"><button onclick="document.getElementById('tab-return-popup').remove()">OK</button></div>`; | |||
document.body.appendChild(el); | |||
setTimeout(() => { if (el.parentNode) el.remove(); }, 6000); | |||
} | |||
function addScrollBlocker() { | |||
let triggered = false; | |||
window.addEventListener('scroll', () => { | |||
if (triggered) return; | |||
if (window.scrollY > 400) { | |||
triggered = true; | |||
const overlay = document.createElement('div'); | |||
overlay.id = 'scroll-overlay'; | |||
document.body.appendChild(overlay); | |||
const el = document.createElement('div'); | |||
el.id = 'scroll-block'; | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<div class="modal-badge">MEMBER CONTENT</div> | |||
<h2>You're reading a member article</h2> | |||
<p style="color:#555">This article is freely available to members. Create a free account to continue reading.</p> | |||
<div style="background:#f9f9f9;border-radius:6px;padding:12px;margin:12px 0;font-size:13px;color:#666"> | |||
✓ Unlimited articles ✓ Save to reading list<br/> | |||
✓ Comment on articles ✓ Personalised feed | |||
</div> | |||
<button onclick="chooseFree();document.getElementById('scroll-block').remove();document.getElementById('scroll-overlay').remove();" style="width:100%;background:#4d96ff;color:white;border:none;padding:12px;border-radius:6px;cursor:pointer;font-weight:bold;font-size:15px">Create free account →</button> | |||
<br/><small onclick="continueReading()" style="cursor:pointer;color:#bbb;font-size:11px;display:block;margin-top:10px;text-align:center">Continue without account (limited access)</small> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
}); | |||
} | |||
window.continueReading = function() { | |||
const sb = document.getElementById('scroll-block'); | |||
const so = document.getElementById('scroll-overlay'); | |||
if (sb) sb.remove(); | |||
if (so) so.remove(); | |||
setTimeout(() => { | |||
let retrigger = false; | |||
window.addEventListener('scroll', function handler() { | |||
if (retrigger) return; | |||
if (window.scrollY > 800) { | |||
retrigger = true; | |||
window.removeEventListener('scroll', handler); | |||
const el = document.createElement('div'); | |||
el.id = 'scroll-block'; | |||
el.innerHTML = ` | |||
<div class="modal-box"> | |||
<h2>You've reached your limit</h2> | |||
<p style="color:#555">You can read <strong>2 articles per month</strong> without an account. You've used both.</p> | |||
<button onclick="chooseFree();document.getElementById('scroll-block').remove();" style="width:100%;background:#4d96ff;color:white;border:none;padding:12px;border-radius:6px;cursor:pointer;font-weight:bold">Create free account →</button> | |||
<br/><small style="color:#bbb;font-size:11px;display:block;margin-top:10px;text-align:center">Resets in 29 days</small> | |||
</div>`; | |||
document.body.appendChild(el); | |||
} | |||
}); | |||
}, 500); | |||
}; | |||
function addBeforeUnloadNag() { | |||
window.addEventListener('beforeunload', (e) => { e.preventDefault(); e.returnValue = ''; }); | |||
} | |||
function addFloatingCountdown() { | |||
const el = document.createElement('div'); | |||
el.id = 'urgency-countdown'; | |||
el.innerHTML = ` | |||
<div id="urgency-inner"> | |||
<div style="font-size:10px;color:#999;margin-bottom:4px">LIMITED TIME OFFER</div> | |||
<strong style="font-size:13px">Get 3 months free</strong> | |||
<div id="countdown-timer">14:59</div> | |||
<small style="font-size:11px;color:#999">Then $9.99/mo. Cancel anytime.*</small> | |||
<button onclick="choosePremium()">Claim offer →</button> | |||
<div id="countdown-close" onclick="closeCountdown()">✕</div> | |||
</div>`; | |||
document.body.appendChild(el); | |||
let seconds = 899; | |||
const iv = setInterval(() => { | |||
seconds--; | |||
if (seconds <= 0) { | |||
clearInterval(iv); | |||
const t = document.getElementById('countdown-timer'); | |||
if (t) { t.textContent = 'EXPIRED'; t.style.color = '#999'; } | |||
return; | |||
} | |||
const t = document.getElementById('countdown-timer'); | |||
if (t) t.textContent = Math.floor(seconds/60) + ':' + String(seconds%60).padStart(2,'0'); | |||
}, 1000); | |||
} | |||
window.closeCountdown = function() { | |||
const el = document.getElementById('urgency-countdown'); | |||
if (!el) return; | |||
el.style.transform = 'translateX(120%)'; | |||
el.style.transition = 'transform 0.3s ease'; | |||
setTimeout(() => { | |||
if (!el.parentNode) return; | |||
el.style.transform = ''; | |||
el.style.transition = ''; | |||
const inner = document.getElementById('urgency-inner'); | |||
if (inner) inner.insertAdjacentHTML('afterbegin', '<div style="font-size:10px;background:#fff3cd;color:#856404;padding:4px 8px;border-radius:4px;margin-bottom:8px">⚠️ Closing this resets the offer timer</div>'); | |||
}, 5000); | |||
}; | |||
function addFakeProgressBar() { | |||
const el = document.createElement('div'); | |||
el.id = 'fake-progress-bar'; | |||
el.innerHTML = `<div id="fake-progress-fill"></div>`; | |||
document.body.prepend(el); | |||
let pct = 0; | |||
let goingDown = false; | |||
setInterval(() => { | |||
if (goingDown) { pct -= 5; if (pct <= 10) goingDown = false; } | |||
else { pct += Math.random() * 4; if (pct > 89) goingDown = true; } | |||
const fill = document.getElementById('fake-progress-fill'); | |||
if (fill) fill.style.width = pct + '%'; | |||
}, 400); | |||
} | |||
function addViewerCounter() { | |||
const el = document.createElement('div'); | |||
el.id = 'viewer-counter'; | |||
el.innerHTML = `<span id="viewer-dot">●</span> <span id="viewer-num">847</span> people reading this now`; | |||
const body = document.querySelector('.mw-body'); | |||
if (body) body.prepend(el); | |||
let count = 847; | |||
setInterval(() => { | |||
count += Math.floor(Math.random() * 5) - 1; | |||
if (count < 800) count = 800; | |||
const n = document.getElementById('viewer-num'); | |||
if (n) n.textContent = count.toLocaleString(); | |||
}, 2000); | |||
} | |||
}); | |||