diff options
| author | Sam Scholten | 2026-06-14 20:00:15 +1000 |
|---|---|---|
| committer | Sam Scholten | 2026-06-14 20:00:15 +1000 |
| commit | decc46c876e7b5552f5f5ecac4ee4f1a64ad1d62 (patch) | |
| tree | 46875e236a062189115c0cd8ed8f1d82980c16b7 /templates | |
| download | abvjt-main.tar.gz abvjt-main.zip | |
Diffstat (limited to 'templates')
| -rw-r--r-- | templates/index.html | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..cccd378 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,211 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Journal Abbreviations Tool</title> + <style> + * { margin: 0; padding: 0; box-sizing: border-box; } + body { + font-family: monospace; + background: #fff; + color: #000; + padding: 20px; + line-height: 1.6; + } + h1 { + font-size: 1.2em; + font-weight: bold; + margin-bottom: 20px; + border-bottom: 1px solid #000; + padding-bottom: 10px; + } + .search-form { + border: 1px solid #000; + padding: 20px; + margin-bottom: 20px; + } + input[type="text"] { + font-family: monospace; + font-size: 1em; + border: 1px solid #000; + padding: 5px; + width: 100%; + } + button { + font-family: monospace; + font-size: 1em; + background: #fff; + color: #000; + border: 1px solid #000; + padding: 5px 15px; + cursor: pointer; + margin-top: 15px; + } + button:hover { + background: #000; + color: #fff; + } + .results { + border: 1px solid #000; + padding: 0; + } + .result { + border-bottom: 1px solid #ccc; + padding: 10px; + } + .result:last-child { + border-bottom: none; + } + .full-name { + font-weight: bold; + margin-bottom: 5px; + } + .abbrev { + color: #666; + font-size: 0.9em; + } + .copy-buttons { + margin-top: 8px; + } + .copy-buttons button { + font-size: 0.85em; + padding: 3px 10px; + margin-right: 10px; + } + .no-results, .error { + padding: 10px; + border: 1px solid #000; + } + .error { + border-color: #f00; + color: #f00; + } + footer { + margin-top: 30px; + border-top: 1px solid #000; + padding-top: 10px; + font-size: 0.9em; + } + </style> +</head> +<body> + <h1>Journal Abbreviations Tool</h1> + <form class="search-form" id="searchForm"> + <input type="text" id="query" name="q" placeholder="search journal name..." autofocus autocomplete="off"> + <br> + <button type="submit">search</button> + </form> + <div id="results"></div> + <footer> + <a href="/api/health">health</a> — + data from <a href="https://wos-help.webofscience.com/WOKRS535R111/help/WOS/A_abrvjt.html">Web of Science</a> + </footer> + <script> + const form = document.getElementById('searchForm'); + const queryInput = document.getElementById('query'); + const resultsDiv = document.getElementById('results'); + + function toSentenceCase(str) { + return str.toLowerCase().replace(/\b\w/g, c => c.toUpperCase()); + } + + function makeResultElement(journal) { + const sent = toSentenceCase(journal.abbreviation); + + const container = document.createElement('div'); + container.className = 'result'; + + const nameDiv = document.createElement('div'); + nameDiv.className = 'full-name'; + nameDiv.textContent = journal.full_name; + container.appendChild(nameDiv); + + const abbrevDiv = document.createElement('div'); + abbrevDiv.className = 'abbrev'; + abbrevDiv.textContent = journal.abbreviation; + container.appendChild(abbrevDiv); + + const buttonsDiv = document.createElement('div'); + buttonsDiv.className = 'copy-buttons'; + + const btnSent = document.createElement('button'); + btnSent.textContent = 'copy sentence case'; + btnSent.addEventListener('click', () => copyText(sent)); + buttonsDiv.appendChild(btnSent); + + const btnCaps = document.createElement('button'); + btnCaps.textContent = 'copy ALL CAPS'; + btnCaps.addEventListener('click', () => copyText(journal.abbreviation)); + buttonsDiv.appendChild(btnCaps); + + container.appendChild(buttonsDiv); + return container; + } + + function renderResults(journals) { + if (journals.length === 0) { + resultsDiv.innerHTML = '<div class="no-results">no results</div>'; + return; + } + + const wrapper = document.createElement('div'); + wrapper.className = 'results'; + for (const j of journals) { + wrapper.appendChild(makeResultElement(j)); + } + resultsDiv.innerHTML = ''; + resultsDiv.appendChild(wrapper); + } + + async function copyText(text) { + try { + await navigator.clipboard.writeText(text); + } catch (e) { + const ta = document.createElement('textarea'); + ta.value = text; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + } + } + + async function doSearch() { + const q = queryInput.value.trim(); + if (!q) { + resultsDiv.innerHTML = ''; + return; + } + if (q.length < 2) { + return; + } + resultsDiv.innerHTML = '<div class="no-results">loading...</div>'; + try { + const resp = await fetch('/api/search?q=' + encodeURIComponent(q)); + if (!resp.ok) throw new Error(resp.status + ' ' + resp.statusText); + const data = await resp.json(); + renderResults(data); + } catch (err) { + const errorDiv = document.createElement('div'); + errorDiv.className = 'error'; + errorDiv.textContent = err.message; + resultsDiv.innerHTML = ''; + resultsDiv.appendChild(errorDiv); + } + } + + let debounceTimer; + queryInput.addEventListener('input', () => { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(doSearch, 200); + }); + + form.addEventListener('submit', (e) => { + e.preventDefault(); + clearTimeout(debounceTimer); + doSearch(); + }); + </script> +</body> +</html> |
