{"id":839,"date":"2025-10-07T21:33:17","date_gmt":"2025-10-07T21:33:17","guid":{"rendered":"https:\/\/nisc.unime.it\/?page_id=839"},"modified":"2026-05-14T09:41:04","modified_gmt":"2026-05-14T09:41:04","slug":"publications","status":"publish","type":"page","link":"https:\/\/nisc.unime.it\/?page_id=839","title":{"rendered":"Publications"},"content":{"rendered":"\n<!-- ===== Recent Publications (OpenAlex, Frontend-Only, Filter + Pagination) ===== -->\n<div class=\"wp-block-group alignfull\" style=\"padding:5vw;background:#ffffff;\">\n  <div style=\"max-width:1100px;margin:0 auto;\">\n    <h2 class=\"wp-block-heading has-text-align-center\" style=\"margin:0 0 1rem;color:#111;\">\n      recent publications\n    <\/h2>\n    <p style=\"color:#555;max-width:720px;margin:0 auto 2.5rem;text-align:center;\">\n      selected publications from <strong>nisc laboratory<\/strong> professors,\n      advancing cognitive science, ai, and human\u2013machine interaction.\n    <\/p>\n\n    <div class=\"pub-controls\" aria-label=\"publication filters\">\n      <div class=\"pub-search-wrap\">\n        <input\n          id=\"pub-search\"\n          class=\"pub-search\"\n          type=\"search\"\n          placeholder=\"search title, author, journal...\"\n          aria-label=\"search publications\"\n        \/>\n      <\/div>\n\n      <div class=\"pub-filter-wrap\">\n        <label for=\"pub-year\" class=\"sr-only\">filter by year<\/label>\n        <select id=\"pub-year\" class=\"pub-select\" aria-label=\"filter by year\">\n          <option value=\"all\">All years<\/option>\n        <\/select>\n      <\/div>\n    <\/div>\n\n    <ul id=\"nisc-publications\" class=\"nisc-pubs\" aria-live=\"polite\">\n      <li class=\"empty\">loading publications\u2026<\/li>\n    <\/ul>\n\n    <div id=\"pub-pagination\" class=\"pub-pagination\" hidden>\n      <button id=\"pub-prev\" class=\"pub-page-btn\" type=\"button\">previous<\/button>\n      <span id=\"pub-page-info\" class=\"pub-page-info\">page 1 of 1<\/span>\n      <button id=\"pub-next\" class=\"pub-page-btn\" type=\"button\">next<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\nconst authors = [\n  { name: \"prof. giorgio mario grasso\", orcid: \"0000-0002-8957-5307\" },\n  { name: \"prof. alessio plebe\", orcid: \"0000-0003-3666-061x\" },\n  { name: \"prof. pietro perconti\", orcid: \"0000-0002-3633-098x\" }\n];\n\nconst MAILTO = \"noreply@example.com\"; \/\/ replace with real email\nconst ITEMS_PER_PAGE = 8;\nconst FETCH_PER_AUTHOR = 50;\nconst MIN_YEAR = 2018;\n\nlet allPublications = [];\nlet filteredPublications = [];\nlet currentPage = 1;\n\nfunction escapeHtml(str = \"\") {\n  return String(str).replace(\/[&<>\"']\/g, m => ({\n    \"&\": \"&amp;\",\n    \"<\": \"&lt;\",\n    \">\": \"&gt;\",\n    '\"': \"&quot;\",\n    \"'\": \"&#39;\"\n  }[m]));\n}\n\nfunction normalizeDoi(doi = \"\") {\n  return String(doi).replace(\/^https?:\\\/\\\/(dx\\.)?doi\\.org\\\/\/i, \"\").trim().toLowerCase();\n}\n\nfunction bestUrl(work) {\n  return (\n    work.primary_location?.landing_page_url ||\n    work.primary_location?.source?.homepage_url ||\n    work.ids?.doi ||\n    (work.doi ? `https:\/\/doi.org\/${normalizeDoi(work.doi)}` : \"\") ||\n    \"#\"\n  );\n}\n\nfunction journalName(work) {\n  return (\n    work.primary_location?.source?.display_name ||\n    work.host_venue?.display_name ||\n    \"\"\n  );\n}\n\nfunction authorNames(work) {\n  const names = (work.authorships || [])\n    .map(a => a?.author?.display_name?.trim())\n    .filter(Boolean);\n\n  if (!names.length) return \"\";\n\n  if (names.length <= 6) return names.join(\", \");\n  return `${names.slice(0, 6).join(\", \")} et al.`;\n}\n\nasync function fetchOpenAlexWorks(orcid) {\n  try {\n    const url = new URL(\"https:\/\/api.openalex.org\/works\");\n    url.searchParams.set(\n      \"filter\",\n      `authorships.author.orcid:https:\/\/orcid.org\/${orcid.toUpperCase()}`\n    );\n    url.searchParams.set(\"sort\", \"publication_date:desc\");\n    url.searchParams.set(\"per-page\", String(FETCH_PER_AUTHOR));\n    url.searchParams.set(\n      \"select\",\n      [\n        \"id\",\n        \"doi\",\n        \"ids\",\n        \"display_name\",\n        \"publication_year\",\n        \"publication_date\",\n        \"type\",\n        \"primary_location\",\n        \"authorships\"\n      ].join(\",\")\n    );\n    url.searchParams.set(\"mailto\", MAILTO);\n\n    const res = await fetch(url.toString(), {\n      headers: { \"accept\": \"application\/json\" }\n    });\n\n    if (!res.ok) throw new Error(`http ${res.status}`);\n\n    const data = await res.json();\n    const results = Array.isArray(data.results) ? data.results : [];\n\n    return results.map(work => ({\n      id: work.id || \"\",\n      doi: normalizeDoi(work.doi || work.ids?.doi || \"\"),\n      title: work.display_name || \"untitled\",\n      year: work.publication_year || null,\n      date: work.publication_date || \"\",\n      journal: journalName(work),\n      authors: authorNames(work),\n      type: work.type || \"\",\n      url: bestUrl(work)\n    }));\n  } catch (err) {\n    console.warn(\"openalex fetch failed:\", orcid, err);\n    return [];\n  }\n}\n\nfunction dedupeWorks(items) {\n  const byKey = new Map();\n\n  for (const item of items) {\n    const key =\n      item.doi ||\n      item.id ||\n      `${item.title.toLowerCase().trim()}__${item.year || \"na\"}`;\n\n    if (!byKey.has(key)) {\n      byKey.set(key, item);\n      continue;\n    }\n\n    const existing = byKey.get(key);\n\n    const existingScore =\n      (existing.date ? 4 : 0) +\n      (existing.authors ? 2 : 0) +\n      (existing.journal ? 1 : 0);\n\n    const itemScore =\n      (item.date ? 4 : 0) +\n      (item.authors ? 2 : 0) +\n      (item.journal ? 1 : 0);\n\n    if (itemScore > existingScore) {\n      byKey.set(key, item);\n    }\n  }\n\n  return Array.from(byKey.values());\n}\n\nfunction sortWorks(items) {\n  return items.slice().sort((a, b) => {\n    const dateA = a.date || `${a.year || 0}-01-01`;\n    const dateB = b.date || `${b.year || 0}-01-01`;\n    return dateB.localeCompare(dateA);\n  });\n}\n\nfunction buildYearOptions(items) {\n  const yearSelect = document.getElementById(\"pub-year\");\n  const years = [...new Set(items.map(w => w.year).filter(Boolean))].sort((a, b) => b - a);\n\n  yearSelect.innerHTML = `<option value=\"all\">all years<\/option>` +\n    years.map(year => `<option value=\"${year}\">${year}<\/option>`).join(\"\");\n}\n\nfunction applyFilters() {\n  const searchValue = document.getElementById(\"pub-search\").value.trim().toLowerCase();\n  const yearValue = document.getElementById(\"pub-year\").value;\n\n  filteredPublications = allPublications.filter(w => {\n    const matchesYear = yearValue === \"all\" || String(w.year) === yearValue;\n    const haystack = `${w.title} ${w.authors} ${w.journal}`.toLowerCase();\n    const matchesSearch = !searchValue || haystack.includes(searchValue);\n    return matchesYear && matchesSearch;\n  });\n\n  currentPage = 1;\n  renderCurrentPage();\n}\n\nfunction renderCurrentPage() {\n  const list = document.getElementById(\"nisc-publications\");\n  const pagination = document.getElementById(\"pub-pagination\");\n  const prevBtn = document.getElementById(\"pub-prev\");\n  const nextBtn = document.getElementById(\"pub-next\");\n  const pageInfo = document.getElementById(\"pub-page-info\");\n\n  if (!filteredPublications.length) {\n    list.innerHTML = \"<li class='empty'>no matching publications found.<\/li>\";\n    pagination.hidden = true;\n    return;\n  }\n\n  const totalPages = Math.max(1, Math.ceil(filteredPublications.length \/ ITEMS_PER_PAGE));\n  currentPage = Math.min(Math.max(1, currentPage), totalPages);\n\n  const start = (currentPage - 1) * ITEMS_PER_PAGE;\n  const pageItems = filteredPublications.slice(start, start + ITEMS_PER_PAGE);\n\n  list.innerHTML = pageItems.map((w, i) => `\n    <li class=\"pub-item\">\n      <div class=\"pub-badge\" aria-hidden=\"true\">${start + i + 1}<\/div>\n      <div class=\"pub-body\">\n        <a href=\"${escapeHtml(w.url)}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"pub-title\">\n          ${escapeHtml(w.title)}\n        <\/a>\n        ${w.authors ? `<div class=\"pub-authors\">${escapeHtml(w.authors)}<\/div>` : \"\"}\n        <div class=\"pub-meta\">\n          ${w.journal ? `<span class=\"journal\">${escapeHtml(w.journal)}<\/span>` : \"\"}\n          ${w.year ? `<span class=\"year\">(${w.year})<\/span>` : \"\"}\n        <\/div>\n      <\/div>\n    <\/li>\n  `).join(\"\");\n\n  pagination.hidden = totalPages <= 1;\n  prevBtn.disabled = currentPage <= 1;\n  nextBtn.disabled = currentPage >= totalPages;\n  pageInfo.textContent = `page ${currentPage} of ${totalPages}`;\n}\n\nfunction wireControls() {\n  document.getElementById(\"pub-search\").addEventListener(\"input\", applyFilters);\n  document.getElementById(\"pub-year\").addEventListener(\"change\", applyFilters);\n\n  document.getElementById(\"pub-prev\").addEventListener(\"click\", () => {\n    currentPage -= 1;\n    renderCurrentPage();\n  });\n\n  document.getElementById(\"pub-next\").addEventListener(\"click\", () => {\n    currentPage += 1;\n    renderCurrentPage();\n  });\n}\n\nasync function renderPublications() {\n  const list = document.getElementById(\"nisc-publications\");\n  list.innerHTML = \"<li class='empty'>loading publications\u2026<\/li>\";\n\n  const all = (await Promise.all(authors.map(a => fetchOpenAlexWorks(a.orcid)))).flat();\n\n  allPublications = sortWorks(\n    dedupeWorks(all).filter(w => w.year && w.year >= MIN_YEAR)\n  );\n\n  buildYearOptions(allPublications);\n  applyFilters();\n}\n\nwireControls();\nrenderPublications();\n<\/script>\n\n<style>\n:root {\n  --nisc-purple-1: #6b3cc9;\n  --nisc-purple-2: #8a4dff;\n  --nisc-ink: #0a4b9e;\n  --nisc-text: #111;\n  --nisc-muted: #555;\n  --nisc-border: #e5e5e5;\n}\n\n\/* controls *\/\n.pub-controls {\n  display: grid;\n  grid-template-columns: 1fr auto;\n  gap: 0.8rem;\n  align-items: center;\n  max-width: 860px;\n  margin: 0 auto 1.5rem;\n}\n\n.pub-search,\n.pub-select {\n  width: 100%;\n  border: 1px solid var(--nisc-border);\n  border-radius: 10px;\n  background: #fff;\n  color: var(--nisc-text);\n  font: 500 0.98rem\/1.2 system-ui, sans-serif;\n  padding: 0.85rem 1rem;\n  outline: none;\n  transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n\n.pub-search:focus,\n.pub-select:focus {\n  border-color: var(--nisc-purple-1);\n  box-shadow: 0 0 0 3px rgba(107,60,201,0.12);\n}\n\n.pub-filter-wrap {\n  min-width: 160px;\n}\n\n\/* list container *\/\n.nisc-pubs {\n  list-style: none;\n  padding: 0;\n  margin: 0 auto;\n  max-width: 860px;\n  position: relative;\n}\n\n@media (min-width: 700px) {\n  .nisc-pubs::before {\n    content: \"\";\n    position: absolute;\n    left: 1.2rem;\n    top: 0.5rem;\n    bottom: 0.5rem;\n    width: 2px;\n    background: linear-gradient(to bottom, rgba(107,60,201,0.18), rgba(138,77,255,0.1));\n  }\n}\n\n\/* publication item *\/\n.pub-item {\n  display: grid;\n  grid-template-columns: auto 1fr;\n  align-items: start;\n  gap: 0.9rem 1rem;\n  padding: 1.1rem 0;\n  border-bottom: 1px solid var(--nisc-border);\n  transition: background 0.2s ease, box-shadow 0.2s ease;\n}\n\n.pub-item:hover {\n  background: #faf8ff;\n  box-shadow: 0 2px 6px rgba(107,60,201,0.08);\n}\n\n\/* number badge *\/\n.pub-badge {\n  width: 2.1rem;\n  height: 2.1rem;\n  border-radius: 50%;\n  background: linear-gradient(135deg, var(--nisc-purple-1), var(--nisc-purple-2));\n  color: #fff;\n  font: 700 0.9rem\/1 system-ui, sans-serif;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  box-shadow: 0 2px 8px rgba(107,60,201,0.3);\n}\n\n\/* text *\/\n.pub-body {\n  min-width: 0;\n}\n\n.pub-title {\n  color: var(--nisc-ink);\n  font-weight: 700;\n  font-size: 1.06rem;\n  line-height: 1.4;\n  text-decoration: none;\n  word-break: break-word;\n  transition: color 0.2s ease;\n}\n\n.pub-title:hover {\n  color: var(--nisc-purple-1);\n}\n\n.pub-authors {\n  margin-top: 0.4rem;\n  color: #666;\n  font-size: 0.95rem;\n  line-height: 1.45;\n}\n\n.pub-meta {\n  margin-top: 0.35rem;\n  color: var(--nisc-muted);\n  font-size: 0.96rem;\n}\n\n.pub-meta .journal {\n  font-style: italic;\n  color: #444;\n}\n\n.pub-meta .year {\n  margin-left: 0.4rem;\n  color: #888;\n}\n\n\/* pagination *\/\n.pub-pagination {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: 0.9rem;\n  margin: 1.5rem auto 0;\n  max-width: 860px;\n}\n\n.pub-page-btn {\n  border: 1px solid var(--nisc-border);\n  background: #fff;\n  color: var(--nisc-text);\n  border-radius: 999px;\n  padding: 0.7rem 1rem;\n  font: 600 0.95rem\/1 system-ui, sans-serif;\n  cursor: pointer;\n  transition: all 0.2s ease;\n}\n\n.pub-page-btn:hover:not(:disabled) {\n  border-color: var(--nisc-purple-1);\n  color: var(--nisc-purple-1);\n  background: #faf8ff;\n}\n\n.pub-page-btn:disabled {\n  opacity: 0.45;\n  cursor: not-allowed;\n}\n\n.pub-page-info {\n  color: var(--nisc-muted);\n  font: 500 0.95rem\/1.2 system-ui, sans-serif;\n}\n\n\/* mobile *\/\n@media (max-width: 699px) {\n  .pub-controls {\n    grid-template-columns: 1fr;\n  }\n\n  .pub-filter-wrap {\n    min-width: 0;\n  }\n\n  .pub-item {\n    grid-template-columns: 2rem 1fr;\n    padding: 1rem 0;\n  }\n\n  .pub-title {\n    font-size: 1.02rem;\n  }\n\n  .pub-authors {\n    font-size: 0.92rem;\n  }\n\n  .pub-pagination {\n    gap: 0.6rem;\n    flex-wrap: wrap;\n  }\n}\n\n\/* empty state *\/\n.nisc-pubs .empty {\n  text-align: center;\n  color: var(--nisc-muted);\n  padding: 2rem 0;\n  border-bottom: 1px solid var(--nisc-border);\n}\n\n\/* accessibility helper *\/\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border: 0;\n}\n<\/style>\n\n\n<p><!-- \/wp:post-content --><\/p>\n<p><!-- \/wp:post-content --><\/p>\n<p><!-- \/wp:post-content --><\/p>\n<p><!-- \/wp:post-content --><\/p>\n<p><!-- wp:paragraph --><\/p>\n<p><!-- \/wp:paragraph --><\/p>","protected":false},"excerpt":{"rendered":"<p>recent publications selected publications from nisc laboratory professors, advancing cognitive science, ai, and human\u2013machine interaction. filter by year All years loading publications\u2026 previous page 1 of 1 next<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-839","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/nisc.unime.it\/index.php?rest_route=\/wp\/v2\/pages\/839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nisc.unime.it\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/nisc.unime.it\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/nisc.unime.it\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nisc.unime.it\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=839"}],"version-history":[{"count":21,"href":"https:\/\/nisc.unime.it\/index.php?rest_route=\/wp\/v2\/pages\/839\/revisions"}],"predecessor-version":[{"id":1144,"href":"https:\/\/nisc.unime.it\/index.php?rest_route=\/wp\/v2\/pages\/839\/revisions\/1144"}],"wp:attachment":[{"href":"https:\/\/nisc.unime.it\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}