Something went wrong. Try again.
A set of JavaScript filters and components meant to replicate Django's humanize
Something went wrong. Try again.
human-eyes demo.html
26 kB · 697 lines
HTML
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Human Eyes — Demo</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, -apple-system, sans-serif; max-width: 48rem; margin: 0 auto; padding: 2rem 1.5rem 4rem; color: #1a1a1a; line-height: 1.6; } h1 { font-size: 1.8rem; margin-bottom: 0.25rem; } h1 + p { color: #666; margin-bottom: 2rem; } h2 { font-size: 1.3rem; margin: 2.5rem 0 0.75rem; padding-bottom: 0.35rem; border-bottom: 2px solid #e5e5e5; } h3 { font-size: 1rem; margin: 1.25rem 0 0.5rem; color: #444; }
.demo-card { background: #fafafa; border: 1px solid #e5e5e5; border-radius: 8px; padding: 1.25rem; margin: 0.75rem 0; } .demo-card + .demo-card { margin-top: 0.5rem; }
.output { font-size: 1.15rem; font-weight: 500; margin-bottom: 0.5rem; } .output:last-child { margin-bottom: 0; }
code { background: #eee; padding: 0.15em 0.4em; border-radius: 3px; font-size: 0.85em; } .code-line { display: block; font-family: ui-monospace, "SF Mono", monospace; font-size: 0.82rem; color: #555; margin-top: 0.3rem; }
label { display: block; font-weight: 600; margin: 1rem 0 0.35rem; } input[type=range] { width: 100%; accent-color: #333; } input[type=text], input[type=number], textarea, select { font-family: inherit; font-size: 0.95rem; padding: 0.4rem 0.6rem; border: 1px solid #ccc; border-radius: 4px; width: 100%; } textarea { resize: vertical; min-height: 3rem; } .input-row { display: flex; gap: 0.75rem; align-items: end; } .input-row > div { flex: 1; }
.note { font-size: 0.85rem; color: #777; margin-top: 0.35rem; }
nav { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 2rem; } nav a { font-size: 0.82rem; padding: 0.25rem 0.6rem; background: #f0f0f0; border-radius: 4px; color: #333; text-decoration: none; } nav a:hover { background: #e0e0e0; }
.component-section { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px dashed #ddd; } .component-section h3 { margin-top: 0; }
.combos-intro { color: #555; margin-bottom: 1.5rem; } </style></head><body> <h1>Human Eyes</h1> <p>Interactive demos for every filter and web component.</p>
<nav> <a href="#pluralize">pluralize</a> <a href="#timesince">timesince</a> <a href="#filesizeformat">filesizeformat</a> <a href="#truncate">truncate</a> <a href="#yesno">yesno</a> <a href="#floatformat">floatformat</a> <a href="#slugify">slugify</a> <a href="#oxfordcomma">oxfordComma</a> <a href="#ordinal">ordinal</a> <a href="#intword">intword</a> <a href="#naturalday">naturalday</a> <a href="#combos">combos</a> </nav>
<!-- ===== PLURALIZE ===== --> <h2 id="pluralize">pluralize</h2>
<label for="pl-count">Count: <span id="pl-count-display">2</span></label> <input type="range" id="pl-count" min="0" max="20" value="2">
<div class="demo-card"> <div class="output" id="pl-fn-output"></div> <span class="code-line">`${count} file${pluralize(count)}`</span> </div>
<div class="demo-card"> <div class="output" id="pl-fn-es"></div> <span class="code-line">`${count} walrus${pluralize(count, "es")}`</span> </div>
<div class="demo-card"> <div class="output" id="pl-fn-irreg"></div> <span class="code-line">`${count} cherr${pluralize(count, "y,ies")}`</span> </div>
<div class="demo-card"> <div class="output" id="pl-fn-full"></div> <span class="code-line">`${count} ${pluralize(count, "person,people")}`</span> </div>
<div class="component-section"> <h3><code><human-eyes-pluralize></code> web component</h3> <p class="note">Reactive — update the <code>count</code> attribute and the text re-renders. The slider above drives these too.</p>
<div class="demo-card"> <div class="output"><human-eyes-pluralize id="pl-msg" count="2" word="message"></human-eyes-pluralize></div> <span class="code-line"><human-eyes-pluralize count="2" word="message"></span> </div>
<div class="demo-card"> <div class="output"><human-eyes-pluralize id="pl-walrus" count="2" word="walrus" suffix="es"></human-eyes-pluralize></div> <span class="code-line"><human-eyes-pluralize count="2" word="walrus" suffix="es"></span> </div>
<div class="demo-card"> <div class="output"><human-eyes-pluralize id="pl-cherry" count="2" word="cherr" suffix="y,ies"></human-eyes-pluralize></div> <span class="code-line"><human-eyes-pluralize count="2" word="cherr" suffix="y,ies"></span> </div>
<div class="demo-card"> <div class="output"><human-eyes-pluralize id="pl-person" count="2" suffix="person,people"></human-eyes-pluralize></div> <span class="code-line"><human-eyes-pluralize count="2" suffix="person,people"></span> </div>
<div class="demo-card"> <div class="output"><span id="pl-inline-count">2</span> item<human-eyes-pluralize id="pl-inline" count="2"></human-eyes-pluralize> in your cart</div> <span class="code-line">item<human-eyes-pluralize count="2"> (suffix-only, inline)</span> </div> </div>
<!-- ===== TIMESINCE ===== --> <h2 id="timesince">timesince / timeuntil</h2>
<label for="ts-date">Date to measure from</label> <input type="text" id="ts-date" value="2025-01-01" placeholder="YYYY-MM-DD or ISO 8601">
<div class="demo-card"> <div class="output" id="ts-fn-output"></div> <span class="code-line">timesince("2025-01-01")</span> </div>
<div class="component-section"> <h3><code><human-eyes-timesince></code> web component</h3> <p class="note">Live-updating — refreshes every 60 seconds by default.</p>
<div class="demo-card"> <div class="output">Page loaded <human-eyes-timesince id="ts-since" datetime="" live="true"></human-eyes-timesince> ago</div> <span class="code-line"><human-eyes-timesince datetime="..." live="true"></span> </div>
<div class="demo-card"> <div class="output">New Year's Day is <human-eyes-timesince id="ts-until" datetime="2027-01-01" mode="until" live="false"></human-eyes-timesince> away</div> <span class="code-line"><human-eyes-timesince datetime="2027-01-01" mode="until"></span> </div> </div>
<!-- ===== FILESIZEFORMAT ===== --> <h2 id="filesizeformat">filesizeformat</h2>
<label for="fs-bytes">Bytes: <span id="fs-bytes-display">123456789</span></label> <input type="range" id="fs-bytes" min="0" max="5000000000" value="123456789" step="1024">
<div class="demo-card"> <div class="output" id="fs-fn-output"></div> <span class="code-line" id="fs-fn-code">filesizeformat(123456789)</span> </div>
<div class="component-section"> <h3><code><human-eyes-filesizeformat></code> web component</h3> <p class="note">Reactive — the slider above drives this too.</p>
<div class="demo-card"> <div class="output"><human-eyes-filesizeformat id="fs-component" bytes="123456789"></human-eyes-filesizeformat></div> <span class="code-line"><human-eyes-filesizeformat bytes="123456789"></span> </div> </div>
<!-- ===== TRUNCATE ===== --> <h2 id="truncate">truncatechars / truncatewords</h2>
<label for="tr-input">Text</label> <input type="text" id="tr-input" value="Joel is a slug">
<div class="input-row"> <div> <label for="tr-chars">Max chars</label> <input type="number" id="tr-chars" value="7" min="1" max="200"> </div> <div> <label for="tr-words">Max words</label> <input type="number" id="tr-words" value="2" min="1" max="50"> </div> </div>
<div class="demo-card"> <div class="output" id="tr-chars-output"></div> <span class="code-line" id="tr-chars-code"></span> </div>
<div class="demo-card"> <div class="output" id="tr-words-output"></div> <span class="code-line" id="tr-words-code"></span> </div>
<!-- ===== YESNO ===== --> <h2 id="yesno">yesno</h2>
<div class="input-row"> <div> <label for="yn-value">Value</label> <select id="yn-value"> <option value="true">true</option> <option value="false">false</option> <option value="null">null</option> </select> </div> <div> <label for="yn-mapping">Mapping</label> <input type="text" id="yn-mapping" value="yeah,no,maybe"> </div> </div>
<div class="demo-card"> <div class="output" id="yn-output"></div> <span class="code-line" id="yn-code"></span> </div>
<!-- ===== FLOATFORMAT ===== --> <h2 id="floatformat">floatformat</h2>
<div class="input-row"> <div> <label for="ff-value">Number</label> <input type="text" id="ff-value" value="34232.23234"> </div> <div> <label for="ff-arg">Arg</label> <input type="text" id="ff-arg" value="2g" placeholder="-1, 3, 2g, etc."> </div> </div>
<div class="demo-card"> <div class="output" id="ff-output"></div> <span class="code-line" id="ff-code"></span> </div>
<div class="note">Positive = always show N decimals. Negative = up to N, strip trailing zeros. Append "g" for grouping.</div>
<!-- ===== SLUGIFY ===== --> <h2 id="slugify">slugify</h2>
<label for="sl-input">Text</label> <input type="text" id="sl-input" value="Héllo Wörld!">
<div class="demo-card"> <div class="output" id="sl-output"></div> <span class="code-line" id="sl-code"></span> </div>
<!-- ===== OXFORD COMMA ===== --> <h2 id="oxfordcomma">oxfordComma</h2>
<label for="ox-items">Items (comma-separated)</label> <input type="text" id="ox-items" value="apple, banana, cherry">
<div class="input-row"> <div> <label for="ox-limit">Limit (optional)</label> <input type="number" id="ox-limit" value="" min="1" placeholder="none"> </div> </div>
<div class="demo-card"> <div class="output" id="ox-output"></div> <span class="code-line" id="ox-code"></span> </div>
<!-- ===== ORDINAL ===== --> <h2 id="ordinal">ordinal</h2>
<label for="ord-value">Number: <span id="ord-display">1</span></label> <input type="range" id="ord-value" min="0" max="125" value="1">
<div class="demo-card"> <div class="output" id="ord-output"></div> <span class="code-line" id="ord-code"></span> </div>
<!-- ===== INTWORD ===== --> <h2 id="intword">intword</h2>
<label for="iw-value">Number</label> <input type="text" id="iw-value" value="1200000">
<div class="demo-card"> <div class="output" id="iw-output"></div> <span class="code-line" id="iw-code"></span> </div>
<div class="note">Numbers below 1 million get comma formatting. Above 1 million: million, billion, trillion, quadrillion, quintillion.</div>
<!-- ===== NATURALDAY ===== --> <h2 id="naturalday">naturalday</h2>
<div class="demo-card"> <div class="output" id="nd-today"></div> <span class="code-line">naturalday(today)</span> </div> <div class="demo-card"> <div class="output" id="nd-yesterday"></div> <span class="code-line">naturalday(yesterday)</span> </div> <div class="demo-card"> <div class="output" id="nd-tomorrow"></div> <span class="code-line">naturalday(tomorrow)</span> </div> <div class="demo-card"> <div class="output" id="nd-other"></div> <span class="code-line">naturalday("2024-06-10")</span> </div>
<label for="nd-custom">Try a date</label> <input type="text" id="nd-custom" value="" placeholder="YYYY-MM-DD">
<div class="demo-card"> <div class="output" id="nd-custom-output"></div> <span class="code-line" id="nd-custom-code"></span> </div>
<!-- ===== COMBOS ===== --> <h2 id="combos">Combining filters</h2> <p class="combos-intro">These filters are simple functions — they compose naturally. Here are a few patterns that work well together.</p>
<!-- pluralize + oxfordComma --> <h3>pluralize + oxfordComma</h3> <p class="note" style="margin-bottom: 0.5rem;">Build a sentence that drops zero-count items and auto-punctuates whatever's left.</p>
<div class="input-row"> <div> <label for="po-comments">Comments</label> <input type="number" id="po-comments" value="3" min="0"> </div> <div> <label for="po-reposts">Reposts</label> <input type="number" id="po-reposts" value="0" min="0"> </div> <div> <label for="po-quotes">Quotes</label> <input type="number" id="po-quotes" value="1" min="0"> </div> </div>
<div class="demo-card" style="margin-top: 0.75rem;"> <div class="output" id="po-output"></div> <span class="code-line" id="po-code"></span> </div>
<!-- intword + pluralize --> <h3>intword + pluralize</h3> <p class="note" style="margin-bottom: 0.5rem;">Humanize a big number and pluralize the label in one go.</p>
<label for="combo-dl">Downloads</label> <input type="text" id="combo-dl" value="1200000">
<div class="demo-card"> <div class="output" id="combo-dl-output"></div> <span class="code-line" id="combo-dl-code"></span> </div>
<!-- naturalday + timesince --> <h3>naturalday + timesince</h3> <p class="note" style="margin-bottom: 0.5rem;">Show a friendly date label with a relative duration in parentheses.</p>
<label for="combo-date">Date</label> <input type="text" id="combo-date" value="" placeholder="YYYY-MM-DD">
<div class="demo-card"> <div class="output" id="combo-date-output"></div> <span class="code-line" id="combo-date-code"></span> </div>
<!-- ordinal + naturalday --> <h3>ordinal + naturalday</h3> <p class="note" style="margin-bottom: 0.5rem;">Turn a day-of-month number into a sentence.</p>
<label for="combo-day">Day of month: <span id="combo-day-display">15</span></label> <input type="range" id="combo-day" min="1" max="31" value="15">
<div class="demo-card"> <div class="output" id="combo-ord-output"></div> <span class="code-line" id="combo-ord-code"></span> </div>
<!-- ===== SCRIPTS ===== --> <script type="module"> // Web components import "./human-eyes-pluralize.js"; import "./human-eyes-timesince.js"; import "./human-eyes-filesizeformat.js";
// Functions import { pluralize } from "./pluralize.js"; import { timesince } from "./timesince.js"; import { filesizeformat } from "./filesizeformat.js"; import { truncatechars, truncatewords } from "./truncate.js"; import { yesno } from "./yesno.js"; import { floatformat } from "./floatformat.js"; import { slugify } from "./slugify.js"; import { oxfordComma } from "./oxford.js"; import { ordinal } from "./ordinal.js"; import { intword } from "./intword.js"; import { naturalday } from "./naturalday.js";
const $ = (id) => document.getElementById(id);
// ── Pluralize ────────────────────────────────────────────── const plSlider = $("pl-count"); const plComponents = ["pl-msg", "pl-walrus", "pl-cherry", "pl-person", "pl-inline"];
function updatePluralize() { const n = Number(plSlider.value); $("pl-count-display").textContent = n; $("pl-inline-count").textContent = n; plComponents.forEach((id) => $(id).setAttribute("count", n));
$("pl-fn-output").textContent = `${n} file${pluralize(n)}`; $("pl-fn-es").textContent = `${n} walrus${pluralize(n, "es")}`; $("pl-fn-irreg").textContent = `${n} cherr${pluralize(n, "y,ies")}`; $("pl-fn-full").textContent = `${n} ${pluralize(n, "person,people")}`; } plSlider.addEventListener("input", updatePluralize); updatePluralize();
// ── Timesince ────────────────────────────────────────────── $("ts-since").setAttribute("datetime", new Date().toISOString());
function updateTimesince() { const val = $("ts-date").value; $("ts-fn-output").textContent = timesince(val); } $("ts-date").addEventListener("input", updateTimesince); updateTimesince();
// ── Filesizeformat ───────────────────────────────────────── const fsSlider = $("fs-bytes"); function updateFilesize() { const bytes = Number(fsSlider.value); $("fs-bytes-display").textContent = bytes.toLocaleString(); $("fs-component").setAttribute("bytes", bytes); $("fs-fn-output").textContent = filesizeformat(bytes); $("fs-fn-code").textContent = `filesizeformat(${bytes})`; } fsSlider.addEventListener("input", updateFilesize); updateFilesize();
// ── Truncate ─────────────────────────────────────────────── function updateTruncate() { const text = $("tr-input").value; const chars = Number($("tr-chars").value); const words = Number($("tr-words").value);
$("tr-chars-output").textContent = truncatechars(text, chars); $("tr-chars-code").textContent = `truncatechars("${text}", ${chars})`;
$("tr-words-output").textContent = truncatewords(text, words); $("tr-words-code").textContent = `truncatewords("${text}", ${words})`; } $("tr-input").addEventListener("input", updateTruncate); $("tr-chars").addEventListener("input", updateTruncate); $("tr-words").addEventListener("input", updateTruncate); updateTruncate();
// ── Yesno ────────────────────────────────────────────────── function updateYesno() { const raw = $("yn-value").value; const mapping = $("yn-mapping").value; const value = raw === "null" ? null : raw === "true";
$("yn-output").textContent = yesno(value, mapping); $("yn-code").textContent = `yesno(${raw}, "${mapping}")`; } $("yn-value").addEventListener("change", updateYesno); $("yn-mapping").addEventListener("input", updateYesno); updateYesno();
// ── Floatformat ──────────────────────────────────────────── function updateFloatformat() { const num = $("ff-value").value; const arg = $("ff-arg").value;
const parsedArg = arg.endsWith("g") ? arg : Number(arg); $("ff-output").textContent = floatformat(Number(num), parsedArg || undefined); $("ff-code").textContent = `floatformat(${num}${arg ? `, ${JSON.stringify(parsedArg)}` : ""})`; } $("ff-value").addEventListener("input", updateFloatformat); $("ff-arg").addEventListener("input", updateFloatformat); updateFloatformat();
// ── Slugify ──────────────────────────────────────────────── function updateSlugify() { const text = $("sl-input").value; $("sl-output").textContent = slugify(text); $("sl-code").textContent = `slugify("${text}")`; } $("sl-input").addEventListener("input", updateSlugify); updateSlugify();
// ── Oxford Comma ─────────────────────────────────────────── function updateOxford() { const items = $("ox-items").value.split(",").map(s => s.trim()).filter(Boolean); const limitVal = $("ox-limit").value; const limit = limitVal ? Number(limitVal) : undefined;
$("ox-output").textContent = oxfordComma(items, limit); $("ox-code").textContent = `oxfordComma(${JSON.stringify(items)}${limit != null ? `, ${limit}` : ""})`; } $("ox-items").addEventListener("input", updateOxford); $("ox-limit").addEventListener("input", updateOxford); updateOxford();
// ── Ordinal ──────────────────────────────────────────────── const ordSlider = $("ord-value"); function updateOrdinal() { const n = Number(ordSlider.value); $("ord-display").textContent = n; $("ord-output").textContent = ordinal(n); $("ord-code").textContent = `ordinal(${n})`; } ordSlider.addEventListener("input", updateOrdinal); updateOrdinal();
// ── Intword ──────────────────────────────────────────────── function updateIntword() { const val = $("iw-value").value; $("iw-output").textContent = intword(Number(val)); $("iw-code").textContent = `intword(${val})`; } $("iw-value").addEventListener("input", updateIntword); updateIntword();
// ── Naturalday ───────────────────────────────────────────── const now = new Date(); const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1); const tomorrow = new Date(now); tomorrow.setDate(now.getDate() + 1);
$("nd-today").textContent = naturalday(now); $("nd-yesterday").textContent = naturalday(yesterday); $("nd-tomorrow").textContent = naturalday(tomorrow); $("nd-other").textContent = naturalday("2024-06-10");
function updateNaturalday() { const val = $("nd-custom").value; if (!val) { $("nd-custom-output").textContent = ""; $("nd-custom-code").textContent = ""; return; } $("nd-custom-output").textContent = naturalday(val); $("nd-custom-code").textContent = `naturalday("${val}")`; } $("nd-custom").addEventListener("input", updateNaturalday);
// ── Combo: pluralize + oxfordComma ───────────────────────── function updatePluralizeOxford() { const comments = Number($("po-comments").value) || 0; const reposts = Number($("po-reposts").value) || 0; const quotes = Number($("po-quotes").value) || 0;
const parts = [ comments && `${comments} comment${pluralize(comments)}`, reposts && `${reposts} repost${pluralize(reposts)}`, quotes && `${quotes} quote${pluralize(quotes)}`, ].filter(Boolean);
const sentence = parts.length ? `This post has ${oxfordComma(parts)}` : "";
$("po-output").textContent = sentence || "(nothing to show)"; $("po-output").style.opacity = sentence ? 1 : 0.4; $("po-code").textContent = parts.length ? `oxfordComma([${parts.map(p => `"${p}"`).join(", ")}])` : "(all counts are zero)"; } $("po-comments").addEventListener("input", updatePluralizeOxford); $("po-reposts").addEventListener("input", updatePluralizeOxford); $("po-quotes").addEventListener("input", updatePluralizeOxford); updatePluralizeOxford();
// ── Combo: intword + pluralize ───────────────────────────── function updateComboDl() { const n = Number($("combo-dl").value); const word = intword(n); const label = `download${pluralize(n)}`; $("combo-dl-output").textContent = `${word} ${label}`; $("combo-dl-code").textContent = `\`\${intword(${n})} download\${pluralize(${n})}\``; } $("combo-dl").addEventListener("input", updateComboDl); updateComboDl();
// ── Combo: naturalday + timesince ────────────────────────── // Default to yesterday's date const comboDateDefault = new Date(now); comboDateDefault.setDate(now.getDate() - 1); const pad = (n) => String(n).padStart(2, "0"); const defaultDateStr = `${comboDateDefault.getFullYear()}-${pad(comboDateDefault.getMonth() + 1)}-${pad(comboDateDefault.getDate())}`; $("combo-date").value = defaultDateStr;
function updateComboDate() { const val = $("combo-date").value; if (!val) { $("combo-date-output").textContent = ""; $("combo-date-code").textContent = ""; return; } const day = naturalday(val); const ago = timesince(val); $("combo-date-output").textContent = `${day} (${ago} ago)`; $("combo-date-code").textContent = `\`\${naturalday("${val}")} (\${timesince("${val}")} ago)\``; } updateComboDate(); $("combo-date").addEventListener("input", updateComboDate);
// ── Combo: ordinal + naturalday ──────────────────────────── const comboOrdSlider = $("combo-day"); function updateComboOrd() { const day = Number(comboOrdSlider.value); $("combo-day-display").textContent = day;
// Build a date for "the Nth of this month" const target = new Date(now.getFullYear(), now.getMonth(), day); const dateStr = `${target.getFullYear()}-${pad(target.getMonth() + 1)}-${pad(target.getDate())}`; const nd = naturalday(dateStr); const ord = ordinal(day);
$("combo-ord-output").textContent = `The ${ord} is ${nd}`; $("combo-ord-code").textContent = `\`The \${ordinal(${day})} is \${naturalday("${dateStr}")}\``; } comboOrdSlider.addEventListener("input", updateComboOrd); updateComboOrd(); </script></body></html>