Spaces:
Running
Running
Download script.js from pumpfun/Robinhood-Web: direct link, hf CLI and curl.
- Browser
- Download file 7.89 kB
-
https://huggingface.co/spaces/pumpfun/Robinhood-Web/resolve/main/script.js
- Command line
-
hf download hf://spaces/pumpfun/Robinhood-Web/script.js
-
curl -L -o script.js https://huggingface.co/spaces/pumpfun/Robinhood-Web/resolve/main/script.js
7.89 kB
| const languageButtons = document.querySelectorAll('[data-lang]'); | |
| const translatedNodes = document.querySelectorAll('[data-en][data-zh]'); | |
| const translatedInputs = document.querySelectorAll('[data-en-placeholder][data-zh-placeholder]'); | |
| const form = document.querySelector('#research-form'); | |
| const queryInput = document.querySelector('#company-query'); | |
| const research = document.querySelector('#research'); | |
| const emptyState = document.querySelector('#research-empty'); | |
| const loadingState = document.querySelector('#research-loading'); | |
| const errorState = document.querySelector('#research-error'); | |
| const resultState = document.querySelector('#research-result'); | |
| let currentLanguage = 'en'; | |
| let currentResult = null; | |
| const apiOrigin = window.location.hostname.endsWith('.hf.space') ? 'https://robinhood-web-pi.vercel.app' : ''; | |
| const copy = { | |
| en: { | |
| sector: 'Classification', fiscal: 'Fiscal year end', state: 'Incorporated', | |
| facts: 'Latest reported facts', filings: 'Recent material filings', period: 'Period', filed: 'Filed', | |
| source: 'Evidence source', limitations: 'Read before using', live: 'LIVE SEC RECORD', | |
| noFacts: 'No matching standardized XBRL facts were found for this issuer.', | |
| noFilings: 'No recent supported filing was found.', | |
| error: 'The public record could not be opened. Try another U.S. ticker or try again shortly.', | |
| }, | |
| zh: { | |
| sector: '行业分类', fiscal: '财年截止', state: '注册地', | |
| facts: '最近披露指标', filings: '近期重要申报', period: '数据期', filed: '提交日', | |
| source: '证据来源', limitations: '使用前请阅读', live: 'SEC 实时公开记录', | |
| noFacts: '未找到该公司的匹配标准化 XBRL 指标。', | |
| noFilings: '未找到近期受支持的申报。', | |
| error: '暂时无法打开公开记录。请尝试其他美股代码,或稍后重试。', | |
| }, | |
| }; | |
| const element = (tag, className, text) => { | |
| const node = document.createElement(tag); | |
| if (className) node.className = className; | |
| if (text !== undefined) node.textContent = text; | |
| return node; | |
| }; | |
| const formatMoney = (value) => new Intl.NumberFormat(currentLanguage === 'zh' ? 'zh-CN' : 'en-US', { | |
| style: 'currency', currency: 'USD', notation: 'compact', maximumFractionDigits: 2, | |
| }).format(value); | |
| const renderResult = (data) => { | |
| const t = copy[currentLanguage]; | |
| const company = data.company; | |
| resultState.replaceChildren(); | |
| const header = element('header', 'result-header'); | |
| const headingGroup = element('div'); | |
| headingGroup.append(element('p', 'section-label', t.live), element('h2', '', company.name)); | |
| const identity = element('div', 'company-identity'); | |
| identity.append(element('strong', '', company.ticker), element('span', '', company.exchange || '—'), element('span', '', `CIK ${company.cik}`)); | |
| header.append(headingGroup, identity); | |
| const profile = element('div', 'company-profile'); | |
| [ | |
| [t.sector, company.sic_description || '—'], | |
| [t.fiscal, company.fiscal_year_end || '—'], | |
| [t.state, company.state || '—'], | |
| ].forEach(([label, value]) => { | |
| const item = element('div'); | |
| item.append(element('span', '', label), element('strong', '', value)); | |
| profile.append(item); | |
| }); | |
| const factsSection = element('section', 'result-block'); | |
| factsSection.append(element('h3', '', t.facts)); | |
| if (data.metrics.length) { | |
| const grid = element('div', 'metric-grid'); | |
| data.metrics.forEach((metric) => { | |
| const card = element('article', 'metric-card'); | |
| const period = metric.period_start ? `${metric.period_start}—${metric.period}` : (metric.period || '—'); | |
| card.append( | |
| element('span', '', currentLanguage === 'zh' ? metric.label_zh : metric.label_en), | |
| element('strong', '', formatMoney(metric.value)), | |
| element('small', '', `${metric.form} · ${t.period} ${period} · ${t.filed} ${metric.filed}`), | |
| ); | |
| grid.append(card); | |
| }); | |
| factsSection.append(grid); | |
| } else { | |
| factsSection.append(element('p', 'empty-copy', t.noFacts)); | |
| } | |
| const filingsSection = element('section', 'result-block filings-block'); | |
| filingsSection.append(element('h3', '', t.filings)); | |
| if (data.filings.length) { | |
| const list = element('div', 'filing-list'); | |
| data.filings.forEach((filing) => { | |
| const link = element('a', 'filing-row'); | |
| link.href = filing.url; | |
| link.target = '_blank'; | |
| link.rel = 'noopener'; | |
| link.append( | |
| element('strong', '', filing.form), | |
| element('span', '', filing.description), | |
| element('time', '', filing.filed), | |
| element('b', '', '↗'), | |
| ); | |
| list.append(link); | |
| }); | |
| filingsSection.append(list); | |
| } else { | |
| filingsSection.append(element('p', 'empty-copy', t.noFilings)); | |
| } | |
| const evidence = element('aside', 'evidence-note'); | |
| const evidenceLink = element('a', '', data.source); | |
| evidenceLink.href = 'https://www.sec.gov/search-filings/edgar-application-programming-interfaces'; | |
| evidenceLink.target = '_blank'; | |
| evidenceLink.rel = 'noopener'; | |
| const retrieved = new Date(data.retrieved_at).toLocaleString(currentLanguage === 'zh' ? 'zh-CN' : 'en-US'); | |
| const sourceLine = element('p'); | |
| sourceLine.append(element('span', '', `${t.source}: `), evidenceLink, document.createTextNode(` · ${retrieved}`)); | |
| evidence.append(sourceLine, element('h3', '', t.limitations), element('p', '', data.limitations[currentLanguage])); | |
| resultState.append(header, profile, factsSection, filingsSection, evidence); | |
| }; | |
| const setLanguage = (language) => { | |
| currentLanguage = language; | |
| document.documentElement.lang = language === 'zh' ? 'zh-CN' : 'en'; | |
| translatedNodes.forEach((node) => { node.textContent = node.dataset[language]; }); | |
| translatedInputs.forEach((node) => { node.placeholder = node.dataset[`${language}Placeholder`]; }); | |
| languageButtons.forEach((button) => { | |
| const active = button.dataset.lang === language; | |
| button.classList.toggle('active', active); | |
| button.setAttribute('aria-pressed', String(active)); | |
| }); | |
| if (currentResult) renderResult(currentResult); | |
| }; | |
| const runResearch = async (query) => { | |
| research.setAttribute('aria-busy', 'true'); | |
| emptyState.hidden = true; | |
| errorState.hidden = true; | |
| resultState.hidden = true; | |
| loadingState.hidden = false; | |
| try { | |
| const response = await fetch(`${apiOrigin}/api/research?q=${encodeURIComponent(query)}`); | |
| if (!response.ok) throw new Error(String(response.status)); | |
| currentResult = await response.json(); | |
| renderResult(currentResult); | |
| resultState.hidden = false; | |
| } catch (error) { | |
| currentResult = null; | |
| errorState.textContent = copy[currentLanguage].error; | |
| errorState.hidden = false; | |
| } finally { | |
| loadingState.hidden = true; | |
| research.setAttribute('aria-busy', 'false'); | |
| research.scrollIntoView({ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'start' }); | |
| } | |
| }; | |
| languageButtons.forEach((button) => button.addEventListener('click', () => setLanguage(button.dataset.lang))); | |
| document.querySelectorAll('[data-query]').forEach((button) => button.addEventListener('click', () => { | |
| queryInput.value = button.dataset.query; | |
| runResearch(button.dataset.query); | |
| })); | |
| form.addEventListener('submit', (event) => { | |
| event.preventDefault(); | |
| const query = queryInput.value.trim(); | |
| if (query) runResearch(query); | |
| }); | |
| if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) { | |
| document.documentElement.classList.add('motion-ready'); | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach((entry) => { | |
| if (!entry.isIntersecting) return; | |
| entry.target.classList.add('visible'); | |
| observer.unobserve(entry.target); | |
| }); | |
| }, { rootMargin: '0px 0px -10% 0px', threshold: 0.12 }); | |
| document.querySelectorAll('.reveal').forEach((node) => observer.observe(node)); | |
| } | |