my eye
published
  • 2025-05-31T14:52:24.795556-07:00
updated
  • 2025-06-16T16:31:12.431849-07:00
visibility
  • public
author
  • {'uid': ['/'], 'name': ['Angelo Gladding'], 'nickname': ['angelo'], 'note': ['<a href=/hacking#for-right>Hacker for right</a> <a href=/homesteading>homesteading into the future</a>.'], 'email': ['angelo@ragt.ag'], 'photo': ['XWjA.png']}
url
  • /species
  • /2025/05/31/ug
type
  • entry
category
  • Micropub
name
  • Species

Content

$ req = web.tx.request

$if req.method == "GET":
    $if web.tx.user.session.get("uid"):
        <label><input type=checkbox name=endpoint value=ragt.ag/posts checked> ragt.ag (this site)</label><br>
    $if endpoint := web.tx.user.session.get("endpoint"):
        <label><input type=checkbox name=endpoint value=$endpoint>$web.tx.user.session["uid"][0] your site</label><br>

    <form method=post action=/species>
    <input name=scientific_name type="text" id="species-search" placeholder="Type a species name…" autocomplete="off">
    <ul id="inat-suggestions" style="border:1px solid #ccc; max-height:200px; overflow-y:auto; position:absolute; background:white; z-index:10; display:none;">
    </ul>

    $if mp_resp := mp_get(h="species"):
        <ul>
        $for species in mp_resp.json["items"]:
            <li>
            $if common_names := species.get("common-name"):
                <big>$", ".join(common_names)</big><br>
            <a href=$species["url"][0]>$species["scientific-name"][0]</a>
            </li>
        </ul>
    $if endpoint := web.tx.user.session.get("endpoint"):
        <div><button>Create</button></div>
    </form>
$elif req.method == "POST":
    $ form = web.form("scientific_name")
    $if mp_resp := mp_post(h="species", scientific_name=form.scientific_name):
        <p>$mp_resp.text</p>
        <p>Species posted.</p>
    $else:
        <p>Your website does not support micropub.</p>

<script>
const searchInput = document.getElementById('species-search')
const suggestionBox = document.getElementById('inat-suggestions')

let debounceTimeout = null

searchInput.addEventListener('input', () => {
  clearTimeout(debounceTimeout)
  const query = searchInput.value.trim()
  if (query.length < 3) {
    suggestionBox.style.display = 'none'
    return
  }
  debounceTimeout = setTimeout(() => searchINat(query), 250)
})

async function searchINat(query) {
  const url = `https://api.inaturalist.org/v1/taxa?q=$${encodeURIComponent(query)}&per_page=20`
  const res = await fetch(url)
  const data = await res.json()
  if (!data.results || data.results.length === 0) {
    suggestionBox.style.display = 'none'
    return
  }

  suggestionBox.innerHTML = ''
  data.results
    .filter(taxon => taxon.rank_level <= 20)
    .forEach(taxon => {
    const li = document.createElement('li')
    li.style.padding = '4px 8px'
    li.style.cursor = 'pointer'
    const label = taxon.preferred_common_name
      ? `$${taxon.name} - $${taxon.preferred_common_name}`
      : taxon.name
    li.textContent = label
    li.addEventListener('click', () => {
      searchInput.value = taxon.name
      suggestionBox.style.display = 'none'
    })
    suggestionBox.appendChild(li)
  })
  suggestionBox.style.display = 'block'
}

document.addEventListener('click', e => {
  if (!suggestionBox.contains(e.target) && e.target !== searchInput) {
    suggestionBox.style.display = 'none'
  }
})
</script>