{"id":20386,"date":"2026-09-17T15:04:00","date_gmt":"2026-09-17T07:04:00","guid":{"rendered":"https:\/\/web.edcity.hk\/?page_id=20386"},"modified":"2026-10-29T18:18:12","modified_gmt":"2026-10-29T10:18:12","slug":"workshops","status":"publish","type":"page","link":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/","title":{"rendered":"Workshops"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"20386\" class=\"elementor elementor-20386\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-21315c7 e-flex e-con-boxed e-con e-parent\" data-id=\"21315c7\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ea378c9 e-con e-atomic-element e-flexbox-base e-ea378c9-759a8cd\" data-id=\"ea378c9\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"ea378c9\" data-e-type=\"e-flexbox\" data-id=\"ea378c9\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c050d0a e-con e-atomic-element e-flexbox-base e-c050d0a-2a00ff1\" data-id=\"c050d0a\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"c050d0a\" data-e-type=\"e-flexbox\" data-id=\"c050d0a\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0b0065c e-con e-atomic-element e-flexbox-base e-0b0065c-0791b16\" data-id=\"0b0065c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"0b0065c\" data-e-type=\"e-flexbox\" data-id=\"0b0065c\">\n\t\t\t\t\t<div class=\"e-9c9dba9-f7c05fe e-svg-base\" data-interaction-id=\"9c9dba9\" data-e-type=\"widget\" data-id=\"9c9dba9\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width: 100%; height: 100%; overflow: unset;\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" fill=\"currentColor\"><circle cx=\"4\" cy=\"4\" r=\"4\" fill=\"url(#paint0_linear_55_466)\"><\/circle><defs><linearGradient id=\"paint0_linear_55_466\" x1=\"0\" y1=\"4\" x2=\"8\" y2=\"4\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#127BFF\"><\/stop><stop offset=\"0.55\" stop-color=\"#0C9A8A\"><\/stop><stop offset=\"1\" stop-color=\"#10A55A\"><\/stop><\/linearGradient><\/defs><\/svg><\/div>\t\t\t\t\t\t\t<h5 \n\t\tdata-interaction-id=\"6df68c2\" \n\t\tclass=\"e-6df68c2-3be0398 e-heading-base\" \n\t\t \n\t\tdata-e-type=\"widget\" data-id=\"6df68c2\"\n\t>\n\t\n\t\t\t<a \n\t\t\thref=\"https:\/\/web.edcity.hk\/en\/aied\/home\/\" target=\"_self\"\n\t\t\tclass=\"e-heading-link-base\">\n\t\t\thome\n\t\t<\/a>\n\t\t<\/h5>\n\t\t\t\t\t\t\t<h5 \n\t\tdata-interaction-id=\"84d16b9\" \n\t\tclass=\"e-84d16b9-4b311c1 e-heading-base\" \n\t\t \n\t\tdata-e-type=\"widget\" data-id=\"84d16b9\"\n\t>\n\t\n\t\t\t\/\n\t\t<\/h5>\n\t\t\t\t\t\t\t<h5 \n\t\tdata-interaction-id=\"29144e4\" \n\t\tclass=\"e-29144e4-365b08d e-heading-base\" \n\t\t \n\t\tdata-e-type=\"widget\" data-id=\"29144e4\"\n\t>\n\t\n\t\t\tworkshops\n\t\t<\/h5>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e97e70e elementor-widget__width-inherit elementor-widget elementor-widget-heading\" data-id=\"e97e70e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\"><span>Workshops<\/span><\/h1>\t\t\t\t<\/div>\n\t\t\t\t\t\n<hr class=\"e-e873158-3183f47 e-divider-base\" data-interaction-id=\"e873158\" id=aied-reg-divider data-e-type=\"widget\" data-id=\"e873158\" \/>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7943eb0 elementor-widget elementor-widget-text-editor\" data-id=\"7943eb0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>Hands-on practical experience with AI-powered educational solutions, and how they work in the classroom.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b760253 e-con e-atomic-element e-div-block-base e-b760253-715822d\" data-id=\"b760253\" data-element_type=\"e-div-block\" data-e-type=\"e-div-block\" data-interaction-id=\"b760253\" id=\"aied-reg-title-bg-container\" data-e-type=\"e-div-block\" data-id=\"b760253\">\n\t\t\t\t<div class=\"elementor-element elementor-element-bc6da10 elementor-widget__width-inherit elementor-absolute elementor-widget elementor-widget-image\" data-id=\"bc6da10\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" src=\"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2026\/09\/aied-watermark.png\" title=\"aied-watermark\" alt=\"aied-watermark\" loading=\"lazy\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-52b5107 e-flex e-con-boxed e-con e-parent\" data-id=\"52b5107\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-14d3310 elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"14d3310\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div\r\n\tclass=\"aied-workshops\"\r\n\tdata-aied-workshops>\r\n\t<div class=\"aied-workshops__toolbar\">\r\n\t\t<div\r\n\t\t\tclass=\"aied-workshops__tabs\"\r\n\t\t\trole=\"tablist\"\r\n\t\t\taria-label=\"Workshop dates\"><\/div>\r\n\t\t<label class=\"aied-workshops__search\">\r\n\t\t\t<svg\r\n\t\t\t\taria-hidden=\"true\"\r\n\t\t\t\tviewBox=\"0 0 24 24\">\r\n\t\t\t\t<circle\r\n\t\t\t\t\tcx=\"11\"\r\n\t\t\t\t\tcy=\"11\"\r\n\t\t\t\t\tr=\"7\"><\/circle>\r\n\t\t\t\t<path d=\"m20 20-4-4\"><\/path>\r\n\t\t\t<\/svg>\r\n\t\t\t<span\r\n\t\t\t\tclass=\"aied-workshops__sr-only\"\r\n\t\t\t\tdata-search-label><\/span>\r\n\t\t\t<input\r\n\t\t\t\ttype=\"search\"\r\n\t\t\t\tautocomplete=\"off\"\r\n\t\t\t\tdata-search \/>\r\n\t\t<\/label>\r\n\t<\/div>\r\n\t<div\r\n\t\tclass=\"aied-workshops__status\"\r\n\t\tdata-status\r\n\t\trole=\"status\"\r\n\t\taria-live=\"polite\"><\/div>\r\n\t<div\r\n\t\tclass=\"aied-workshops__groups\"\r\n\t\tdata-groups><\/div>\r\n\t<dialog\r\n\t\tclass=\"aied-workshops__dialog\"\r\n\t\tdata-dialog\r\n\t\taria-label=\"Workshop details\">\r\n\t\t<div\r\n\t\t\tclass=\"aied-workshops__dialog-content\"\r\n\t\t\tdata-dialog-content><\/div>\r\n\t<\/dialog>\r\n<\/div>\r\n\r\n<script>\r\n\t;(() => {\r\n\t\t'use strict'\r\n\r\n\t\tconst root = document.currentScript.parentElement.querySelector(\r\n\t\t\t'[data-aied-workshops]'\r\n\t\t)\r\n\t\tif (\r\n\t\t\t!root ||\r\n\t\t\t!root.matches('[data-aied-workshops]') ||\r\n\t\t\troot.dataset.initialized === 'true'\r\n\t\t) {\r\n\t\t\treturn\r\n\t\t}\r\n\t\troot.dataset.initialized = 'true'\r\n\r\n\t\tconst supportedLanguages = ['zh-hant', 'en', 'zh-hans']\r\n\t\tconst language =\r\n\t\t\tlocation.pathname\r\n\t\t\t\t.toLowerCase()\r\n\t\t\t\t.split('\/')\r\n\t\t\t\t.find((part) => supportedLanguages.includes(part)) || 'zh-hant'\r\n\t\tconst eventSlug =\r\n\t\t\t(location.pathname.match(\/\\\/event\\\/([^\/]+)\\\/?$\/i) || [])[1] || ''\r\n\t\tconst locale = { 'zh-hant': 'zh-HK', en: 'en-GB', 'zh-hans': 'zh-CN' }[\r\n\t\t\tlanguage\r\n\t\t]\r\n\t\tconst copy = {\r\n\t\t\ten: {\r\n\t\t\t\tall: 'All days',\r\n\t\t\t\tsearch: 'Search workshops',\r\n\t\t\t\tday: 'Day',\r\n\t\t\t\tworkshops: 'workshops',\r\n\t\t\t\tdetails: 'View details',\r\n\t\t\t\tcategory: 'Workshop',\r\n\t\t\t\tcantonese: 'Cantonese',\r\n\t\t\t\tenglish: 'English',\r\n\t\t\t\tloading: 'Loading workshops...',\r\n\t\t\t\tclose: 'Close',\r\n\t\t\t\tregister: 'Register',\r\n\t\t\t\tworkshopDetails: 'Workshop details',\r\n\t\t\t\tspeakers: 'Speakers',\r\n\t\t\t\tdate: 'Date',\r\n\t\t\t\ttime: 'Time',\r\n\t\t\t\tvenue: 'Venue',\r\n\t\t\t\tlanguage: 'Language',\r\n\t\t\t\tdescription: 'About this workshop',\r\n\t\t\t\tfilterKicker: 'Empty state',\r\n\t\t\t\tfilterTitle: 'No workshops match these filters.',\r\n\t\t\t\tfilterCopy: 'Try another day or clear your search.',\r\n\t\t\t\tclear: 'Clear filters',\r\n\t\t\t\tcomingSoonKicker: 'Workshops',\r\n\t\t\t\tcomingSoonTitle: 'Workshops',\r\n\t\t\t\tcomingSoonCopy: 'Workshop details will be announced soon.',\r\n\t\t\t\tcomingSoonLabel: 'Coming soon',\r\n\t\t\t\tmore: (count) => `${count} more`\r\n\t\t\t},\r\n\t\t\t'zh-hant': {\r\n\t\t\t\tall: '\u6240\u6709\u65e5\u5b50',\r\n\t\t\t\tsearch: '\u641c\u5c0b\u5de5\u4f5c\u574a',\r\n\t\t\t\tday: '\u7b2c',\r\n\t\t\t\tworkshops: '\u500b\u5de5\u4f5c\u574a',\r\n\t\t\t\tdetails: '\u67e5\u770b\u8a73\u60c5',\r\n\t\t\t\tcategory: '\u5de5\u4f5c\u574a',\r\n\t\t\t\tcantonese: '\u5ee3\u6771\u8a71',\r\n\t\t\t\tenglish: '\u82f1\u8a9e',\r\n\t\t\t\tloading: '\u6b63\u5728\u8f09\u5165\u5de5\u4f5c\u574a...',\r\n\t\t\t\tclose: '\u95dc\u9589',\r\n\t\t\t\tregister: '\u5831\u540d',\r\n\t\t\t\tworkshopDetails: '\u5de5\u4f5c\u574a\u8a73\u60c5',\r\n\t\t\t\tspeakers: '\u8b1b\u8005',\r\n\t\t\t\tdate: '\u65e5\u671f',\r\n\t\t\t\ttime: '\u6642\u9593',\r\n\t\t\t\tvenue: '\u5730\u9ede',\r\n\t\t\t\tlanguage: '\u8a9e\u8a00',\r\n\t\t\t\tdescription: '\u5de5\u4f5c\u574a\u7c21\u4ecb',\r\n\t\t\t\tfilterKicker: '\u6c92\u6709\u7d50\u679c',\r\n\t\t\t\tfilterTitle: '\u6c92\u6709\u7b26\u5408\u7be9\u9078\u689d\u4ef6\u7684\u5de5\u4f5c\u574a\u3002',\r\n\t\t\t\tfilterCopy: '\u8acb\u9078\u64c7\u5176\u4ed6\u65e5\u5b50\u6216\u6e05\u9664\u641c\u5c0b\u3002',\r\n\t\t\t\tclear: '\u6e05\u9664\u7be9\u9078',\r\n\t\t\t\tcomingSoonKicker: '\u5de5\u4f5c\u574a',\r\n\t\t\t\tcomingSoonTitle: '\u5de5\u4f5c\u574a',\r\n\t\t\t\tcomingSoonCopy: '\u5de5\u4f5c\u574a\u8a73\u60c5\u5373\u5c07\u516c\u5e03\u3002',\r\n\t\t\t\tcomingSoonLabel: '\u5373\u5c07\u63a8\u51fa',\r\n\t\t\t\tmore: (count) => `\u5176\u9918 ${count} \u4f4d`\r\n\t\t\t},\r\n\t\t\t'zh-hans': {\r\n\t\t\t\tall: '\u6240\u6709\u65e5\u671f',\r\n\t\t\t\tsearch: '\u641c\u7d22\u5de5\u4f5c\u574a',\r\n\t\t\t\tday: '\u7b2c',\r\n\t\t\t\tworkshops: '\u4e2a\u5de5\u4f5c\u574a',\r\n\t\t\t\tdetails: '\u67e5\u770b\u8be6\u60c5',\r\n\t\t\t\tcategory: '\u5de5\u4f5c\u574a',\r\n\t\t\t\tcantonese: '\u7ca4\u8bed',\r\n\t\t\t\tenglish: '\u82f1\u8bed',\r\n\t\t\t\tloading: '\u6b63\u5728\u52a0\u8f7d\u5de5\u4f5c\u574a...',\r\n\t\t\t\tclose: '\u5173\u95ed',\r\n\t\t\t\tregister: '\u62a5\u540d',\r\n\t\t\t\tworkshopDetails: '\u5de5\u4f5c\u574a\u8be6\u60c5',\r\n\t\t\t\tspeakers: '\u8bb2\u8005',\r\n\t\t\t\tdate: '\u65e5\u671f',\r\n\t\t\t\ttime: '\u65f6\u95f4',\r\n\t\t\t\tvenue: '\u5730\u70b9',\r\n\t\t\t\tlanguage: '\u8bed\u8a00',\r\n\t\t\t\tdescription: '\u5de5\u4f5c\u574a\u7b80\u4ecb',\r\n\t\t\t\tfilterKicker: '\u6ca1\u6709\u7ed3\u679c',\r\n\t\t\t\tfilterTitle: '\u6ca1\u6709\u7b26\u5408\u7b5b\u9009\u6761\u4ef6\u7684\u5de5\u4f5c\u574a\u3002',\r\n\t\t\t\tfilterCopy: '\u8bf7\u9009\u62e9\u5176\u4ed6\u65e5\u671f\u6216\u6e05\u9664\u641c\u7d22\u3002',\r\n\t\t\t\tclear: '\u6e05\u9664\u7b5b\u9009',\r\n\t\t\t\tcomingSoonKicker: '\u5de5\u4f5c\u574a',\r\n\t\t\t\tcomingSoonTitle: '\u5de5\u4f5c\u574a',\r\n\t\t\t\tcomingSoonCopy: '\u5de5\u4f5c\u574a\u8be6\u60c5\u5373\u5c06\u516c\u5e03\u3002',\r\n\t\t\t\tcomingSoonLabel: '\u5373\u5c06\u63a8\u51fa',\r\n\t\t\t\tmore: (count) => `\u5176\u4f59 ${count} \u4f4d`\r\n\t\t\t}\r\n\t\t}[language]\r\n\r\n\t\tconst tabsElement = root.querySelector('.aied-workshops__tabs')\r\n\t\tconst searchElement = root.querySelector('[data-search]')\r\n\t\tconst searchLabelElement = root.querySelector('[data-search-label]')\r\n\t\tconst statusElement = root.querySelector('[data-status]')\r\n\t\tconst groupsElement = root.querySelector('[data-groups]')\r\n\t\tconst dialogElement = root.querySelector('[data-dialog]')\r\n\t\tconst dialogContentElement = root.querySelector('[data-dialog-content]')\r\n\t\tlet workshops = []\r\n\t\tlet selectedDate = 'all'\r\n\t\tlet searchTerm = ''\r\n\t\tlet searchTimer\r\n\t\tlet dialogTrigger\r\n\r\n\t\tsearchElement.placeholder = copy.search\r\n\t\tsearchElement.setAttribute('aria-label', copy.search)\r\n\t\tsearchLabelElement.textContent = copy.search\r\n\t\ttabsElement.setAttribute('aria-label', copy.all)\r\n\r\n\t\tconst icon = (type) => {\r\n\t\t\tconst icons = {\r\n\t\t\t\tclock:\r\n\t\t\t\t\t'<svg class=\"aied-workshops__icon\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"9\"><\/circle><path d=\"M12 7v5l3 2\"><\/path><\/svg>',\r\n\t\t\t\tlocation:\r\n\t\t\t\t\t'<svg class=\"aied-workshops__icon\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20 10c0 5-8 11-8 11S4 15 4 10a8 8 0 1 1 16 0Z\"><\/path><circle cx=\"12\" cy=\"10\" r=\"2.5\"><\/circle><\/svg>',\r\n\t\t\t\tlanguage:\r\n\t\t\t\t\t'<svg class=\"aied-workshops__icon\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M4 5h16v12H8l-4 3V5Z\"><\/path><path d=\"M8 9h8M8 13h5\"><\/path><\/svg>',\r\n\t\t\t\tclose:\r\n\t\t\t\t\t'<svg class=\"aied-workshops__dialog-close-icon\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"m6 6 12 12M18 6 6 18\"><\/path><\/svg>'\r\n\t\t\t}\r\n\t\t\tconst template = document.createElement('template')\r\n\t\t\ttemplate.innerHTML = icons[type]\r\n\t\t\treturn template.content.firstElementChild\r\n\t\t}\r\n\r\n\t\tconst parseDate = (value) => {\r\n\t\t\tconst match = String(value || '').match(\/^(\\d{4})-(\\d{2})-(\\d{2})$\/)\r\n\t\t\treturn match\r\n\t\t\t\t? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))\r\n\t\t\t\t: null\r\n\t\t}\r\n\r\n\t\tconst normalize = (item, index) => {\r\n\t\t\tconst link = String(item.link || '')\r\n\t\t\tconst rawLanguages = Array.isArray(item.language)\r\n\t\t\t\t? item.language\r\n\t\t\t\t: [item.language]\r\n\t\t\tconst speakers = Array.isArray(item.speakers)\r\n\t\t\t\t? item.speakers.filter(Boolean).map((speaker) => ({\r\n\t\t\t\t\t\tphoto: String(speaker.photo || ''),\r\n\t\t\t\t\t\tname: String(speaker.name || ''),\r\n\t\t\t\t\t\ttitle: String(speaker.title || ''),\r\n\t\t\t\t\t\torganisation: String(speaker.organisation || '')\r\n\t\t\t\t\t}))\r\n\t\t\t\t: []\r\n\t\t\treturn {\r\n\t\t\t\tid: `${item.date || 'undated'}-${item.start_time || 'time'}-${index}`,\r\n\t\t\t\ttitle: String(item.title || ''),\r\n\t\t\t\tdescription: String(item.description || ''),\r\n\t\t\t\tdate: String(item.date || ''),\r\n\t\t\t\tstartTime: String(item.start_time || '').slice(0, 5),\r\n\t\t\t\tendTime: String(item.end_time || '').slice(0, 5),\r\n\t\t\t\tvenue: String(item.venue || ''),\r\n\t\t\t\tlink: link.trim() ? link : `\/${language}\/aied\/registration\/`,\r\n\t\t\t\tlanguages: rawLanguages\r\n\t\t\t\t\t.filter(Boolean)\r\n\t\t\t\t\t.map((value) => String(value).toLowerCase()),\r\n\t\t\t\tspeakers\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst formatDate = (dateValue, options) => {\r\n\t\t\tconst date = parseDate(dateValue)\r\n\t\t\treturn date\r\n\t\t\t\t? new Intl.DateTimeFormat(locale, options).format(date)\r\n\t\t\t\t: dateValue\r\n\t\t}\r\n\r\n\t\tconst formatTabDate = (dateValue, dayIndex) =>\r\n\t\t\tlanguage === 'en'\r\n\t\t\t\t? `${copy.day} ${dayIndex + 1} \u00b7 ${formatDate(dateValue, { day: 'numeric', month: 'short' })}`\r\n\t\t\t\t: `${copy.day} ${dayIndex + 1} \u65e5 \u00b7 ${formatDate(dateValue, { day: 'numeric', month: 'short' })}`\r\n\t\tconst formatCardDate = (dateValue) =>\r\n\t\t\tformatDate(dateValue, {\r\n\t\t\t\tweekday: 'short',\r\n\t\t\t\tday: 'numeric',\r\n\t\t\t\tmonth: 'short'\r\n\t\t\t})\r\n\t\tconst formatHeadingDate = (dateValue) =>\r\n\t\t\tformatDate(dateValue, {\r\n\t\t\t\tweekday: 'short',\r\n\t\t\t\tday: 'numeric',\r\n\t\t\t\tmonth: 'short',\r\n\t\t\t\tyear: 'numeric'\r\n\t\t\t})\r\n\t\tconst formatDayTitle = (dateValue, dayIndex) =>\r\n\t\t\tlanguage === 'en'\r\n\t\t\t\t? `${copy.day} ${dayIndex + 1} \u00b7 ${formatHeadingDate(dateValue)}`\r\n\t\t\t\t: `${copy.day} ${dayIndex + 1} \u65e5 \u00b7 ${formatHeadingDate(dateValue)}`\r\n\r\n\t\tconst createElement = (tag, className, text) => {\r\n\t\t\tconst element = document.createElement(tag)\r\n\t\t\tif (className) element.className = className\r\n\t\t\tif (text !== undefined) element.textContent = text\r\n\t\t\treturn element\r\n\t\t}\r\n\r\n\t\tconst createMetaRow = (type, text) => {\r\n\t\t\tconst row = createElement(\r\n\t\t\t\t'div',\r\n\t\t\t\t`aied-workshops__meta-row aied-workshops__meta-row--${type}`\r\n\t\t\t)\r\n\t\t\trow.append(icon(type), createElement('span', '', text))\r\n\t\t\treturn row\r\n\t\t}\r\n\r\n\t\tconst createAvatar = (speaker) => {\r\n\t\t\tconst avatar = createElement('span', 'aied-workshops__avatar')\r\n\t\t\tif (speaker.photo) {\r\n\t\t\t\tconst image = document.createElement('img')\r\n\t\t\t\timage.src = speaker.photo\r\n\t\t\t\timage.alt = ''\r\n\t\t\t\timage.loading = 'lazy'\r\n\t\t\t\timage.addEventListener(\r\n\t\t\t\t\t'error',\r\n\t\t\t\t\t() => {\r\n\t\t\t\t\t\timage.remove()\r\n\t\t\t\t\t\tavatar.append(\r\n\t\t\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t\t\t'span',\r\n\t\t\t\t\t\t\t\t'aied-workshops__avatar-fallback',\r\n\t\t\t\t\t\t\t\t(speaker.name || '?').charAt(0).toUpperCase()\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{ once: true }\r\n\t\t\t\t)\r\n\t\t\t\tavatar.append(image)\r\n\t\t\t} else {\r\n\t\t\t\tavatar.append(\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'span',\r\n\t\t\t\t\t\t'aied-workshops__avatar-fallback',\r\n\t\t\t\t\t\t(speaker.name || '?').charAt(0).toUpperCase()\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t\treturn avatar\r\n\t\t}\r\n\r\n\t\tconst createSpeaker = (workshop) => {\r\n\t\t\tconst wrapper = createElement('div', 'aied-workshops__speaker')\r\n\t\t\tconst avatars = createElement('div', 'aied-workshops__avatars')\r\n\t\t\tconst visibleSpeakers = workshop.speakers.slice(0, 2)\r\n\t\t\tvisibleSpeakers.forEach((speaker) =>\r\n\t\t\t\tavatars.append(createAvatar(speaker))\r\n\t\t\t)\r\n\t\t\tif (!visibleSpeakers.length) avatars.append(createAvatar({ name: '?' }))\r\n\t\t\tif (workshop.speakers.length > 2)\r\n\t\t\t\tavatars.append(\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'span',\r\n\t\t\t\t\t\t'aied-workshops__avatar-more',\r\n\t\t\t\t\t\t`+${workshop.speakers.length - 2}`\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\r\n\t\t\tconst names = workshop.speakers\r\n\t\t\t\t.slice(0, 2)\r\n\t\t\t\t.map((speaker) => speaker.name)\r\n\t\t\t\t.filter(Boolean)\r\n\t\t\tlet nameText = names.join(', ')\r\n\t\t\tif (workshop.speakers.length > 2)\r\n\t\t\t\tnameText += `${nameText ? ' & ' : ''}${copy.more(workshop.speakers.length - 2)}`\r\n\t\t\tconst organisations = [\r\n\t\t\t\t...new Set(\r\n\t\t\t\t\tworkshop.speakers\r\n\t\t\t\t\t\t.map((speaker) => speaker.organisation)\r\n\t\t\t\t\t\t.filter(Boolean)\r\n\t\t\t\t)\r\n\t\t\t].join(' \u00b7 ')\r\n\t\t\tconst speakerCopy = createElement('div', 'aied-workshops__speaker-copy')\r\n\t\t\tspeakerCopy.append(\r\n\t\t\t\tcreateElement('div', 'aied-workshops__speaker-name', nameText || '-'),\r\n\t\t\t\tcreateElement('div', 'aied-workshops__speaker-org', organisations || '')\r\n\t\t\t)\r\n\t\t\twrapper.append(avatars, speakerCopy)\r\n\t\t\treturn wrapper\r\n\t\t}\r\n\r\n\t\tconst createCard = (workshop) => {\r\n\t\t\tconst card = createElement('article', 'aied-workshops__card')\r\n\t\t\tconst main = createElement('div', 'aied-workshops__card-main')\r\n\t\t\tconst schedule = createElement('div', 'aied-workshops__schedule')\r\n\t\t\tschedule.append(\r\n\t\t\t\tcreateElement(\r\n\t\t\t\t\t'span',\r\n\t\t\t\t\t'aied-workshops__date-chip',\r\n\t\t\t\t\tformatCardDate(workshop.date)\r\n\t\t\t\t)\r\n\t\t\t)\r\n\t\t\tconst time = createElement('span', 'aied-workshops__time')\r\n\t\t\ttime.append(\r\n\t\t\t\ticon('clock'),\r\n\t\t\t\tdocument.createTextNode(`${workshop.startTime} \u2013 ${workshop.endTime}`)\r\n\t\t\t)\r\n\t\t\tschedule.append(time)\r\n\t\t\tmain.append(\r\n\t\t\t\tschedule,\r\n\t\t\t\tcreateElement('h3', 'aied-workshops__title', workshop.title),\r\n\t\t\t\tcreateSpeaker(workshop)\r\n\t\t\t)\r\n\r\n\t\t\tconst meta = createElement('div', 'aied-workshops__meta')\r\n\t\t\tif (workshop.venue) meta.append(createMetaRow('location', workshop.venue))\r\n\t\t\tif (workshop.languages.length) {\r\n\t\t\t\tmeta.append(\r\n\t\t\t\t\tcreateMetaRow(\r\n\t\t\t\t\t\t'language',\r\n\t\t\t\t\t\tworkshop.languages.map((value) => copy[value] || value).join(' \u00b7 ')\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t\tmain.append(\r\n\t\t\t\tmeta,\r\n\t\t\t\tcreateElement('p', 'aied-workshops__description', workshop.description)\r\n\t\t\t)\r\n\r\n\t\t\tconst footer = createElement('div', 'aied-workshops__footer')\r\n\t\t\tconst details = createElement('button', 'aied-workshops__details')\r\n\t\t\tdetails.type = 'button'\r\n\t\t\tdetails.dataset.workshopId = workshop.id\r\n\t\t\tdetails.append(\r\n\t\t\t\tdocument.createTextNode(copy.details),\r\n\t\t\t\tdocument.createTextNode(' \u2192')\r\n\t\t\t)\r\n\t\t\tfooter.append(details)\r\n\t\t\tcard.append(main, footer)\r\n\t\t\treturn card\r\n\t\t}\r\n\r\n\t\tconst createDialogFact = (label, value) => {\r\n\t\t\tconst fact = createElement('div', 'aied-workshops__dialog-fact')\r\n\t\t\tfact.append(\r\n\t\t\t\tcreateElement('span', 'aied-workshops__dialog-fact-label', label),\r\n\t\t\t\tcreateElement('span', 'aied-workshops__dialog-fact-value', value)\r\n\t\t\t)\r\n\t\t\treturn fact\r\n\t\t}\r\n\r\n\t\tconst createDialogSpeaker = (speaker) => {\r\n\t\t\tconst row = createElement('div', 'aied-workshops__dialog-speaker')\r\n\t\t\tconst speakerCopy = createElement(\r\n\t\t\t\t'div',\r\n\t\t\t\t'aied-workshops__dialog-speaker-copy'\r\n\t\t\t)\r\n\t\t\tspeakerCopy.append(\r\n\t\t\t\tcreateElement(\r\n\t\t\t\t\t'div',\r\n\t\t\t\t\t'aied-workshops__dialog-speaker-name',\r\n\t\t\t\t\tspeaker.name || '-'\r\n\t\t\t\t),\r\n\t\t\t\tcreateElement(\r\n\t\t\t\t\t'div',\r\n\t\t\t\t\t'aied-workshops__dialog-speaker-title',\r\n\t\t\t\t\tspeaker.title || ''\r\n\t\t\t\t),\r\n\t\t\t\tcreateElement(\r\n\t\t\t\t\t'div',\r\n\t\t\t\t\t'aied-workshops__dialog-speaker-org',\r\n\t\t\t\t\tspeaker.organisation || ''\r\n\t\t\t\t)\r\n\t\t\t)\r\n\t\t\trow.append(createAvatar(speaker), speakerCopy)\r\n\t\t\treturn row\r\n\t\t}\r\n\r\n\t\tconst renderDialog = (workshop) => {\r\n\t\t\tconst header = createElement('header', 'aied-workshops__dialog-header')\r\n\t\t\tconst headerCopy = createElement(\r\n\t\t\t\t'div',\r\n\t\t\t\t'aied-workshops__dialog-header-copy'\r\n\t\t\t)\r\n\t\t\tconst eyebrow = createElement('div', 'aied-workshops__dialog-eyebrows')\r\n\t\t\teyebrow.append(\r\n\t\t\t\tcreateElement('span', 'aied-workshops__dialog-category', copy.category)\r\n\t\t\t)\r\n\t\t\tconst title = createElement(\r\n\t\t\t\t'h2',\r\n\t\t\t\t'aied-workshops__dialog-title',\r\n\t\t\t\tworkshop.title\r\n\t\t\t)\r\n\t\t\ttitle.id = 'aied-workshops-dialog-title'\r\n\t\t\tconst facts = createElement('div', 'aied-workshops__dialog-facts')\r\n\t\t\tfacts.append(\r\n\t\t\t\tcreateDialogFact(copy.date, formatHeadingDate(workshop.date)),\r\n\t\t\t\tcreateDialogFact(\r\n\t\t\t\t\tcopy.time,\r\n\t\t\t\t\t`${workshop.startTime} \u2013 ${workshop.endTime}`\r\n\t\t\t\t)\r\n\t\t\t)\r\n\t\t\tif (workshop.venue)\r\n\t\t\t\tfacts.append(createDialogFact(copy.venue, workshop.venue))\r\n\t\t\tif (workshop.languages.length)\r\n\t\t\t\tfacts.append(\r\n\t\t\t\t\tcreateDialogFact(\r\n\t\t\t\t\t\tcopy.language,\r\n\t\t\t\t\t\tworkshop.languages.map((value) => copy[value] || value).join(' \u00b7 ')\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\theaderCopy.append(eyebrow, title, facts)\r\n\t\t\tconst closeButton = createElement(\r\n\t\t\t\t'button',\r\n\t\t\t\t'aied-workshops__dialog-close'\r\n\t\t\t)\r\n\t\t\tcloseButton.type = 'button'\r\n\t\t\tcloseButton.setAttribute('aria-label', copy.close)\r\n\t\t\tcloseButton.append(icon('close'))\r\n\t\t\tcloseButton.addEventListener('click', () => dialogElement.close())\r\n\t\t\theader.append(headerCopy, closeButton)\r\n\r\n\t\t\tconst body = createElement('div', 'aied-workshops__dialog-body')\r\n\r\n\t\t\tif (workshop.description) {\r\n\t\t\t\tconst description = createElement(\r\n\t\t\t\t\t'section',\r\n\t\t\t\t\t'aied-workshops__dialog-description'\r\n\t\t\t\t)\r\n\t\t\t\tdescription.append(\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'h3',\r\n\t\t\t\t\t\t'aied-workshops__dialog-section-title aied-workshops__dialog-section-title--description',\r\n\t\t\t\t\t\tcopy.description\r\n\t\t\t\t\t),\r\n\t\t\t\t\tcreateElement('p', '', workshop.description)\r\n\t\t\t\t)\r\n\t\t\t\tbody.append(description)\r\n\t\t\t}\r\n\r\n\t\t\tif (workshop.speakers.length) {\r\n\t\t\t\tconst speakers = createElement(\r\n\t\t\t\t\t'section',\r\n\t\t\t\t\t'aied-workshops__dialog-speakers'\r\n\t\t\t\t)\r\n\t\t\t\tspeakers.append(\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'h3',\r\n\t\t\t\t\t\t'aied-workshops__dialog-section-title aied-workshops__dialog-section-title--speakers',\r\n\t\t\t\t\t\tcopy.speakers\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t\tworkshop.speakers.forEach((speaker) =>\r\n\t\t\t\t\tspeakers.append(createDialogSpeaker(speaker))\r\n\t\t\t\t)\r\n\t\t\t\tbody.append(speakers)\r\n\t\t\t}\r\n\r\n\t\t\tconst fragment = document.createDocumentFragment()\r\n\t\t\tfragment.append(header, body)\r\n\t\t\tif (workshop.link) {\r\n\t\t\t\tconst footer = createElement('footer', 'aied-workshops__dialog-footer')\r\n\t\t\t\tconst register = createElement('a', 'aied-workshops__dialog-register')\r\n\t\t\t\tregister.href = workshop.link\r\n\t\t\t\tregister.append(\r\n\t\t\t\t\tdocument.createTextNode(copy.register),\r\n\t\t\t\t\tdocument.createTextNode(' \u2192')\r\n\t\t\t\t)\r\n\t\t\t\tfooter.append(register)\r\n\t\t\t\tfragment.append(footer)\r\n\t\t\t}\r\n\t\t\tdialogContentElement.replaceChildren(fragment)\r\n\t\t\tdialogElement.setAttribute('aria-labelledby', title.id)\r\n\t\t\treturn closeButton\r\n\t\t}\r\n\r\n\t\tconst openDialog = (workshop, trigger) => {\r\n\t\t\tdialogTrigger = trigger\r\n\t\t\tconst closeButton = renderDialog(workshop)\r\n\t\t\tdialogElement.showModal()\r\n\t\t\tcloseButton.focus()\r\n\t\t}\r\n\r\n\t\tconst renderTabs = (dates) => {\r\n\t\t\ttabsElement.replaceChildren()\r\n\t\t\t;[\r\n\t\t\t\t{ value: 'all', label: copy.all },\r\n\t\t\t\t...dates.map((date, dayIndex) => ({\r\n\t\t\t\t\tvalue: date,\r\n\t\t\t\t\tlabel: formatTabDate(date, dayIndex)\r\n\t\t\t\t}))\r\n\t\t\t].forEach((item) => {\r\n\t\t\t\tconst button = createElement(\r\n\t\t\t\t\t'button',\r\n\t\t\t\t\t'aied-workshops__tab',\r\n\t\t\t\t\titem.label\r\n\t\t\t\t)\r\n\t\t\t\tbutton.type = 'button'\r\n\t\t\t\tbutton.dataset.date = item.value\r\n\t\t\t\tbutton.setAttribute('role', 'tab')\r\n\t\t\t\tbutton.setAttribute(\r\n\t\t\t\t\t'aria-selected',\r\n\t\t\t\t\tString(item.value === selectedDate)\r\n\t\t\t\t)\r\n\t\t\t\tbutton.tabIndex = item.value === selectedDate ? 0 : -1\r\n\t\t\t\ttabsElement.append(button)\r\n\t\t\t})\r\n\t\t}\r\n\r\n\t\tconst renderState = (type, showClear = false) => {\r\n\t\t\tconst state = createElement('div', 'aied-workshops__state')\r\n\t\t\tif (type === 'loading') {\r\n\t\t\t\tstate.append(\r\n\t\t\t\t\tcreateElement('span', 'aied-workshops__loader'),\r\n\t\t\t\t\tcreateElement('p', 'aied-workshops__state-copy', copy.loading)\r\n\t\t\t\t)\r\n\t\t\t} else if (type === 'filter') {\r\n\t\t\t\tstate.append(\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'div',\r\n\t\t\t\t\t\t'aied-workshops__state-kicker',\r\n\t\t\t\t\t\tcopy.filterKicker\r\n\t\t\t\t\t),\r\n\t\t\t\t\tcreateElement('p', 'aied-workshops__state-title', copy.filterTitle),\r\n\t\t\t\t\tcreateElement('p', 'aied-workshops__state-copy', copy.filterCopy)\r\n\t\t\t\t)\r\n\t\t\t\tif (showClear) {\r\n\t\t\t\t\tconst clearButton = createElement(\r\n\t\t\t\t\t\t'button',\r\n\t\t\t\t\t\t'aied-workshops__clear',\r\n\t\t\t\t\t\t`${copy.clear} \u2192`\r\n\t\t\t\t\t)\r\n\t\t\t\t\tclearButton.type = 'button'\r\n\t\t\t\t\tclearButton.addEventListener('click', clearFilters)\r\n\t\t\t\t\tstate.append(clearButton)\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tstate.classList.add('aied-workshops__state--coming-soon')\r\n\t\t\t\tconst content = createElement(\r\n\t\t\t\t\t'div',\r\n\t\t\t\t\t'aied-workshops__coming-soon-content'\r\n\t\t\t\t)\r\n\t\t\t\tcontent.append(\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'span',\r\n\t\t\t\t\t\t'aied-workshops__coming-soon-kicker',\r\n\t\t\t\t\t\tcopy.comingSoonKicker\r\n\t\t\t\t\t),\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'h2',\r\n\t\t\t\t\t\t'aied-workshops__coming-soon-title',\r\n\t\t\t\t\t\tcopy.comingSoonTitle\r\n\t\t\t\t\t),\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'p',\r\n\t\t\t\t\t\t'aied-workshops__coming-soon-copy',\r\n\t\t\t\t\t\tcopy.comingSoonCopy\r\n\t\t\t\t\t),\r\n\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t'span',\r\n\t\t\t\t\t\t'aied-workshops__coming-soon-label',\r\n\t\t\t\t\t\tcopy.comingSoonLabel\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t\tstate.append(content)\r\n\t\t\t}\r\n\t\t\tgroupsElement.replaceChildren(state)\r\n\t\t}\r\n\r\n\t\t\/\/ Shared by the \"no workshops\" and \"request failed\" paths: both show the\r\n\t\t\/\/ Coming Soon card, so the day tabs are dropped and the toolbar keeps only\r\n\t\t\/\/ the search box (see .aied-workshops--empty in page.css).\r\n\t\tconst renderComingSoon = () => {\r\n\t\t\troot.classList.add('aied-workshops--empty')\r\n\t\t\ttabsElement.replaceChildren()\r\n\t\t\trenderState('empty')\r\n\t\t\tstatusElement.textContent = ''\r\n\t\t}\r\n\r\n\t\tconst searchableText = (workshop) =>\r\n\t\t\t[\r\n\t\t\t\tworkshop.title,\r\n\t\t\t\tworkshop.description,\r\n\t\t\t\tworkshop.venue,\r\n\t\t\t\t...workshop.languages.flatMap((value) => [value, copy[value] || '']),\r\n\t\t\t\t...workshop.speakers.flatMap((speaker) => [\r\n\t\t\t\t\tspeaker.name,\r\n\t\t\t\t\tspeaker.organisation\r\n\t\t\t\t])\r\n\t\t\t]\r\n\t\t\t\t.join(' ')\r\n\t\t\t\t.toLocaleLowerCase(locale)\r\n\r\n\t\tconst render = () => {\r\n\t\t\t\/\/ The search box stays visible while the Coming Soon card is up, so a\r\n\t\t\t\/\/ keystroke must not swap that card for the no-results state.\r\n\t\t\tif (!workshops.length) {\r\n\t\t\t\trenderComingSoon()\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\t\t\troot.classList.remove('aied-workshops--empty')\r\n\t\t\tconst dates = [\r\n\t\t\t\t...new Set(workshops.map((workshop) => workshop.date).filter(Boolean))\r\n\t\t\t]\r\n\t\t\trenderTabs(dates)\r\n\t\t\tconst normalizedTerm = searchTerm.trim().toLocaleLowerCase(locale)\r\n\t\t\tconst filtered = workshops.filter(\r\n\t\t\t\t(workshop) =>\r\n\t\t\t\t\t(selectedDate === 'all' || workshop.date === selectedDate) &&\r\n\t\t\t\t\t(!normalizedTerm || searchableText(workshop).includes(normalizedTerm))\r\n\t\t\t)\r\n\t\t\tif (!filtered.length) {\r\n\t\t\t\trenderState('filter', true)\r\n\t\t\t\tstatusElement.textContent = copy.filterTitle\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tconst fragment = document.createDocumentFragment()\r\n\t\t\tdates\r\n\t\t\t\t.filter((date) => filtered.some((workshop) => workshop.date === date))\r\n\t\t\t\t.forEach((date) => {\r\n\t\t\t\t\tconst dayIndex = dates.indexOf(date)\r\n\t\t\t\t\tconst dayWorkshops = filtered.filter(\r\n\t\t\t\t\t\t(workshop) => workshop.date === date\r\n\t\t\t\t\t)\r\n\t\t\t\t\tconst section = createElement('section', 'aied-workshops__day')\r\n\t\t\t\t\tconst heading = createElement('div', 'aied-workshops__day-heading')\r\n\t\t\t\t\theading.append(\r\n\t\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t\t'h2',\r\n\t\t\t\t\t\t\t'aied-workshops__day-title',\r\n\t\t\t\t\t\t\tformatDayTitle(date, dayIndex)\r\n\t\t\t\t\t\t),\r\n\t\t\t\t\t\tcreateElement('span', 'aied-workshops__day-line'),\r\n\t\t\t\t\t\tcreateElement(\r\n\t\t\t\t\t\t\t'span',\r\n\t\t\t\t\t\t\t'aied-workshops__day-count',\r\n\t\t\t\t\t\t\t`${dayWorkshops.length} ${copy.workshops}`\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t)\r\n\t\t\t\t\tconst grid = createElement('div', 'aied-workshops__grid')\r\n\t\t\t\t\tdayWorkshops.forEach((workshop) => grid.append(createCard(workshop)))\r\n\t\t\t\t\tsection.append(heading, grid)\r\n\t\t\t\t\tfragment.append(section)\r\n\t\t\t\t})\r\n\t\t\tgroupsElement.replaceChildren(fragment)\r\n\t\t\tstatusElement.textContent = ''\r\n\t\t}\r\n\r\n\t\tfunction clearFilters() {\r\n\t\t\tselectedDate = 'all'\r\n\t\t\tsearchTerm = ''\r\n\t\t\tsearchElement.value = ''\r\n\t\t\trender()\r\n\t\t}\r\n\r\n\t\tgroupsElement.addEventListener('click', (event) => {\r\n\t\t\tconst button = event.target.closest('[data-workshop-id]')\r\n\t\t\tif (!button) return\r\n\t\t\tconst workshop = workshops.find(\r\n\t\t\t\t(item) => item.id === button.dataset.workshopId\r\n\t\t\t)\r\n\t\t\tif (workshop) openDialog(workshop, button)\r\n\t\t})\r\n\r\n\t\tdialogElement.addEventListener('click', (event) => {\r\n\t\t\tif (event.target === dialogElement) dialogElement.close()\r\n\t\t})\r\n\r\n\t\tdialogElement.addEventListener('close', () => {\r\n\t\t\tif (dialogTrigger && document.contains(dialogTrigger))\r\n\t\t\t\tdialogTrigger.focus()\r\n\t\t\tdialogTrigger = null\r\n\t\t})\r\n\r\n\t\ttabsElement.addEventListener('click', (event) => {\r\n\t\t\tconst button = event.target.closest('[data-date]')\r\n\t\t\tif (!button) return\r\n\t\t\tselectedDate = button.dataset.date\r\n\t\t\trender()\r\n\t\t})\r\n\r\n\t\ttabsElement.addEventListener('keydown', (event) => {\r\n\t\t\tif (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key))\r\n\t\t\t\treturn\r\n\t\t\tconst tabs = [...tabsElement.querySelectorAll('[role=\"tab\"]')]\r\n\t\t\tconst currentIndex = tabs.indexOf(document.activeElement)\r\n\t\t\tlet nextIndex =\r\n\t\t\t\tevent.key === 'Home'\r\n\t\t\t\t\t? 0\r\n\t\t\t\t\t: event.key === 'End'\r\n\t\t\t\t\t\t? tabs.length - 1\r\n\t\t\t\t\t\t: currentIndex + (event.key === 'ArrowRight' ? 1 : -1)\r\n\t\t\tnextIndex = (nextIndex + tabs.length) % tabs.length\r\n\t\t\tevent.preventDefault()\r\n\t\t\ttabs[nextIndex].click()\r\n\t\t\ttabsElement.querySelector('[aria-selected=\"true\"]').focus()\r\n\t\t})\r\n\r\n\t\tsearchElement.addEventListener('input', () => {\r\n\t\t\tclearTimeout(searchTimer)\r\n\t\t\tsearchTimer = setTimeout(() => {\r\n\t\t\t\tsearchTerm = searchElement.value\r\n\t\t\t\trender()\r\n\t\t\t}, 180)\r\n\t\t})\r\n\r\n\t\trenderState('loading')\r\n\t\t\/\/ 'event' is only sent on an event page, so the plain workshops page keeps\r\n\t\t\/\/ asking for the full list and every event gets its own cache entry below.\r\n\t\tconst endpointParams = new URLSearchParams({ lang: language })\r\n\t\tif (eventSlug) endpointParams.set('event', eventSlug)\r\n\t\tconst endpoint = `\/wp-json\/aied\/workshops\/list?${endpointParams.toString()}`\r\n\t\twindow.__aiedWorkshopRequests =\r\n\t\t\twindow.__aiedWorkshopRequests || Object.create(null)\r\n\t\tif (!window.__aiedWorkshopRequests[endpoint]) {\r\n\t\t\twindow.__aiedWorkshopRequests[endpoint] = fetch(endpoint, {\r\n\t\t\t\tcredentials: 'same-origin'\r\n\t\t\t})\r\n\t\t\t\t.then((response) => {\r\n\t\t\t\t\tif (!response.ok)\r\n\t\t\t\t\t\tthrow new Error(`Workshop request failed: ${response.status}`)\r\n\t\t\t\t\treturn response.json()\r\n\t\t\t\t})\r\n\t\t\t\t.then((data) => {\r\n\t\t\t\t\tif (!Array.isArray(data))\r\n\t\t\t\t\t\tthrow new TypeError('Workshop response must be an array.')\r\n\t\t\t\t\treturn data\r\n\t\t\t\t})\r\n\t\t}\r\n\r\n\t\twindow.__aiedWorkshopRequests[endpoint]\r\n\t\t\t.then((data) => {\r\n\t\t\t\tworkshops = data\r\n\t\t\t\t\t.map(normalize)\r\n\t\t\t\t\t.filter((workshop) => workshop.date && parseDate(workshop.date))\r\n\t\t\t\tworkshops.sort((first, second) =>\r\n\t\t\t\t\t`${first.date} ${first.startTime}`.localeCompare(\r\n\t\t\t\t\t\t`${second.date} ${second.startTime}`\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t\tif (!workshops.length) {\r\n\t\t\t\t\trenderComingSoon()\r\n\t\t\t\t\treturn\r\n\t\t\t\t}\r\n\t\t\t\trender()\r\n\t\t\t})\r\n\t\t\t.catch(renderComingSoon)\r\n\t})()\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>home \/ workshops Workshops Hands-on practical experience with AI-powered educational solutions, and how they work in the classroom.<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":20352,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-20386","page","type-page","status-publish","hentry"],"pp_statuses_selecting_workflow":false,"pp_workflow_action":"current","pp_status_selection":"publish","acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Workshops - AIED - EdCity<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/web.edcity.hk\/en\/aied\/workshops\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Workshops - AIED - EdCity\" \/>\n<meta property=\"og:description\" content=\"home \/ workshops Workshops Hands-on practical experience with AI-powered educational solutions, and how they work in the classroom.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/web.edcity.hk\/en\/aied\/workshops\/\" \/>\n<meta property=\"og:site_name\" content=\"EdCity\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-29T10:18:12+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2026\/09\/aied-watermark.png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/\",\"url\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/\",\"name\":\"Workshops - AIED - EdCity\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/campaignprodstor.blob.core.windows.net\\\/uploads\\\/2026\\\/09\\\/aied-watermark.png\",\"datePublished\":\"2026-09-17T07:04:00+00:00\",\"dateModified\":\"2026-10-29T10:18:12+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/#primaryimage\",\"url\":\"https:\\\/\\\/campaignprodstor.blob.core.windows.net\\\/uploads\\\/2026\\\/09\\\/aied-watermark.png\",\"contentUrl\":\"https:\\\/\\\/campaignprodstor.blob.core.windows.net\\\/uploads\\\/2026\\\/09\\\/aied-watermark.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/workshops\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"AIED\",\"item\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/aied\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Workshops\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/\",\"name\":\"\u9999\u6e2f\u6559\u80b2\u57ce\",\"description\":\"EdCity\",\"publisher\":{\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/#organization\",\"name\":\"EdCity\",\"url\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/campaignprodstor.blob.core.windows.net\\\/uploads\\\/2025\\\/02\\\/hkedcity_logo.svg\",\"contentUrl\":\"https:\\\/\\\/campaignprodstor.blob.core.windows.net\\\/uploads\\\/2025\\\/02\\\/hkedcity_logo.svg\",\"width\":252,\"height\":94,\"caption\":\"EdCity\"},\"image\":{\"@id\":\"https:\\\/\\\/web.edcity.hk\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Workshops - AIED - EdCity","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/","og_locale":"en_US","og_type":"article","og_title":"Workshops - AIED - EdCity","og_description":"home \/ workshops Workshops Hands-on practical experience with AI-powered educational solutions, and how they work in the classroom.","og_url":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/","og_site_name":"EdCity","article_modified_time":"2026-10-29T10:18:12+00:00","og_image":[{"url":"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2026\/09\/aied-watermark.png","type":"","width":"","height":""}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/","url":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/","name":"Workshops - AIED - EdCity","isPartOf":{"@id":"https:\/\/web.edcity.hk\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/#primaryimage"},"image":{"@id":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/#primaryimage"},"thumbnailUrl":"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2026\/09\/aied-watermark.png","datePublished":"2026-09-17T07:04:00+00:00","dateModified":"2026-10-29T10:18:12+00:00","breadcrumb":{"@id":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/web.edcity.hk\/en\/aied\/workshops\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/#primaryimage","url":"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2026\/09\/aied-watermark.png","contentUrl":"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2026\/09\/aied-watermark.png"},{"@type":"BreadcrumbList","@id":"https:\/\/web.edcity.hk\/en\/aied\/workshops\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/web.edcity.hk\/en\/"},{"@type":"ListItem","position":2,"name":"AIED","item":"https:\/\/web.edcity.hk\/en\/aied\/"},{"@type":"ListItem","position":3,"name":"Workshops"}]},{"@type":"WebSite","@id":"https:\/\/web.edcity.hk\/en\/#website","url":"https:\/\/web.edcity.hk\/en\/","name":"\u9999\u6e2f\u6559\u80b2\u57ce","description":"EdCity","publisher":{"@id":"https:\/\/web.edcity.hk\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/web.edcity.hk\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/web.edcity.hk\/en\/#organization","name":"EdCity","url":"https:\/\/web.edcity.hk\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/web.edcity.hk\/en\/#\/schema\/logo\/image\/","url":"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2025\/02\/hkedcity_logo.svg","contentUrl":"https:\/\/campaignprodstor.blob.core.windows.net\/uploads\/2025\/02\/hkedcity_logo.svg","width":252,"height":94,"caption":"EdCity"},"image":{"@id":"https:\/\/web.edcity.hk\/en\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/pages\/20386","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/comments?post=20386"}],"version-history":[{"count":168,"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/pages\/20386\/revisions"}],"predecessor-version":[{"id":22042,"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/pages\/20386\/revisions\/22042"}],"up":[{"embeddable":true,"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/pages\/20352"}],"wp:attachment":[{"href":"https:\/\/web.edcity.hk\/en\/wp-json\/wp\/v2\/media?parent=20386"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}