// ═══════════════════════════════════════════════════════════ // UTILS CLASS // ═══════════════════════════════════════════════════════════ class Utils { static esc(s) { if (!s) return ""; const map = { "'": "'", '"': """, "&": "&", "<": "<", ">": ">", }; return s.replace(/[&<>"']/g, (ch) => map[ch]); } static decodeHTML(s) { const textarea = document.createElement("textarea"); textarea.innerHTML = s; return textarea.value; } static fmt(n) { if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`; if (n >= 1000) return `${(n / 1000).toFixed(1)}K`; return n?.toString() ?? "0"; } static timeAgo(ts) { const s = Math.floor((Date.now() - ts) / 1000); if (s < 60) return "NOW"; if (s < 3600) return `${Math.floor(s / 60)}M`; if (s < 86400) return `${Math.floor(s / 3600)}H`; return `${Math.floor(s / 86400)}D`; } static highlight(text, query) { if (!query) return text; const words = query.split(/\s+/).filter((w) => w.length > 2); let result = text; words.forEach((w) => { const regex = new RegExp(`(${w})`, "gi"); result = result.replace(regex, "$1"); }); return result; } static async copy(text) { try { await navigator.clipboard.writeText(text); Toast.show("COPIED"); } catch (err) { console.error("Clipboard write failed:", err); Toast.show("CLIPBOARD FAILED"); } } static toast(message, duration = 3000) { Toast.show(message, duration); } static safeEval(expr) { const safeExpr = expr.replace(/[^0-9+\-*/().^]/g, "").replace(/\^/g, "**"); try { const result = new Function(`return ${safeExpr}`)(); return typeof result === "number" && !Number.isNaN(result) && Number.isFinite(result) ? result : "ERR"; } catch { return "ERR"; } } static generateUUID() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); }); } static rgbToHsl(r, g, b) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b), min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; } else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break; case g: h = ((b - r) / d + 2) / 6; break; case b: h = ((r - g) / d + 4) / 6; break; } } return `${Math.round(h * 360)}°, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%`; } static debounce(func, wait) { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } static generatePassword(length = 20) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*"; return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join(""); } } // ═══════════════════════════════════════════════════════════ // TOAST CLASS // ═══════════════════════════════════════════════════════════ class Toast { static show(message, duration = 3000) { const container = document.getElementById("toast-container") || (() => { const div = document.createElement("div"); div.id = "toast-container"; div.className = "toast-container"; document.body.appendChild(div); return div; })(); const toast = document.createElement("div"); toast.className = "toast"; toast.textContent = message; container.appendChild(toast); setTimeout(() => { toast.style.opacity = "0"; toast.style.transform = "translateY(20px)"; setTimeout(() => toast.remove(), 200); }, duration); } } // ═══════════════════════════════════════════════════════════ // GENERIC FETCHER // ═══════════════════════════════════════════════════════════ class Fetcher { constructor() { this.abortController = null; } async fetch(url, options = {}) { this.abort(); this.abortController = new AbortController(); try { const response = await fetch(url, { ...options, signal: this.abortController.signal, }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return await response.json(); } catch (error) { if (error.name === "AbortError") throw error; console.error("Fetch failed:", error); return null; } } abort() { this.abortController?.abort(); } } // ═══════════════════════════════════════════════════════════ // TRADINGVIEW WIDGET CLASS // ═══════════════════════════════════════════════════════════ class TradingViewWidget { constructor(containerId, symbol, isLight = false) { this.containerId = containerId; this.symbol = symbol; this.isLight = isLight; this.widget = null; } async init() { if (!window.TradingView) { await this.loadScript(); } this.createWidget(); } loadScript() { return new Promise((resolve, reject) => { const script = document.createElement("script"); script.src = "https://s3.tradingview.com/tv.js"; script.onload = resolve; script.onerror = () => reject(new Error("Failed to load TradingView script")); document.head.appendChild(script); }); } createWidget() { this.widget?.remove(); this.widget = new TradingView.widget({ allow_symbol_change: true, backgroundColor: this.isLight ? "rgba(244, 244, 245, 1)" : "rgba(0,0,0,1)", container_id: this.containerId, enable_publishing: false, gridColor: this.isLight ? "rgba(220, 220, 220, 1)" : "rgba(30,30,30,1)", height: 500, interval: "D", locale: "en", studies: ["Volume@tv-basicstudies"], style: "1", symbol: this.symbol, theme: this.isLight ? "light" : "dark", timezone: "Etc/UTC", toolbar_bg: this.isLight ? "#f4f4f5" : "#000", width: "100%", }); } destroy() { this.widget?.remove(); this.widget = null; } updateSymbol(symbol) { this.symbol = symbol; this.widget?.chart()?.setSymbol?.(symbol); } } // ═══════════════════════════════════════════════════════════ // SOURCES CONFIGURATION // ═══════════════════════════════════════════════════════════ const SOURCES = { dictionary: { bang: "!d", enabled: true, fetch: async (q, signal) => { const data = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${q}`, { signal, }).then((r) => (r.ok ? r.json() : [])); return data.slice(0, 3).flatMap( (entry) => entry.meanings?.slice(0, 2).map((meaning) => ({ meta: { phonetic: entry.phonetic }, snippet: meaning.definitions?.[0]?.definition ?? "", source: "dictionary", title: `${entry.word} [${meaning.partOfSpeech}]`, url: entry.sourceUrls?.[0] ?? "#", })) ?? [], ); }, name: "DICTIONARY", }, github: { bang: "!g", enabled: true, fetch: async (q, signal) => { const data = await fetch( `https://api.github.com/search/repositories?q=${q}&per_page=8&sort=stars`, { signal }, ).then((r) => r.json()); return (data.items ?? []).map((item) => ({ meta: { forks: item.forks_count, lang: item.language, stars: item.stargazers_count }, snippet: item.description ?? "NO DESCRIPTION", source: "github", title: item.full_name, url: item.html_url, })); }, name: "GITHUB", }, hackernews: { bang: "!hn", enabled: true, fetch: async (q, signal) => { const data = await fetch(`https://hn.algolia.com/api/v1/search?query=${q}&hitsPerPage=8`, { signal, }).then((r) => r.json()); return (data.hits ?? []) .filter((hit) => hit.title) .map((hit) => ({ meta: { comments: hit.num_comments, points: hit.points }, snippet: `${hit.points ?? 0} POINTS // ${hit.author}`, source: "hackernews", title: hit.title, url: hit.url ?? `https://news.ycombinator.com/item?id=${hit.objectID}`, })); }, name: "HACKERNEWS", }, news: { bang: "!n", enabled: true, fetch: async (q, signal) => { const [hn, rd] = await Promise.all([ fetch(`https://hn.algolia.com/api/v1/search_by_date?query=${q}&tags=story&hitsPerPage=6`, { signal, }) .then((r) => r.json()) .catch(() => ({ hits: [] })), fetch( `https://www.reddit.com/r/worldnews+news+technology/search.json?q=${q}&restrict_sr=1&sort=new&limit=6`, { signal }, ) .then((r) => r.json()) .catch(() => ({ data: { children: [] } })), ]); const hnResults = (hn.hits ?? []).map((hit) => ({ meta: { date: hit.created_at_i, lang: "HN" }, snippet: `HACKERNEWS // ${hit.points ?? 0} PTS // ${Utils.timeAgo(hit.created_at_i * 1000)}`, source: "news", title: hit.title, url: hit.url ?? `https://news.ycombinator.com/item?id=${hit.objectID}`, })); const redditResults = (rd.data?.children ?? []).map((child) => ({ meta: { date: child.data.created_utc, lang: "RD" }, snippet: `REDDIT r/${child.data.subreddit.toUpperCase()} // ${child.data.score} UPVOTES`, source: "news", title: child.data.title, url: `https://reddit.com${child.data.permalink}`, })); return [...hnResults, ...redditResults].sort((a, b) => b.meta.date - a.meta.date); }, name: "AGGREGATE NEWS", }, npm: { bang: "!npm", enabled: true, fetch: async (q, signal) => { const data = await fetch(`https://registry.npmjs.org/-/v1/search?text=${q}&size=8`, { signal, }).then((r) => r.json()); return (data.objects ?? []).map((obj) => ({ meta: { version: obj.package.version }, snippet: obj.package.description ?? "NO DESCRIPTION", source: "npm", title: obj.package.name, url: `https://npmjs.com/package/${obj.package.name}`, })); }, name: "NPM", }, openlibrary: { bang: "!b", enabled: true, fetch: async (q, signal) => { const data = await fetch(`https://openlibrary.org/search.json?q=${q}&limit=6`, { signal, }).then((r) => r.json()); return (data.docs ?? []).map((book) => ({ meta: { year: book.first_publish_year }, snippet: `BY ${(book.author_name ?? ["UNKNOWN"]).join(", ").toUpperCase()}`, source: "openlibrary", title: book.title, url: `https://openlibrary.org${book.key}`, })); }, name: "OPENLIBRARY", }, reddit: { bang: "!r", enabled: true, fetch: async (q, signal) => { const data = await fetch(`https://www.reddit.com/search.json?q=${q}&limit=8`, { signal, }).then((r) => r.json()); return (data.data?.children ?? []).map((child) => ({ meta: { comments: child.data.num_comments, score: child.data.score }, snippet: child.data.selftext?.substring(0, 200) ?? `r/${child.data.subreddit}`, source: "reddit", title: child.data.title, url: `https://reddit.com${child.data.permalink}`, })); }, name: "REDDIT", }, stackoverflow: { bang: "!so", enabled: true, fetch: async (q, signal) => { const data = await fetch( `https://api.stackexchange.com/2.3/search?order=desc&sort=relevance&intitle=${q}&site=stackoverflow&pagesize=8`, { signal }, ).then((r) => r.json()); return (data.items ?? []).map((item) => ({ meta: { answers: item.answer_count, score: item.score }, snippet: `${item.answer_count} ANSWERS // ${item.view_count} VIEWS`, source: "stackoverflow", title: Utils.decodeHTML(item.title), url: item.link, })); }, name: "STACKOVERFLOW", }, wikipedia: { bang: "!w", enabled: true, fetch: async (q, signal) => { const data = await fetch( `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${q}&format=json&origin=*&srlimit=8`, { signal }, ).then((r) => r.json()); return (data.query?.search ?? []).map((item) => ({ meta: { words: item.wordcount }, snippet: item.snippet.replace(/<[^>]*>/g, ""), source: "wikipedia", title: item.title, url: `https://en.wikipedia.org/wiki/${item.title}`, })); }, instant: async (q, signal) => { const searchData = await fetch( `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${q}&format=json&origin=*&srlimit=1`, { signal }, ).then((r) => r.json()); if (!searchData.query?.search?.length) return null; const title = searchData.query.search[0].title; const pageData = await fetch( `https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&titles=${title}&format=json&origin=*`, { signal }, ).then((r) => r.json()); const page = Object.values(pageData.query?.pages ?? {})[0]; if (!page?.extract) return null; return { content: page.extract, source: "WIKIPEDIA", title: page.title, url: `https://en.wikipedia.org/wiki/${title}`, }; }, name: "WIKIPEDIA", }, }; const STOCK_EXCHANGES = { AAPL: "NASDAQ", ADA: "CRYPTO", AMD: "NASDAQ", AMZN: "NASDAQ", BAC: "NYSE", BTC: "CRYPTO", DIS: "NYSE", DOGE: "CRYPTO", ETH: "CRYPTO", EURUSD: "FX", GBPUSD: "FX", GOOG: "NASDAQ", GOOGL: "NASDAQ", INTC: "NASDAQ", JNJ: "NYSE", JPM: "NYSE", KO: "NYSE", META: "NASDAQ", MSFT: "NASDAQ", NFLX: "NASDAQ", NVDA: "NASDAQ", PYPL: "NASDAQ", QQQ: "NASDAQ", SOL: "CRYPTO", SPY: "AMEX", TSLA: "NASDAQ", USDJPY: "FX", V: "NYSE", WMT: "NYSE", XOM: "NYSE", XRP: "CRYPTO", }; // ═══════════════════════════════════════════════════════════ // SEARCH CLASS // ═══════════════════════════════════════════════════════════ class Search { constructor(sources, stockExchanges) { this.sources = sources; this.stockExchanges = stockExchanges; this.fetcher = new Fetcher(); this.state = { calcExpr: "", calcResult: "0", hasSearched: false, history: JSON.parse(localStorage.getItem("sh") ?? "[]"), query: "", results: [], selectedResultIndex: -1, watchlist: JSON.parse(localStorage.getItem("sw") ?? '["TSLA", "AAPL", "BTC", "ETH"]'), }; this.timerSec = 0; this.timerInterval = null; this.suggestions = []; this.initElements(); this.init(); } initElements() { this.searchInput = document.getElementById("search-input"); this.resultsEl = document.getElementById("results"); this.heroEl = document.getElementById("hero"); this.mainEl = document.getElementById("main-content"); this.statsBar = document.getElementById("stats-bar"); } init() { this.initEventListeners(); this.initCalculator(); this.renderSources(); this.renderHistory(); this.renderWatchlist(); this.updateClocks(); setInterval(() => this.updateClocks(), 1000); this.handleUrlQueryOnLoad(); } // ─── Event Listeners ───────────────────────────── initEventListeners() { // Search this.searchInput.addEventListener( "input", Utils.debounce((e) => { this.search(e.target.value); }, 300), ); this.searchInput.addEventListener("keydown", (e) => { if (e.key === "Enter") this.search(this.searchInput.value); if (e.key === "Escape") this.goHome(); }); // Form document.getElementById("search-form")?.addEventListener("submit", (e) => { e.preventDefault(); this.search(this.searchInput.value); }); // Buttons document.getElementById("clear-btn")?.addEventListener("pointerdown", () => { this.goHome(); this.searchInput.focus(); }); document.getElementById("voice-btn")?.addEventListener("pointerdown", () => this.voiceSearch()); document.getElementById("theme-btn")?.addEventListener("pointerdown", () => this.toggleTheme()); // Widget toggles document.querySelectorAll("[data-toggle]").forEach((el) => { el.addEventListener("pointerdown", (e) => { e.currentTarget.closest(".widget")?.classList.toggle("open"); }); }); // Shortcuts, bangs, tools document.addEventListener("pointerdown", (e) => { const target = e.target; if (target.closest(".shortcut, .bang")) { if (target.dataset.bang) this.insertBang(target.dataset.bang); if (target.dataset.query) this.searchFor(target.dataset.query); if (target.dataset.tool) this[`tool${target.dataset.tool.toUpperCase()}`]?.(); } if (target.closest(".tool-btn")) { const tool = target.closest(".tool-btn")?.dataset.tool; if (tool) this[`tool${tool.toUpperCase()}`]?.(); } if (target.closest(".source-item")) { const source = target.closest(".source-item")?.dataset.source; if (source) this.toggleSource(source); } if (target.closest(".history-item")) { const query = target.closest(".history-item")?.dataset.query; if (query) this.searchFor(query); } if (target.closest(".watchlist-del-btn")) { const ticker = target.closest(".watchlist-item-row")?.dataset.ticker; if (ticker) this.removeTicker(ticker); } }); // Watchlist document .getElementById("watchlist-add-btn") ?.addEventListener("pointerdown", () => this.addTicker()); document.getElementById("watchlist-input")?.addEventListener("keydown", (e) => { if (e.key === "Enter") this.addTicker(); }); // Global shortcuts document.addEventListener("keydown", (e) => { if (e.key === "/" && e.target !== this.searchInput) { e.preventDefault(); this.searchInput.focus(); } if (e.key === "Escape") this.goHome(); }); } // ─── Calculator ──────────────────────────────── initCalculator() { const buttons = [ "C", "(", ")", "÷", "7", "8", "9", "×", "4", "5", "6", "−", "1", "2", "3", "+", "0", ".", "^", "=", ]; const grid = document.getElementById("calc-grid"); if (!grid) return; grid.innerHTML = buttons .map((value) => { const isOp = ["÷", "×", "−", "+", "="].includes(value); return ``; }) .join(""); grid.addEventListener("pointerdown", (e) => { const value = e.target.dataset.v; if (!value) return; if (value === "C") { this.state.calcExpr = ""; this.state.calcResult = "0"; } else if (value === "=") { this.state.calcResult = Utils.safeEval(this.state.calcExpr); } else { this.state.calcExpr += value; } document.getElementById("calc-expr").textContent = this.state.calcExpr; document.getElementById("calc-result").textContent = this.state.calcResult; }); } // ─── Search ───────────────────────────────────── async search(query) { const trimmed = query.trim(); if (!trimmed) return this.goHome(); this.fetcher.abort(); this.state.query = trimmed; this.updateUrlWithQuery(trimmed); this.ensureResultsView(); this.addHistory(trimmed); this.showLoading(); // Special queries const stockMatch = trimmed.match(/^\$([A-Za-z]{1,5})$/); if (stockMatch) return this.showStockWidget(stockMatch[1].toUpperCase()); if (trimmed.startsWith("=")) return this.showCalcResult(trimmed.slice(1)); // Bang handling const bangMatch = trimmed.match(/^!(\w+)\s*(.*)/); if (bangMatch) { const bang = `!${bangMatch[1]}`; const source = Object.entries(this.sources).find(([, s]) => s.bang === bang); if (source) { Object.keys(this.sources).forEach((k) => { this.sources[k].enabled = k === source[0]; }); this.renderSources(); } } const startTime = performance.now(); const enabledSources = Object.entries(this.sources).filter(([, s]) => s.enabled); // Fetch instant results this.fetchInstant(trimmed); try { const results = await Promise.all( enabledSources.map(async ([key, source]) => { try { const data = await source.fetch( encodeURIComponent(trimmed), this.fetcher.abortController?.signal, ); this.updateSourceCount(key, data?.length ?? 0); return data ?? []; } catch (error) { if (error.name !== "AbortError") console.error(`Source ${key} failed:`, error); return []; } }), ); this.state.results = results.flat(); this.updateStats(startTime, enabledSources.length); this.renderResults(); } catch (error) { if (error.name !== "AbortError") Toast.show("SEARCH FAILED"); } } // ─── UI Updates ──────────────────────────────── ensureResultsView() { if (this.state.hasSearched) return; this.state.hasSearched = true; this.heroEl.classList.add("compact"); this.mainEl.style.display = "grid"; this.statsBar.classList.add("visible"); } updateStats(startTime, sourceCount) { const elapsed = Math.round(performance.now() - startTime); document.getElementById("stat-results").textContent = this.state.results.length; document.getElementById("stat-sources").textContent = sourceCount; document.getElementById("stat-time").textContent = `${elapsed}MS`; document.getElementById("results-label").textContent = `${this.state.results.length} RESULTS`; document.getElementById("results-info").textContent = `${elapsed}MS // ${sourceCount} SOURCES`; } updateSourceCount(key, count) { const countEl = document.getElementById(`c-${key}`); if (countEl) countEl.textContent = count; } // ─── Rendering ──────────────────────────────── renderSources() { const sourcesList = document.getElementById("sources-list"); if (!sourcesList) return; sourcesList.innerHTML = Object.entries(this.sources) .map( ([key, source]) => `
${source.name} 0
`, ) .join(""); } renderHistory() { const list = document.getElementById("history-list"); if (!list) return; list.innerHTML = this.state.history .slice(0, 6) .map( (item) => `
${Utils.esc(item.q)} ${Utils.timeAgo(item.t)}
`, ) .join("") || '
NO HISTORY
'; } renderWatchlist() { const container = document.getElementById("watchlist-container"); if (!container) return; container.innerHTML = this.state.watchlist .map( (ticker) => `
${ticker} ${this.stockExchanges[ticker] ?? "STOCK"} ×
`, ) .join("") || '
EMPTY
'; } renderResults() { if (!this.state.results.length) { this.resultsEl.innerHTML = `
NO RESULTS
TRY DIFFERENT KEYWORDS OR ENABLE MORE SOURCES
`; return; } const fragment = document.createDocumentFragment(); this.state.results.forEach((result, index) => { const div = document.createElement("div"); div.className = "result"; div.innerHTML = `
${String(index + 1).padStart(2, "0")}
${result.source.toUpperCase()}
${Utils.esc(result.title)}
${Utils.esc(result.url)}
${Utils.highlight(Utils.esc(result.snippet), this.state.query)}
${ result.meta ? `
${result.meta.stars !== undefined ? `★ ${Utils.fmt(result.meta.stars)}` : ""} ${result.meta.forks !== undefined ? `⑂ ${Utils.fmt(result.meta.forks)}` : ""} ${result.meta.score !== undefined ? `↑ ${Utils.fmt(result.meta.score)}` : ""} ${result.meta.comments !== undefined ? `◨ ${Utils.fmt(result.meta.comments)}` : ""} ${result.meta.words !== undefined ? `◎ ${Utils.fmt(result.meta.words)}W` : ""} ${result.meta.lang ? `${result.meta.lang.toUpperCase()}` : ""} ${result.meta.version ? `V${result.meta.version}` : ""}
` : "" }
`; div.addEventListener("click", () => window.open(result.url, "_blank")); fragment.appendChild(div); }); this.resultsEl.innerHTML = ""; this.resultsEl.appendChild(fragment); } // ─── Tools ──────────────────────────────────── showToolModal(title, content) { this.ensureResultsView(); this.resultsEl.innerHTML = `
${title}
${content}
`; document.getElementById("results-label").textContent = title; } toolUUID() { const uuid = Utils.generateUUID(); Utils.copy(uuid); Toast.show(`UUID: ${uuid.substring(0, 13)}...`); } toolTIMER() { this.showToolModal( "TIMER", `
00:00:00
`, ); document.getElementById("timer-start").addEventListener("pointerdown", () => this.startTimer()); document.getElementById("timer-stop").addEventListener("pointerdown", () => this.stopTimer()); document.getElementById("timer-reset").addEventListener("pointerdown", () => this.resetTimer()); } toolBASE64() { this.showToolModal( "BASE64", `
OUTPUT
`, ); document.getElementById("b64-encode").addEventListener("pointerdown", () => { document.getElementById("b64-output").textContent = btoa( document.getElementById("b64-input").value, ); }); document.getElementById("b64-decode").addEventListener("pointerdown", () => { try { document.getElementById("b64-output").textContent = atob( document.getElementById("b64-input").value, ); } catch { document.getElementById("b64-output").textContent = "INVALID"; } }); } toolJSON() { this.showToolModal( "JSON", `
OUTPUT
`, ); document.getElementById("json-format").addEventListener("pointerdown", () => { try { const input = document.getElementById("json-input").value; document.getElementById("json-output").textContent = JSON.stringify( JSON.parse(input), null, 2, ); } catch (e) { document.getElementById("json-output").textContent = `INVALID: ${e.message}`; } }); document.getElementById("json-minify").addEventListener("pointerdown", () => { try { const input = document.getElementById("json-input").value; document.getElementById("json-output").textContent = JSON.stringify(JSON.parse(input)); } catch (e) { document.getElementById("json-output").textContent = `INVALID: ${e.message}`; } }); } toolHASH() { this.showToolModal( "SHA-256 HASH", `
OUTPUT
`, ); document.getElementById("hash-generate").addEventListener("pointerdown", async () => { const input = document.getElementById("hash-input").value; const data = new TextEncoder().encode(input); const hash = await crypto.subtle.digest("SHA-256", data); const output = Array.from(new Uint8Array(hash)) .map((b) => b.toString(16).padStart(2, "0")) .join(""); document.getElementById("hash-output").textContent = output; }); } toolCOLOR() { this.showToolModal( "COLOR", `
`, ); const updateColor = () => { const hex = document.getElementById("color-input").value; const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); document.getElementById("color-output").innerHTML = `HEX: ${hex}
RGB: ${r}, ${g}, ${b}
HSL: ${Utils.rgbToHsl(r, g, b)}`; }; document.getElementById("color-input").addEventListener("input", updateColor); updateColor(); } toolPASSWORD() { Utils.copy(Utils.generatePassword()); Toast.show("PASSWORD COPIED"); } toolLOREM() { const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."; Utils.copy(lorem); Toast.show("LOREM IPSUM COPIED"); } // ─── Timer Methods ──────────────────────────── startTimer() { if (this.timerInterval) return; this.timerInterval = setInterval(() => { this.timerSec++; const h = String(Math.floor(this.timerSec / 3600)).padStart(2, "0"); const m = String(Math.floor((this.timerSec % 3600) / 60)).padStart(2, "0"); const s = String(this.timerSec % 60).padStart(2, "0"); const display = document.getElementById("timer-display"); if (display) display.textContent = `${h}:${m}:${s}`; }, 1000); } stopTimer() { clearInterval(this.timerInterval); this.timerInterval = null; } resetTimer() { this.stopTimer(); this.timerSec = 0; const timer = document.getElementById("timer-display"); if (timer !== null) { timer.textContent = "00:00:00"; } } // ─── Helper Methods ─────────────────────────── toggleSource(key) { this.sources[key].enabled = !this.sources[key].enabled; this.renderSources(); if (this.state.query) this.search(this.state.query); } toggleTheme() { document.body.classList.toggle("light-mode"); localStorage.setItem( "theme", document.body.classList.contains("light-mode") ? "light" : "dark", ); } addTicker() { const input = document.getElementById("watchlist-input"); const ticker = input?.value?.toUpperCase().trim(); if (ticker && !this.state.watchlist.includes(ticker)) { this.state.watchlist.push(ticker); localStorage.setItem("sw", JSON.stringify(this.state.watchlist)); this.renderWatchlist(); input.value = ""; } } removeTicker(ticker) { this.state.watchlist = this.state.watchlist.filter((t) => t !== ticker); localStorage.setItem("sw", JSON.stringify(this.state.watchlist)); this.renderWatchlist(); } getSymbol(ticker) { const t = ticker.toUpperCase(); const exchange = this.stockExchanges[t]; if (exchange === "CRYPTO") return `BINANCE:${t}USDT`; if (exchange === "FX") return `FX:${t}`; return exchange ? `${exchange}:${t}` : `NASDAQ:${t}`; } updateUrlWithQuery(query) { const url = new URL(window.location); query?.trim() ? url.searchParams.set("q", query) : url.searchParams.delete("q"); window.history.replaceState({}, "", url); } handleUrlQueryOnLoad() { const query = new URLSearchParams(window.location.search).get("q"); if (query?.trim()) { this.searchInput.value = query; this.search(query); } } goHome() { this.state.hasSearched = false; this.heroEl.classList.remove("compact"); this.mainEl.style.display = "none"; this.statsBar.classList.remove("visible"); this.searchInput.value = ""; this.updateUrlWithQuery(""); this.state.selectedResultIndex = -1; } showLoading() { this.resultsEl.innerHTML = Array(4) .fill( '
FETCHING
', ) .join(""); } showStockWidget(ticker) { const symbol = this.getSymbol(ticker); const widgetId = `tv_${Math.random().toString(36).slice(2, 11)}`; const isLight = document.body.classList.contains("light-mode"); this.resultsEl.innerHTML = `

