topics_domains_unsd

by jlujan

HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>.Stat Data Explorer — demo SDMX</title>

</head>
<body>

<div class="header">
  <div class="logo">SIS-CC</div>
  <div class="nav-icons">
    🔍 ⚙️ ❓
    <span class="lang-switch" id="langSwitch" role="group" aria-label="Idioma">
      <button type="button" data-lang="en">EN</button>
      <button type="button" data-lang="es">ES</button>
      <button type="button" data-lang="fr">FR</button>
    </span>
  </div>
</div>

<h1>.Stat Data Explorer</h1>
<p>Find, access and share data...</p>

<div class="search-container">
  <input type="text" placeholder="Search by keywords" id="searchInput">
  <button type="button">➡️</button>
</div>

<div class="browse-container">
  <p>or browse by</p>
  <div class="dropdown-container" id="dropdown">
    <button class="category-dropdown" id="topicsBtn" aria-haspopup="true" aria-expanded="false">
      TOPICOS ⬇️
    </button>
    <div class="submenu" id="submenu" role="dialog" aria-modal="true" aria-labelledby="submenuTitle">
      <div class="submenu-header">
        <h2 id="submenuTitle">Topics</h2>
        <span class="submenu-meta" id="submenuMeta"></span>
        <button type="button" class="submenu-close" id="submenuClose" aria-label="Cerrar">✕</button>
      </div>
      <div class="submenu-container" id="submenuBody"></div>
    </div>
  </div>
  <div class="overlay" id="overlay"></div>
</div>

<div class="selection" id="selection" hidden></div>

<!-- ------------------------------------------------------------------
     Fuente de datos: CategoryScheme SDMX 2.1.
     Para leerlo de un archivo externo, poner XML_URL = 'CS_TOPICS.xml'
     (requiere servidor por CORS; con file:// se usa el XML embebido).
------------------------------------------------------------------- -->
<script type="application/xml"...

CSS

* { box-sizing: border-box; }

  body {
    font-family: Arial, Helvetica, sans-serif;
    margin: 0;
    min-height: 100vh;
    color: #222;
    text-align: center;
    background: #ff0099;
  }

  .header {
    background: rgba(245,245,245,.96);
    padding: 10px 18px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    color: #222;
    box-shadow: 0 1px 5px rgba(0,0,0,.16);
  }

  .logo { font-weight: 700; letter-spacing: 1px; }
  .nav-icons { display: flex; align-items: center; gap: 10px; }
  .lang-switch { display: inline-flex; gap: 4px; }
  .lang-switch button {
    border: 1px solid #bbb;
    background: #fff;
    color: #222;
    padding: 4px 8px;
    font-size: 12px;
    cursor: pointer;
    border-radius: 3px;
  }
  .lang-switch button[aria-pressed="true"] {
    background: #0b5db3;
    border-color: #0b5db3;
    color: #fff;
  }

  h1 { margin: 28px 0 8px; color: #fff; font-size: 31px; }
  body > p { color: #fff; margin-top: 0; }

  .search-container {
    background: #0d5cad;
    padding: 10px;
    display: inline-flex;
    border-radius: 5px;
    box-shadow: 0 6px 18px rgba(0,0,0,.18);
  }

  .search-container input {
    padding: 9px 10px;
    width: min(420px, 68vw);
    border: 0;
    outline: none;
  }

  .search-container button {
    border: 0;
    background: white;
    padding: 0 12px;
    cursor: pointer;
  }

  .browse-container { margin-top: 12px; }
  .browse-container > p { color: #fff; margin-bottom: 4px; }
  .dropdown-container { display: inline-block; position: relative; }

  .category-dropdown {
    min-width: 210px;
    padding: 11px 14px;
    border: none;
    cursor: pointer;
    font-size: 14px;
    background: #f8a526;
    color: #171717;
    border-radius: 3px;
    box-shadow: 0 3px 9px rgba(0,0,0,.22);
  }

  .overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(2, 15, 31, .52);
   ...

JavaScript

(function () {
  "use strict";

  var XML_URL = null;                 // p.ej. 'CS_TOPICS.xml' para cargar por fetch
  var SCHEME_ID = 'CS_TOPICS';        // CategoryScheme a renderizar
  var XML_LANG = 'http://www.w3.org/XML/1998/namespace';
  var LANG_FALLBACK = ['en'];
  var lang = 'es';
  var scheme = null;                  // {id, urn, names, categories:[...]}

  /* ---------- helpers DOM/XML (sin depender del prefijo str:/com:) ---------- */

  function kids(el, local) {
    var out = [], n = el.firstElementChild;
    while (n) { if (n.localName === local) out.push(n); n = n.nextElementSibling; }
    return out;
  }
  function firstKid(el, local) { return kids(el, local)[0] || null; }

  function langOf(el) {
    return el.getAttributeNS(XML_LANG, 'lang') || el.getAttribute('xml:lang') || '';
  }

  // Todas las traducciones de com:Name -> {en:'...', es:'...', fr:'...'}
  function namesOf(el) {
    var map = {};
    kids(el, 'Name').forEach(function (n) {
      map[langOf(n) || 'und'] = (n.textContent || '').trim();
    });
    return map;
  }

  function label(names) {
    if (names[lang]) return names[lang];
    for (var i = 0; i < LANG_FALLBACK.length; i++) {
      if (names[LANG_FALLBACK[i]]) return names[LANG_FALLBACK[i]];
    }
    var keys = Object.keys(names);
    return keys.length ? names[keys[0]] : '(sin nombre)';
  }

  // Anotación ORDER -> número usado para ordenar hermanos
  function orderOf(el) {
    var anns = firstKid(el, 'Annotations');
    if (!anns) return Number.MAX_SAFE_INTEGER;
    var list = kids(anns, 'Annotation');
    for (var i = 0; i < list.length; i++) {
      var type = firstKid(list[i], 'AnnotationType');
      if (type && type.textContent.trim().toUpperCase() === 'ORDER') {
        var txt = firstKid(list[i], 'AnnotationText');
        var v = parseFloat(txt ? txt.textContent : '');
        return isNaN(v) ? Number.MAX_SAFE_INTEGER : v;
      }
    }
    return...