{"id":33252,"date":"2026-06-29T02:11:54","date_gmt":"2026-06-29T02:11:54","guid":{"rendered":"https:\/\/ukschoolsearch.ai\/?page_id=33252"},"modified":"2026-06-29T02:41:23","modified_gmt":"2026-06-29T02:41:23","slug":"newsletteren","status":"publish","type":"page","link":"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/","title":{"rendered":"NewsletterEn"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"33252\" class=\"elementor elementor-33252\" data-elementor-settings=\"{&quot;ha_cmc_init_switcher&quot;:&quot;no&quot;}\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-8680d89 e-con-full e-flex wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"8680d89\" data-element_type=\"container\" data-settings=\"{&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2f39506 elementor-widget elementor-widget-html\" data-id=\"2f39506\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>Newsletter - UK School Search<\/title>\r\n    <style>\r\n        .ns-app-wrapper * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n            -webkit-tap-highlight-color: transparent;\r\n        }\r\n\r\n        body {\r\n            font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\r\n            background-color: #f5f7fa;\r\n            color: #001733;\r\n            line-height: 1.6;\r\n        }\r\n\r\n        .ns-main-container {\r\n            max-width: 1000px;\r\n            margin: 0 auto;\r\n            padding: 0;\r\n        }\r\n\r\n        .ns-toolbar {\r\n            width: 96%;\r\n            margin: 24px auto 20px auto;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n            gap: 16px;\r\n        }\r\n\r\n        .ns-search-box {\r\n            flex: 1;\r\n            height: 48px;\r\n            background: #fff;\r\n            border-radius: 24px;\r\n            display: flex;\r\n            align-items: center;\r\n            padding: 0 20px;\r\n            gap: 12px;\r\n            box-shadow: 0 4px 12px rgba(0,0,0,0.06);\r\n            transition: box-shadow 0.3s;\r\n            border: none !important;\r\n        }\r\n\r\n        .ns-search-box:focus-within {\r\n            box-shadow: 0 6px 16px rgba(0,0,0,0.1);\r\n        }\r\n\r\n        .ns-search-icon {\r\n            width: 18px;\r\n            height: 18px;\r\n            color: #999;\r\n            flex-shrink: 0;\r\n        }\r\n\r\n        .ns-input-field {\r\n            flex: 1;\r\n            height: 100%;\r\n            width: 100%;\r\n            font-size: 15px;\r\n            color: #001733;\r\n            border: none !important;\r\n            outline: none !important;\r\n            background: transparent !important;\r\n            box-shadow: none !important;\r\n            -webkit-appearance: none;\r\n            appearance: none;\r\n        }\r\n\r\n        .ns-input-field::-webkit-search-decoration,\r\n        .ns-input-field::-webkit-search-cancel-button,\r\n        .ns-input-field::-webkit-search-results-button,\r\n        .ns-input-field::-webkit-search-results-decoration {\r\n            -webkit-appearance: none;\r\n        }\r\n\r\n        .ns-filter-btn {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 6px;\r\n            padding: 10px 16px;\r\n            background: #fff;\r\n            border-radius: 24px;\r\n            box-shadow: 0 4px 12px rgba(0,0,0,0.06);\r\n            cursor: pointer;\r\n            transition: all 0.2s;\r\n            border: none !important;\r\n        }\r\n\r\n        .ns-filter-btn:hover {\r\n            background-color: #fcfcfc;\r\n            transform: translateY(-1px);\r\n        }\r\n\r\n        .ns-filter-text { font-size: 14px; color: #45484D; font-weight: 500; }\r\n\r\n        .ns-card-list {\r\n            width: 96%;\r\n            margin: 0 auto;\r\n            padding-bottom: 40px;\r\n        }\r\n\r\n        .ns-card {\r\n            position: relative;\r\n            display: flex;\r\n            align-items: center;\r\n            background: #F1F5F7;\r\n            border-radius: 12px;\r\n            margin-bottom: 18px;\r\n            padding: 12px;\r\n            overflow: hidden;\r\n            cursor: pointer;\r\n            text-decoration: none;\r\n            transition: opacity 0.2s ease;\r\n        }\r\n        .ns-card:active { opacity: 0.9; }\r\n\r\n        .ns-card-img-wrap {\r\n            width: 33%;\r\n            flex-shrink: 0;\r\n            aspect-ratio: 4 \/ 3;\r\n            border-radius: 10px;\r\n            overflow: hidden;\r\n            position: relative;\r\n        }\r\n        .ns-card-img {\r\n            width: 100%;\r\n            height: 100%;\r\n            object-fit: cover;\r\n            display: block;\r\n            background: #f0f0f0;\r\n        }\r\n\r\n        .ns-card-content {\r\n            flex: 1;\r\n            min-width: 0;\r\n            padding-left: 14px;\r\n            padding-top: 24px;\r\n            display: flex;\r\n            flex-direction: column;\r\n            justify-content: center;\r\n        }\r\n\r\n        .ns-card-header {\r\n            display: flex;\r\n            align-items: baseline;\r\n            margin-bottom: 6px;\r\n        }\r\n\r\n        .ns-card-number {\r\n            font-family: \"DinAlternate\", \"Courier New\", monospace;\r\n            font-size: 26px;\r\n            font-weight: 700;\r\n            color: #001733;\r\n            line-height: 1;\r\n        }\r\n\r\n        .ns-card-date {\r\n            font-size: 15px;\r\n            color: #808080;\r\n            margin-left: 8px;\r\n        }\r\n\r\n        .ns-card-title {\r\n            font-size: 14px;\r\n            font-weight: 700;\r\n            color: #1a1a1a;\r\n            margin-bottom: 4px;\r\n            white-space: nowrap;\r\n            overflow: hidden;\r\n            text-overflow: ellipsis;\r\n        }\r\n\r\n        .ns-card-body {\r\n            font-size: 16px;\r\n            line-height: 26px;\r\n            color: #001733;\r\n            display: -webkit-box;\r\n            -webkit-line-clamp: 4;\r\n            -webkit-box-orient: vertical;\r\n            overflow: hidden;\r\n        }\r\n\r\n        .ns-badge {\r\n            position: absolute;\r\n            top: 0;\r\n            right: 0;\r\n            padding: 5px 12px;\r\n            border-top-right-radius: 12px;\r\n            border-bottom-left-radius: 8px;\r\n            color: #fff;\r\n            font-size: 15px;\r\n            font-weight: 500;\r\n            white-space: nowrap;\r\n            z-index: 2;\r\n        }\r\n        .ns-grad-1 { background: #1a3a8f; }\r\n        .ns-grad-2 { background: #d32f2f; }\r\n\r\n        .ns-sk-wrapper { width: 96%; margin: 0 auto; }\r\n        .ns-sk-card {\r\n            background: #F1F5F7;\r\n            border-radius: 12px;\r\n            margin-bottom: 18px;\r\n            padding: 12px;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 14px;\r\n        }\r\n        .ns-sk-img {\r\n            width: 33%;\r\n            flex-shrink: 0;\r\n            aspect-ratio: 4 \/ 3;\r\n            background: #e0e0e0;\r\n            border-radius: 10px;\r\n        }\r\n        .ns-sk-info { flex: 1; display: flex; flex-direction: column; gap: 10px; padding-top: 14px; }\r\n        .ns-sk-line { height: 14px; background: #e0e0e0; border-radius: 4px; }\r\n        .ns-sk-line.short { width: 40%; height: 18px; }\r\n        .ns-sk-line.long { width: 90%; }\r\n        .ns-sk-img, .ns-sk-line {\r\n            background: linear-gradient(90deg, #f0f2f5 25%, #f9fafb 37%, #f0f2f5 63%);\r\n            background-size: 400% 100%; animation: ns-sk-loading 1.4s ease infinite;\r\n        }\r\n        @keyframes ns-sk-loading { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }\r\n\r\n        .ns-empty { text-align: center; padding: 80px 20px; color: #999; font-size: 16px; }\r\n        .ns-error { background: #fff3cd; border: 1px solid #ffc107; color: #856404; padding: 12px; border-radius: 8px; margin: 16px auto; width: 96%; display: none; text-align: center; }\r\n        .ns-error.active { display: block; }\r\n\r\n        .loading { display: none; }\r\n\r\n        @media (min-width: 1025px) {\r\n            .ns-toolbar { max-width: 720px; }\r\n            .ns-card-list,\r\n            .ns-sk-wrapper,\r\n            .ns-error { max-width: 720px; }\r\n\r\n            .ns-card { padding: 10px; margin-bottom: 14px; }\r\n            .ns-card-img-wrap { width: 26%; }\r\n            .ns-card-content { padding-left: 16px; padding-top: 22px; }\r\n            .ns-card-number { font-size: 22px; }\r\n            .ns-card-date { font-size: 14px; }\r\n            .ns-card-body { font-size: 15px; line-height: 23px; -webkit-line-clamp: 3; }\r\n            .ns-badge { font-size: 14px; padding: 4px 10px; }\r\n\r\n            .ns-sk-card { padding: 10px; margin-bottom: 14px; }\r\n            .ns-sk-img { width: 26%; }\r\n        }\r\n\r\n        @media (max-width: 768px) {\r\n            .ns-main-container { max-width: 100%; }\r\n            .ns-toolbar { width: 94%; gap: 10px; margin: 16px auto; }\r\n            .ns-search-box { height: 40px; padding: 0 16px; }\r\n            .ns-card-list { width: 94%; }\r\n            .ns-sk-wrapper { width: 94%; }\r\n            .ns-card-number { font-size: 18px; }\r\n            .ns-card-date { font-size: 11px; }\r\n            .ns-card-body { font-size: 12px; line-height: 18px; -webkit-line-clamp: 4; }\r\n            .ns-badge { font-size: 11px; padding: 3px 8px; }\r\n        }\r\n    <\/style>\r\n<\/head>\r\n<body class=\"ns-app-wrapper\">\r\n\r\n    <div class=\"ns-main-container\">\r\n        <div class=\"ns-error\" id=\"errorMessage\"><\/div>\r\n\r\n        <div class=\"ns-toolbar\">\r\n            <div class=\"ns-search-box\">\r\n                <svg class=\"ns-search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\">\r\n                    <circle cx=\"11\" cy=\"11\" r=\"8\"><\/circle>\r\n                    <path d=\"m21 21-4.35-4.35\"><\/path>\r\n                <\/svg>\r\n                <input\r\n                    type=\"text\"\r\n                    class=\"ns-input-field\"\r\n                    id=\"searchInput\"\r\n                    placeholder=\"Search keywords\"\r\n                    autocomplete=\"off\"\r\n                >\r\n            <\/div>\r\n\r\n            <div class=\"ns-filter-btn\" id=\"filterButton\">\r\n                <span class=\"ns-filter-text\" id=\"filterText\">Latest<\/span>\r\n                <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#45484D\">\r\n                    <path d=\"M3 6h18M7 12h10M11 18h2\" stroke-width=\"2\" stroke-linecap=\"round\"\/>\r\n                <\/svg>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"ns-sk-wrapper\" id=\"skeletonWrapper\"><\/div>\r\n\r\n        <div class=\"ns-card-list\" id=\"newsletterList\" style=\"display: none;\"><\/div>\r\n\r\n        <div class=\"ns-empty\" id=\"emptyContainer\" style=\"display: none;\">\r\n            <div class=\"empty-text\">No content<\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <script>\r\n        const WORDPRESS_API_BASE = 'https:\/\/ukschoolsearch.ai\/wp-json\/wp\/v2';\r\n        \/\/ \u82f1\u6587\u7248 CPT slug\r\n        const POST_TYPE = 'newsletteren';\r\n        \/\/ \u82f1\u6587\u8be6\u60c5\u9875 slug \u2014\u2014 \u5982\u4e0d\u662f\u8fd9\u4e2a\uff0c\u6539\u8fd9\u91cc\u5373\u53ef\r\n        const DETAIL_PAGE = 'newsletteren-detail';\r\n\r\n        const GRADIENT_CONFIG = {\r\n            1: ['#1F68F9', '#185DE7', '#0F4FD0'],\r\n            2: ['#FF3F32', '#E41700']\r\n        };\r\n\r\n        const PLACEHOLDER_IMG = 'https:\/\/via.placeholder.com\/400x400\/1F68F9\/FFFFFF?text=Newsletter';\r\n\r\n        let newsletters = [];\r\n        let sortAsc = false;\r\n        let searchText = '';\r\n\r\n        function initSkeleton() {\r\n            const wrapper = document.getElementById('skeletonWrapper');\r\n            let html = '';\r\n            for(let i=0; i<6; i++) {\r\n                html += `\r\n                <div class=\"ns-sk-card\">\r\n                    <div class=\"ns-sk-img\"><\/div>\r\n                    <div class=\"ns-sk-info\">\r\n                        <div class=\"ns-sk-line short\"><\/div>\r\n                        <div class=\"ns-sk-line long\"><\/div>\r\n                        <div class=\"ns-sk-line\"><\/div>\r\n                        <div class=\"ns-sk-line\"><\/div>\r\n                    <\/div>\r\n                <\/div>`;\r\n            }\r\n            wrapper.innerHTML = html;\r\n        }\r\n\r\n        function toggleLoading(show) {\r\n            const skeleton = document.getElementById('skeletonWrapper');\r\n            const list = document.getElementById('newsletterList');\r\n            const empty = document.getElementById('emptyContainer');\r\n\r\n            if (show) {\r\n                skeleton.style.display = 'block';\r\n                list.style.display = 'none';\r\n                empty.style.display = 'none';\r\n            } else {\r\n                skeleton.style.display = 'none';\r\n            }\r\n        }\r\n\r\n        function formatShortMonthDate(dateString) {\r\n            const date = new Date(dateString);\r\n            if (isNaN(date.getTime())) return dateString || '';\r\n            const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\r\n            return `${months[date.getMonth()]} ${date.getDate()}`;\r\n        }\r\n\r\n        function extractExcerpt(htmlContent) {\r\n            const textContent = htmlContent.replace(\/<[^>]*>\/g, '');\r\n            return textContent.length > 150\r\n                ? textContent.substring(0, 150) + '...'\r\n                : textContent;\r\n        }\r\n\r\n        function extractContentImages(content) {\r\n            const images = [];\r\n            try {\r\n                const imgRegex = \/<img[^>]+>\/gi;\r\n                const tags = content.match(imgRegex) || [];\r\n                for (const tag of tags) {\r\n                    const srcMatch = tag.match(\/src=[\"']([^\"']+)[\"']\/i);\r\n                    if (srcMatch && srcMatch[1]) {\r\n                        const widthMatch = tag.match(\/width=[\"']?(\\d+)[\"']?\/i);\r\n                        const heightMatch = tag.match(\/height=[\"']?(\\d+)[\"']?\/i);\r\n                        images.push({\r\n                            url: srcMatch[1],\r\n                            width: widthMatch ? parseInt(widthMatch[1]) : 750,\r\n                            height: heightMatch ? parseInt(heightMatch[1]) : 1000\r\n                        });\r\n                    }\r\n                }\r\n            } catch (e) {\r\n                console.error('\u63d0\u53d6\u5185\u5bb9\u56fe\u7247\u65f6\u51fa\u9519:', e);\r\n            }\r\n            return images;\r\n        }\r\n\r\n        function computeContentSig(imgs) {\r\n            const raw = imgs.map(i => `${i.url}|${i.width}|${i.height}`).join('||');\r\n            let h = 0;\r\n            for (let i = 0; i < raw.length; i++) {\r\n                h = (h * 31 + raw.charCodeAt(i)) | 0;\r\n            }\r\n            return String(h);\r\n        }\r\n\r\n        function showError(message) {\r\n            const errorEl = document.getElementById('errorMessage');\r\n            errorEl.textContent = message;\r\n            errorEl.classList.add('active');\r\n            setTimeout(() => errorEl.classList.remove('active'), 5000);\r\n        }\r\n\r\n        \/* ============================================================\r\n           IMAGE \u517c\u5bb9\u6838\u5fc3\uff1a\r\n           acf.image \u53ef\u80fd\u662f URL \u5b57\u7b26\u4e32\uff0c\u4e5f\u53ef\u80fd\u662f\u9644\u4ef6\u6570\u5b57 ID\u3002\r\n           - \u5b57\u7b26\u4e32\u4e14\u4ee5 http \u5f00\u5934 \u2192 \u76f4\u63a5\u7528\r\n           - \u7eaf\u6570\u5b57 \/ \u6570\u5b57\u5b57\u7b26\u4e32 \u2192 \u89c6\u4e3a\u9644\u4ef6 ID\uff0c\u6279\u91cf\u67e5 \/media\/{id} \u53d6 URL\r\n           ============================================================ *\/\r\n\r\n        \/\/ \u5224\u65ad\u662f\u5426\u4e3a\"\u6570\u5b57ID\"\uff08\u6570\u5b57\uff0c\u6216\u7eaf\u6570\u5b57\u5b57\u7b26\u4e32\uff09\r\n        function isMediaId(val) {\r\n            if (typeof val === 'number') return val > 0;\r\n            if (typeof val === 'string') return \/^\\d+$\/.test(val.trim());\r\n            return false;\r\n        }\r\n\r\n        \/\/ \u53d6 media \u7aef\u70b9\u62ff\u771f\u5b9e URL\uff1b\u5931\u8d25\u8fd4\u56de null\r\n        async function fetchMediaUrl(id) {\r\n            try {\r\n                const res = await fetch(`${WORDPRESS_API_BASE}\/media\/${id}`);\r\n                if (!res.ok) return null;\r\n                const m = await res.json();\r\n                \/\/ \u4f18\u5148\u7528\u4e2d\u7b49\u5c3a\u5bf8\u7f29\u7565\u56fe\uff08\u5217\u8868\u5361\u7247\u591f\u7528\u3001\u66f4\u5feb\uff09\uff0c\u6ca1\u6709\u5219\u9000\u56de\u539f\u56fe\r\n                return (m?.media_details?.sizes?.medium?.source_url)\r\n                    || (m?.media_details?.sizes?.large?.source_url)\r\n                    || m?.source_url\r\n                    || null;\r\n            } catch (e) {\r\n                console.error('\u67e5 media \u5931\u8d25 id=', id, e);\r\n                return null;\r\n            }\r\n        }\r\n\r\n        \/\/ \u628a newsletters \u91cc\u6240\u6709\u6570\u5b57 ID \u7684 image \u6279\u91cf\u89e3\u6790\u6210 URL\r\n        async function resolveImageIds() {\r\n            \/\/ \u6536\u96c6\u6240\u6709\u9700\u8981\u89e3\u6790\u7684\u552f\u4e00 ID\r\n            const idSet = new Set();\r\n            newsletters.forEach(n => {\r\n                if (isMediaId(n.image)) idSet.add(String(n.image).trim());\r\n            });\r\n            if (idSet.size === 0) return;\r\n\r\n            const ids = Array.from(idSet);\r\n            const results = await Promise.all(ids.map(id => fetchMediaUrl(id)));\r\n\r\n            const idToUrl = {};\r\n            ids.forEach((id, i) => { idToUrl[id] = results[i]; });\r\n\r\n            newsletters.forEach(n => {\r\n                if (isMediaId(n.image)) {\r\n                    const url = idToUrl[String(n.image).trim()];\r\n                    n.image = url || PLACEHOLDER_IMG;\r\n                }\r\n            });\r\n        }\r\n\r\n        async function fetchNewsletters() {\r\n            try {\r\n                if(newsletters.length === 0) toggleLoading(true);\r\n\r\n                console.log('\u5f00\u59cb\u4ece WordPress \u83b7\u53d6 Newsletter (en)...');\r\n\r\n                let response = await fetch(`${WORDPRESS_API_BASE}\/${POST_TYPE}?per_page=100`);\r\n\r\n                if (response.status === 404) {\r\n                    response = await fetch(`${WORDPRESS_API_BASE}\/posts?post_type=${POST_TYPE}&per_page=100`);\r\n                }\r\n\r\n                if (response.status === 404) {\r\n                    const catRes = await fetch(`${WORDPRESS_API_BASE}\/categories?search=newsletter`);\r\n                    if (catRes.ok) {\r\n                        const cats = await catRes.json();\r\n                        if (Array.isArray(cats) && cats.length > 0) {\r\n                            const catId = cats[0].id;\r\n                            response = await fetch(`${WORDPRESS_API_BASE}\/posts?categories=${catId}&per_page=100`);\r\n                        }\r\n                    }\r\n                }\r\n\r\n                if (!response.ok) {\r\n                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);\r\n                }\r\n\r\n                const posts = await response.json();\r\n\r\n                if (!Array.isArray(posts)) {\r\n                    throw new Error('\u8fd4\u56de\u7684\u6570\u636e\u4e0d\u662f\u6570\u7ec4');\r\n                }\r\n\r\n                console.log('\u6210\u529f\u83b7\u53d6', posts.length, '\u6761 Newsletter');\r\n\r\n                newsletters = posts.map((post, index) => {\r\n                    \/\/ \u517c\u5bb9 acf \u4e3a\u7a7a\u6570\u7ec4 [] \u7684\u60c5\u51b5\r\n                    const acf = (post?.acf && !Array.isArray(post.acf)) ? post.acf : {};\r\n                    const idNum = typeof acf.id === 'number' ? acf.id : Number(post?.id) || index + 1;\r\n\r\n                    const parsedGradient = Number(acf.gradient);\r\n                    const safeGradient = (Number.isFinite(parsedGradient) && GRADIENT_CONFIG[parsedGradient])\r\n                        ? parsedGradient\r\n                        : 1;\r\n\r\n                    const contentImages = extractContentImages(post?.content?.rendered || '');\r\n                    const contentSig = computeContentSig(contentImages);\r\n\r\n                    return {\r\n                        key: acf.key || `wp${idNum}`,\r\n                        id: idNum,\r\n                        \/\/ \u7528\u771f\u5b9e post id \u505a\u8df3\u8f6c\uff0c\u786e\u4fdd\u8be6\u60c5\u9875\u80fd\u6309 id \u53d6\u5230\r\n                        postId: Number(post?.id) || idNum,\r\n                        number: acf.number || `No.${String(index + 1).padStart(3, '0')}`,\r\n                        title: acf.title || post?.title?.rendered || 'Untitled',\r\n                        date: acf.date || post?.date || new Date().toISOString(),\r\n                        badge: acf.badge || 'News',\r\n                        gradient: safeGradient,\r\n                        \/\/ \u5148\u539f\u6837\u5b58\uff08\u53ef\u80fd\u662f URL \u6216\u6570\u5b57 ID\uff09\uff0c\u7a0d\u540e resolveImageIds \u7edf\u4e00\u5904\u7406\r\n                        image: acf.image || PLACEHOLDER_IMG,\r\n                        body: acf.body || extractExcerpt(post?.excerpt?.rendered || post?.content?.rendered || ''),\r\n                        isRead: false,\r\n                        isDeleted: false,\r\n                        content: contentImages,\r\n                        contentSig: contentSig\r\n                    };\r\n                });\r\n\r\n                \/\/ \u5173\u952e\uff1a\u628a\u6570\u5b57 ID \u7684 image \u89e3\u6790\u6210\u771f\u5b9e URL\r\n                await resolveImageIds();\r\n\r\n                return true;\r\n\r\n            } catch (error) {\r\n                console.error('\u83b7\u53d6 Newsletter \u5931\u8d25:', error);\r\n                showError('Load failed: ' + error.message);\r\n                newsletters = [];\r\n                return false;\r\n            } finally {\r\n                toggleLoading(false);\r\n            }\r\n        }\r\n\r\n        function renderNewsletters() {\r\n            const keyword = searchText.trim().toLowerCase();\r\n            let list = newsletters.filter(n => !n.isDeleted);\r\n\r\n            if (keyword) {\r\n                list = list.filter(n => {\r\n                    const searchContent = [\r\n                        n.badge || '',\r\n                        n.date || '',\r\n                        n.body || '',\r\n                        n.number || '',\r\n                        n.title || ''\r\n                    ].join(' ').toLowerCase();\r\n                    return searchContent.includes(keyword);\r\n                });\r\n            }\r\n\r\n            list.sort((a, b) => sortAsc ? a.id - b.id : b.id - a.id);\r\n\r\n            const container = document.getElementById('newsletterList');\r\n            const emptyContainer = document.getElementById('emptyContainer');\r\n\r\n            if (list.length === 0) {\r\n                container.style.display = 'none';\r\n                emptyContainer.style.display = 'block';\r\n                emptyContainer.querySelector('.empty-text').textContent =\r\n                    keyword ? 'No matching content' : 'No content';\r\n            } else {\r\n                emptyContainer.style.display = 'none';\r\n                container.style.display = 'block';\r\n\r\n                container.innerHTML = list.map(item => `\r\n                    <div class=\"ns-card\" onclick=\"handleNewsletterClick(${item.postId})\">\r\n                        <div class=\"ns-card-img-wrap\">\r\n                            <img decoding=\"async\"\r\n                                src=\"${item.image}\"\r\n                                class=\"ns-card-img\"\r\n                                alt=\"${item.title}\"\r\n                                loading=\"lazy\"\r\n                                onerror=\"this.src='${PLACEHOLDER_IMG}'\"\r\n                            >\r\n                        <\/div>\r\n                        <div class=\"ns-card-content\">\r\n                            <div class=\"ns-card-header\">\r\n                                <span class=\"ns-card-number\">${item.number}<\/span>\r\n                                <span class=\"ns-card-date\">${formatShortMonthDate(item.date)}<\/span>\r\n                            <\/div>\r\n                            <div class=\"ns-card-body\">${item.body}<\/div>\r\n                        <\/div>\r\n                        <span class=\"ns-badge ns-grad-${item.gradient}\">${item.badge}<\/span>\r\n                    <\/div>\r\n                `).join('');\r\n            }\r\n        }\r\n\r\n       function handleNewsletterClick(postId) {\r\n    window.location.href = `https:\/\/ukschoolsearch.ai\/index.php\/newsletteren-detail\/?id=${postId}`;\r\n}\r\n\r\n        document.getElementById('filterButton').addEventListener('click', () => {\r\n            sortAsc = !sortAsc;\r\n            document.getElementById('filterText').textContent = sortAsc ? 'Oldest' : 'Latest';\r\n            renderNewsletters();\r\n        });\r\n\r\n        document.getElementById('searchInput').addEventListener('input', (e) => {\r\n            searchText = e.target.value;\r\n            renderNewsletters();\r\n        });\r\n\r\n        window.addEventListener('DOMContentLoaded', async () => {\r\n            initSkeleton();\r\n            await fetchNewsletters();\r\n            renderNewsletters();\r\n        });\r\n    <\/script>\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\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>Newsletter &#8211; UK School Search Latest No content<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-33252","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>NewsletterEn - SmartGen in UK<\/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:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"NewsletterEn - SmartGen in UK\" \/>\n<meta property=\"og:description\" content=\"Newsletter &#8211; UK School Search Latest No content\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/\" \/>\n<meta property=\"og:site_name\" content=\"SmartGen in UK\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-29T02:41:23+00:00\" \/>\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=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/\",\"url\":\"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/\",\"name\":\"NewsletterEn - SmartGen in UK\",\"isPartOf\":{\"@id\":\"https:\/\/ukschoolsearch.ai\/#website\"},\"datePublished\":\"2026-06-29T02:11:54+00:00\",\"dateModified\":\"2026-06-29T02:41:23+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ukschoolsearch.ai\/index.php\/new-home-page-english\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"NewsletterEn\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ukschoolsearch.ai\/#website\",\"url\":\"https:\/\/ukschoolsearch.ai\/\",\"name\":\"SmartGen in UK\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/ukschoolsearch.ai\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ukschoolsearch.ai\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ukschoolsearch.ai\/#organization\",\"name\":\"SmartGen in UK\",\"url\":\"https:\/\/ukschoolsearch.ai\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ukschoolsearch.ai\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ukschoolsearch.ai\/wp-content\/uploads\/2024\/06\/SmartGen_logo.png\",\"contentUrl\":\"https:\/\/ukschoolsearch.ai\/wp-content\/uploads\/2024\/06\/SmartGen_logo.png\",\"width\":213,\"height\":213,\"caption\":\"SmartGen in UK\"},\"image\":{\"@id\":\"https:\/\/ukschoolsearch.ai\/#\/schema\/logo\/image\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"NewsletterEn - SmartGen in UK","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:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/","og_locale":"en_US","og_type":"article","og_title":"NewsletterEn - SmartGen in UK","og_description":"Newsletter &#8211; UK School Search Latest No content","og_url":"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/","og_site_name":"SmartGen in UK","article_modified_time":"2026-06-29T02:41:23+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/","url":"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/","name":"NewsletterEn - SmartGen in UK","isPartOf":{"@id":"https:\/\/ukschoolsearch.ai\/#website"},"datePublished":"2026-06-29T02:11:54+00:00","dateModified":"2026-06-29T02:41:23+00:00","breadcrumb":{"@id":"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/ukschoolsearch.ai\/index.php\/newsletteren\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ukschoolsearch.ai\/index.php\/new-home-page-english\/"},{"@type":"ListItem","position":2,"name":"NewsletterEn"}]},{"@type":"WebSite","@id":"https:\/\/ukschoolsearch.ai\/#website","url":"https:\/\/ukschoolsearch.ai\/","name":"SmartGen in UK","description":"","publisher":{"@id":"https:\/\/ukschoolsearch.ai\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ukschoolsearch.ai\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ukschoolsearch.ai\/#organization","name":"SmartGen in UK","url":"https:\/\/ukschoolsearch.ai\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ukschoolsearch.ai\/#\/schema\/logo\/image\/","url":"https:\/\/ukschoolsearch.ai\/wp-content\/uploads\/2024\/06\/SmartGen_logo.png","contentUrl":"https:\/\/ukschoolsearch.ai\/wp-content\/uploads\/2024\/06\/SmartGen_logo.png","width":213,"height":213,"caption":"SmartGen in UK"},"image":{"@id":"https:\/\/ukschoolsearch.ai\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/pages\/33252","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/comments?post=33252"}],"version-history":[{"count":7,"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/pages\/33252\/revisions"}],"predecessor-version":[{"id":33267,"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/pages\/33252\/revisions\/33267"}],"wp:attachment":[{"href":"https:\/\/ukschoolsearch.ai\/index.php\/wp-json\/wp\/v2\/media?parent=33252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}