${ticker}

${symbol}
`; document .getElementById("tv-open") .addEventListener("pointerdown", () => window.open(`https://www.tradingview.com/symbols/${symbol}/`, "_blank"), ); document.getElementById("tv-copy").addEventListener("pointerdown", () => Utils.copy(symbol)); new TradingViewWidget(widgetId, symbol, isLight).init(); } async fetchInstant(query) { // Dictionary if (/^[a-zA-Z]+$/.test(query) && this.sources.dictionary.enabled) { try { const data = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${query}`).then( (r) => (r.ok ? r.json() : []), ); if (data[0]) { const meanings = data[0].meanings ?.slice(0, 3) .map((m) => `[${m.partOfSpeech.toUpperCase()}] ${m.definitions?.[0]?.definition}`) .join("\n\n"); this.showInstant({ content: meanings, source: "DICTIONARY", title: data[0].word.toUpperCase() + (data[0].phonetic ? ` ${data[0].phonetic}` : ""), }); return; } } catch (error) { console.error("Dictionary fetch failed:", error); } } // Wikipedia if (this.sources.wikipedia.enabled && this.sources.wikipedia.instant) { try { const instantResult = await this.sources.wikipedia.instant(query); if (instantResult) this.showInstant(instantResult); } catch (error) { console.error("Wikipedia instant fetch failed:", error); } } } showInstant(data) { const existing = this.resultsEl.querySelector(".instant-box"); existing?.remove(); const instantBox = document.createElement("div"); instantBox.className = "instant-box"; instantBox.innerHTML = `
INSTANT ANSWER
${Utils.esc(data.title)}
${Utils.esc(data.content) .split("\n") .map((p) => `

${p}

`) .join("")}
SOURCE: ${Utils.esc(data.source)} ${data.url ? `VIEW FULL →` : ""}
`; this.resultsEl.prepend(instantBox); } showCalcResult(expr) { const result = Utils.safeEval(expr); this.resultsEl.innerHTML = `
CALCULATOR
${Utils.esc(expr)} = ${result}
`; document.getElementById("results-label").textContent = "CALCULATOR"; } addHistory(query) { this.state.history = this.state.history.filter((h) => h.q !== query); this.state.history.unshift({ q: query, t: Date.now() }); this.state.history = this.state.history.slice(0, 20); localStorage.setItem("sh", JSON.stringify(this.state.history)); this.renderHistory(); } updateClocks() { const now = new Date(); const clockEl = document.getElementById("clock"); if (clockEl) clockEl.textContent = now.toLocaleTimeString("en-GB"); const zones = [ { label: "LOCAL", tz: Intl.DateTimeFormat().resolvedOptions().timeZone }, { label: "UTC", tz: "UTC" }, { label: "NYC", tz: "America/New_York" }, { label: "TOKYO", tz: "Asia/Tokyo" }, ]; const clocksEl = document.getElementById("world-clocks"); if (clocksEl) { clocksEl.innerHTML = zones .map( (zone) => `
${now.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", timeZone: zone.tz, })}
${zone.label}
`, ) .join(""); } } voiceSearch() { if (!("webkitSpeechRecognition" in window)) { Toast.show("VOICE NOT SUPPORTED"); return; } const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); recognition.lang = "en-US"; const voiceBtn = document.getElementById("voice-btn"); voiceBtn.style.background = "#fff"; voiceBtn.style.color = "#000"; recognition.onresult = (e) => { const transcript = e.results[0][0].transcript; this.searchInput.value = transcript; this.search(transcript); }; recognition.onend = () => { voiceBtn.style.background = ""; voiceBtn.style.color = ""; }; recognition.start(); } searchFor(query) { this.searchInput.value = query; this.search(query); } insertBang(bang) { this.searchInput.value = `${bang} `; this.searchInput.focus(); this.updateUrlWithQuery(`${bang} `); } } // ═══════════════════════════════════════════════════════════ // INITIALIZATION // ═══════════════════════════════════════════════════════════ const searchEngine = new Search(SOURCES, STOCK_EXCHANGES);