{"id":4650,"date":"2026-05-13T09:50:12","date_gmt":"2026-05-13T07:50:12","guid":{"rendered":"https:\/\/runway34.ch\/?page_id=4650"},"modified":"2026-08-25T12:56:54","modified_gmt":"2026-08-25T10:56:54","slug":"abfluege-zuerich","status":"publish","type":"page","link":"https:\/\/runway34.ch\/en\/abfluege-zuerich\/","title":{"rendered":"Departures Zurich"},"content":{"rendered":"\n<div class=\"tv-wrap\">\n\n  <div class=\"tv-title\">\n    Z\u00dcRICH (ZRH) \u2013 ABFL\u00dcGE\n  <\/div>\n\n  <div id=\"tv-status\" class=\"tv-status\">\n    Lade Daten\u2026\n  <\/div>\n\n  <div id=\"tv-board\"><\/div>\n\n<\/div>\n\n<style>\n\nbody{\n  margin:0;\n  background:#000;\n  color:#fff;\n  font-family:Arial, sans-serif;\n}\n\n.tv-wrap{\n  padding:40px 60px;\n}\n\n.tv-title{\n  color:#bf1223;\n  font-size:60px;\n  font-weight:bold;\n  margin-bottom:10px;\n}\n\n.tv-status{\n  color:#bf1223;\n  margin-bottom:20px;\n  font-size:20px;\n}\n\ntable{\n  width:100%;\n  border-collapse:collapse;\n  font-size:36px;\n}\n\nth{\n  text-align:left;\n  padding:14px;\n  border-bottom:3px solid #333;\n  color:#bf1223;\n}\n\ntd{\n  padding:12px;\n  border-bottom:1px solid #222;\n  vertical-align:middle;\n}\n\n\/*\n|--------------------------------------------------------------------------\n| Airline Logo\n|--------------------------------------------------------------------------\n*\/\n\n.logo{\n  width:45px;\n  height:45px;\n  object-fit:contain;\n  margin-right:12px;\n  vertical-align:middle;\n}\n\n\/*\n|--------------------------------------------------------------------------\n| Statusfarben\n|--------------------------------------------------------------------------\n*\/\n\n.active{\n  color:#00ff00;\n}\n\n.scheduled{\n  color:#ffffff;\n}\n\n.delayed{\n  color:#ffcc00;\n}\n\n.cancelled{\n  color:#ff4444;\n}\n\n.boarding{\n  color:#00ff00;\n}\n\n.landed{\n  color:#ffffff;\n}\n\n<\/style>\n\n\n<script>\n\n\/*\n|--------------------------------------------------------------------------\n| Status \u00fcbersetzen\n|--------------------------------------------------------------------------\n*\/\n\nfunction translateStatus(status){\n\n  if(!status){\n    return \"\";\n  }\n\n  const s = String(status).toLowerCase();\n\n  if(s === \"active\"){\n    return \"Aktiv\";\n  }\n\n  if(s === \"scheduled\"){\n    return \"Planm\u00e4ssig\";\n  }\n\n  if(s === \"delayed\"){\n    return \"Versp\u00e4tet\";\n  }\n\n  if(s === \"cancelled\"){\n    return \"Annulliert\";\n  }\n\n  if(s === \"landed\"){\n    return \"Gelandet\";\n  }\n\n  if(s === \"boarding\"){\n    return \"Boarding\";\n  }\n\n  return status;\n}\n\n\n\/*\n|--------------------------------------------------------------------------\n| CSS Klasse f\u00fcr Status\n|--------------------------------------------------------------------------\n*\/\n\nfunction statusClass(status){\n\n  if(!status){\n    return \"scheduled\";\n  }\n\n  const s = String(status).toLowerCase();\n\n  if(s === \"active\"){\n    return \"active\";\n  }\n\n  if(s === \"delayed\"){\n    return \"delayed\";\n  }\n\n  if(s === \"cancelled\"){\n    return \"cancelled\";\n  }\n\n  if(s === \"boarding\"){\n    return \"boarding\";\n  }\n\n  if(s === \"landed\"){\n    return \"landed\";\n  }\n\n  return \"scheduled\";\n}\n\n\n\/*\n|--------------------------------------------------------------------------\n| Fl\u00fcge laden\n|--------------------------------------------------------------------------\n*\/\n\nasync function loadFlights(){\n\n  const board =\n    document.getElementById(\"tv-board\");\n\n  const status =\n    document.getElementById(\"tv-status\");\n\n  try{\n\n    status.textContent =\n      \"Aktualisiere\u2026\";\n\n    \/*\n    |--------------------------------------------------------------------------\n    | WordPress REST API\n    |--------------------------------------------------------------------------\n    *\/\n\n    const res = await fetch(\n      \"\/wp-json\/flights\/v1\/departures\",\n      {\n        cache:\"no-store\"\n      }\n    );\n\n    if(!res.ok){\n\n      throw new Error(\n        \"HTTP Fehler: \" + res.status\n      );\n    }\n\n    const data = await res.json();\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Fehler von API\n    |--------------------------------------------------------------------------\n    *\/\n\n    if(data.error){\n\n      console.error(\n        \"API Fehler:\",\n        data.error\n      );\n\n      status.textContent =\n        \"Fehler beim Laden\";\n\n      board.innerHTML = \"\";\n\n      return;\n    }\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Keine Daten\n    |--------------------------------------------------------------------------\n    *\/\n\n    if(\n      !data.items ||\n      !Array.isArray(data.items) ||\n      data.items.length === 0\n    ){\n\n      status.textContent =\n        \"Keine Daten erhalten\";\n\n      board.innerHTML = \"\";\n\n      return;\n    }\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Aktuelle Zeit\n    |--------------------------------------------------------------------------\n    *\/\n\n    const now =\n      new Date();\n\n    const currentMinutes =\n      now.getHours() * 60 +\n      now.getMinutes();\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Nur aktuelle und kommende Fl\u00fcge\n    |\n    | Fl\u00fcge bleiben noch 30 Minuten nach Abflug sichtbar\n    |--------------------------------------------------------------------------\n    *\/\n\n    const filtered =\n      data.items.filter(f => {\n\n        if(!f.time){\n          return false;\n        }\n\n        const parts =\n          f.time.split(\":\");\n\n        if(parts.length !== 2){\n          return false;\n        }\n\n        const hours =\n          parseInt(parts[0], 10);\n\n        const minutes =\n          parseInt(parts[1], 10);\n\n        if(\n          isNaN(hours) ||\n          isNaN(minutes)\n        ){\n          return false;\n        }\n\n        const flightMinutes =\n          hours * 60 +\n          minutes;\n\n        return flightMinutes >=\n          currentMinutes - 30;\n\n      });\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Maximal 25 anzeigen\n    |--------------------------------------------------------------------------\n    *\/\n\n    const items =\n      (\n        filtered.length\n          ? filtered\n          : data.items\n      ).slice(0,25);\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Tabelle erstellen\n    |--------------------------------------------------------------------------\n    *\/\n\n    let html = `\n\n      <table>\n\n        <tr>\n\n          <th>ZEIT<\/th>\n\n          <th>AIRLINE<\/th>\n\n          <th>FLUG<\/th>\n\n          <th>ZIEL<\/th>\n\n          <th>STATUS<\/th>\n\n        <\/tr>\n\n    `;\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Fl\u00fcge einf\u00fcgen\n    |--------------------------------------------------------------------------\n    *\/\n\n    items.forEach(f => {\n\n      const flight =\n        f.flight || \"\";\n\n      \/*\n      |--------------------------------------------------------------------------\n      | Airline IATA Code\n      |\n      | Beispiel:\n      | Swiss      = LX\n      | Lufthansa  = LH\n      | Air Canada = AC\n      |--------------------------------------------------------------------------\n      *\/\n\n      let airlineCode =\n        f.airline_iata || \"\";\n\n      \/*\n      |--------------------------------------------------------------------------\n      | Fallback:\n      | Wenn Aviationstack keinen Airline-Code liefert,\n      | nehmen wir die ersten 2 Zeichen der Flugnummer\n      |--------------------------------------------------------------------------\n      *\/\n\n      if(!airlineCode && flight){\n\n        airlineCode =\n          flight\n            .substring(0,2)\n            .toUpperCase();\n\n      }\n\n      \/*\n      |--------------------------------------------------------------------------\n      | Airline Logo\n      |--------------------------------------------------------------------------\n      *\/\n\n      const logo =\n        airlineCode\n          ? `https:\/\/images.kiwi.com\/airlines\/64\/${airlineCode}.png`\n          : \"\";\n\n\n      \/*\n      |--------------------------------------------------------------------------\n      | Logo HTML\n      |--------------------------------------------------------------------------\n      *\/\n\n      let logoHtml = \"\";\n\n      if(logo){\n\n        logoHtml = `\n\n          <img decoding=\"async\"\n            class=\"logo\"\n            src=\"${logo}\"\n            alt=\"${f.airline || \"\"}\"\n            onerror=\"this.style.display='none'\"\n          >\n\n        `;\n\n      }\n\n\n      \/*\n      |--------------------------------------------------------------------------\n      | Tabellenzeile\n      |--------------------------------------------------------------------------\n      *\/\n\n      html += `\n\n        <tr>\n\n          <td>\n            ${f.time || \"\"}\n          <\/td>\n\n          <td>\n\n            ${logoHtml}\n\n            ${f.airline || \"\"}\n\n          <\/td>\n\n          <td>\n            ${flight}\n          <\/td>\n\n          <td>\n            ${f.destination || \"\"}\n          <\/td>\n\n          <td class=\"${statusClass(f.status)}\">\n            ${translateStatus(f.status)}\n          <\/td>\n\n        <\/tr>\n\n      `;\n\n    });\n\n\n    html += `\n      <\/table>\n    `;\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Tabelle anzeigen\n    |--------------------------------------------------------------------------\n    *\/\n\n    board.innerHTML =\n      html;\n\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Update Zeit\n    |--------------------------------------------------------------------------\n    *\/\n\n    status.textContent =\n      \"Letztes Update: \" +\n      new Date().toLocaleTimeString(\n        \"de-CH\",\n        {\n          hour:\"2-digit\",\n          minute:\"2-digit\"\n        }\n      );\n\n\n  }catch(e){\n\n    status.textContent =\n      \"Fehler beim Laden\";\n\n    board.innerHTML =\n      \"\";\n\n    console.error(\n      \"Flightboard Fehler:\",\n      e\n    );\n\n  }\n\n}\n\n\n\/*\n|--------------------------------------------------------------------------\n| Beim Laden sofort starten\n|--------------------------------------------------------------------------\n*\/\n\nloadFlights();\n\n\n\/*\n|--------------------------------------------------------------------------\n| Alle 2 Stunden neu laden\n|--------------------------------------------------------------------------\n*\/\n\nsetInterval(\n  loadFlights,\n  7200000\n);\n\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>ZURICH (ZRH) \u2013 DEPARTURES Loading data\u2026<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_citadela_hide_page_title":"1","_citadela_remove_header_space":"1","_citadela_custom_class":"","_citadela_half_map_provider":"","_citadela_half_map_theme":"","_citadela_half_map_themeOSM":"","_citadela_half_map_customTheme":"","_citadela_half_map_dataType":"","_citadela_half_map_dynamicTrack":false,"_citadela_half_map_trackColor":"","_citadela_half_map_trackEndpointsColor":"","_citadela_half_map_filterCategory":"","_citadela_half_map_filterLocation":"","_citadela_half_map_filterFeatured":false,"_citadela_half_map_noDataBehavior":"empty-map","_citadela_half_map_noDataText":"","_citadela_half_map_clusterGridSize":80,"_citadela_header":false,"_citadela_header_over_content":false,"_citadela_header_text_color":"","_citadela_header_logo":[],"_citadela_header_bg_image":[],"_citadela_header_bg_position":{"x":0.5,"y":0.5},"_citadela_header_bg_size":"cover","_citadela_header_bg_repeat":"no-repeat","_citadela_header_bg_fixed":false,"_citadela_header_bg_color":"","_citadela_header_bg_image_overlay":"","_citadela_header_transparent_bg":false,"_citadela_content_width":"full","_citadela_half_layout_position":"","footnotes":""},"class_list":["post-4650","page","type-page","status-publish","hentry"],"featured_image_src":"","featured_image_src_square":false,"rbea_author_info":{"display_name":"infoyo9","author_link":"https:\/\/runway34.ch\/en\/author\/infoyo9\/"},"rbea_excerpt_info":"Z\u00dcRICH (ZRH) \u2013 ABFL\u00dcGE Lade Daten\u2026","_links":{"self":[{"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/pages\/4650","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/comments?post=4650"}],"version-history":[{"count":5,"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/pages\/4650\/revisions"}],"predecessor-version":[{"id":4706,"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/pages\/4650\/revisions\/4706"}],"wp:attachment":[{"href":"https:\/\/runway34.ch\/en\/wp-json\/wp\/v2\/media?parent=4650"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}