{"id":54,"date":"2016-12-13T16:36:43","date_gmt":"2016-12-13T14:36:43","guid":{"rendered":"http:\/\/blogs.sun.ac.za\/mod\/?page_id=54"},"modified":"2026-03-16T08:51:28","modified_gmt":"2026-03-16T06:51:28","slug":"news-and-gallery","status":"publish","type":"page","link":"https:\/\/blogs.sun.ac.za\/mod\/news-and-gallery\/","title":{"rendered":"Gallery"},"content":{"rendered":"<p><style>\r\n    \/* MASONRY GRID *\/\r\n    .mod-gallery-wrapper { column-count: 1; column-gap: 15px; }\r\n    @media (min-width: 600px) { .mod-gallery-wrapper { column-count: 2; } }\r\n    @media (min-width: 900px) { .mod-gallery-wrapper { column-count: 3; } }\r\n\r\n    \/* CARD STYLES *\/\r\n    .gallery-card {\r\n        break-inside: avoid; margin-bottom: 15px; border-radius: 8px;\r\n        overflow: hidden; position: relative; cursor: pointer;\r\n        transition: transform 0.3s ease, box-shadow 0.3s ease;\r\n        box-shadow: 0 4px 6px rgba(0,0,0,0.1);\r\n        background: #f7fafc;\r\n        \/* Fix for Safari overflow *\/\r\n        -webkit-mask-image: -webkit-radial-gradient(white, black);\r\n    }\r\n\r\n    \/* THE \"CLICK\" OVERLAY *\/\r\n    .gallery-card::after {\r\n        content: \"CLICK\";\r\n        position: absolute;\r\n        top: 0; left: 0; width: 100%; height: 100%;\r\n        background: rgba(0,0,0,0.4); \/* Semi-transparent dark tint *\/\r\n        color: rgba(255,255,255,0.9);\r\n        display: flex; align-items: center; justify-content: center;\r\n        font-weight: bold; font-size: 1.2rem; letter-spacing: 3px;\r\n        text-transform: uppercase;\r\n        opacity: 0;\r\n        transition: opacity 0.3s ease;\r\n        pointer-events: none; \/* Ensures the click passes through to the card *\/\r\n        z-index: 20;\r\n    }\r\n    \r\n    \/* HOVER STATES *\/\r\n    .gallery-card:hover { \r\n        transform: translateY(-5px); \r\n        box-shadow: 0 15px 30px rgba(0,0,0,0.15);\r\n    }\r\n    .gallery-card:hover::after {\r\n        opacity: 1; \/* Show the \"Click\" text *\/\r\n    }\r\n    .gallery-card img { \r\n        width: 100%; height: auto; display: block; \r\n        transition: transform 0.5s ease;\r\n    }\r\n    .gallery-card:hover img {\r\n        transform: scale(1.08); \r\n    }\r\n\r\n    \/* MODAL STYLES *\/\r\n    .mod-modal-overlay {\r\n        display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;\r\n        background: rgba(0,0,0,0.85); z-index: 9999;\r\n        align-items: center; justify-content: center; padding: 20px;\r\n        backdrop-filter: blur(5px);\r\n    }\r\n    .mod-modal-content {\r\n        background: white; width: 100%; max-width: 900px; height: 600px;\r\n        display: flex; border-radius: 8px; overflow: hidden; position: relative;\r\n        box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);\r\n    }\r\n    @media (max-width: 768px) {\r\n        .mod-modal-content { flex-direction: column; height: auto; max-height: 90vh; }\r\n        .mod-img-col { width: 100%; height: 250px; }\r\n        .mod-txt-col { width: 100%; padding: 20px; }\r\n    }\r\n    .mod-img-col { width: 65%; background: white; display: flex; align-items: center; justify-content: center; }\r\n    .mod-img-col img { max-width: 100%; max-height: 100%; object-fit: contain; }\r\n    .mod-txt-col { width: 35%; padding: 30px; display: flex; flex-direction: column; justify-content: center; }\r\n<\/style>\r\n\r\n<div id=\"mod-gallery-root\" class=\"mod-gallery-wrapper\">\r\n    <p>Loading Gallery...<\/p>\r\n<\/div>\r\n\r\n<div id=\"gallery-modal\" class=\"mod-modal-overlay\" onclick=\"closeModal(event)\">\r\n    <div class=\"mod-modal-content\">\r\n        <div class=\"mod-img-col\"><img decoding=\"async\" id=\"modal-img\" src=\"\"><\/div>\r\n        <div class=\"mod-txt-col\">\r\n            <span style=\"background:#d4af37; color:white; padding:4px 10px; border-radius:12px; font-size:0.75rem; font-weight:bold; align-self:flex-start; text-transform: uppercase; letter-spacing: 1px;\">From Project<\/span>\r\n            <h3 id=\"modal-student\" style=\"margin: 20px 0 10px 0; color:#2d3748; font-size: 1.5rem;\">Student Name<\/h3>\r\n            <hr style=\"border:0; border-top:1px solid #edf2f7; width:100%; margin-bottom: 20px;\">\r\n            <p id=\"modal-desc\" style=\"color:#4a5568; line-height:1.6; font-size:1rem;\">Description...<\/p>\r\n        <\/div>\r\n    <\/div>\r\n<\/div>\r\n\r\n<script>\r\ndocument.addEventListener(\"DOMContentLoaded\", function() {\r\n    const container = document.getElementById('mod-gallery-root');\r\n    const apiUrl = 'https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/gallery_item?per_page=50';\r\n    const mediaUrl = 'https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/media?include=';\r\n\r\n    fetch(apiUrl)\r\n        .then(res => res.json())\r\n        .then(async posts => {\r\n            if(!posts.length) { container.innerHTML = 'No gallery posts found.'; return; }\r\n\r\n            \/\/ 1. COLLECT ALL IMAGE IDs\r\n            let imageIds = [];\r\n            let postsWithIds = [];\r\n\r\n            posts.forEach(post => {\r\n                const acf = post.acf || {};\r\n                let raw = acf.gallery_photo;\r\n                \r\n                if (typeof raw === 'number' || (typeof raw === 'string' && !raw.includes('http'))) {\r\n                    imageIds.push(raw);\r\n                    post.tempImageId = raw; \r\n                } else if (typeof raw === 'string') {\r\n                    post.tempImageUrl = raw;\r\n                } else if (typeof raw === 'object' && raw.url) {\r\n                    post.tempImageUrl = raw.url;\r\n                }\r\n                postsWithIds.push(post);\r\n            });\r\n\r\n            \/\/ 2. BULK FETCH MISSING IMAGES\r\n            let mediaLookup = {};\r\n            if (imageIds.length > 0) {\r\n                try {\r\n                    const mediaRes = await fetch(mediaUrl + imageIds.join(','));\r\n                    const mediaItems = await mediaRes.json();\r\n                    mediaItems.forEach(media => { mediaLookup[media.id] = media.source_url; });\r\n                } catch (e) { console.error(\"Media fetch failed\", e); }\r\n            }\r\n\r\n            \/\/ 3. RENDER GALLERY\r\n            postsWithIds.sort(() => Math.random() - 0.5);\r\n            let html = '';\r\n\r\n            postsWithIds.forEach(post => {\r\n                let finalUrl = post.tempImageUrl || mediaLookup[post.tempImageId];\r\n                \r\n                const desc = post.title.rendered || 'No description available.';\r\n                const student = post.acf?.related_student_name || 'Anonymous';\r\n\r\n                if (finalUrl) {\r\n                    html += `\r\n                    <div class=\"gallery-card\" onclick=\"openModal('${finalUrl}', '${escapeHtml(desc)}', '${escapeHtml(student)}')\">\r\n                        <img decoding=\"async\" src=\"${finalUrl}\" loading=\"lazy\" alt=\"Gallery\">\r\n                    <\/div>`;\r\n                }\r\n            });\r\n\r\n            container.innerHTML = html || '<p>Images matched, but none could be displayed.<\/p>';\r\n        })\r\n        .catch(err => {\r\n            container.innerHTML = '<p>Error: ' + err.message + '<\/p>';\r\n        });\r\n});\r\n\r\n\/\/ POPUP FUNCTIONS\r\nfunction openModal(img, desc, student) {\r\n    document.getElementById('modal-img').src = img;\r\n    document.getElementById('modal-desc').innerHTML = desc;\r\n    document.getElementById('modal-student').innerText = student;\r\n    document.getElementById('gallery-modal').style.display = 'flex';\r\n}\r\nfunction closeModal(e, force) { \r\n    if (force || e.target.id === 'gallery-modal') {\r\n        document.getElementById('gallery-modal').style.display = 'none';\r\n    }\r\n}\r\nfunction escapeHtml(t) { return t ? t.replace(\/'\/g, \"\\\\'\").replace(\/\"\/g, '&quot;') : ''; }\r\n<\/script><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>Loading Gallery&#8230; From Project Student Name Description&#8230;<!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":9907,"featured_media":0,"parent":0,"menu_order":5,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"ngg_post_thumbnail":0,"footnotes":""},"class_list":["post-54","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/pages\/54","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/users\/9907"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/comments?post=54"}],"version-history":[{"count":24,"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/pages\/54\/revisions"}],"predecessor-version":[{"id":1207,"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/pages\/54\/revisions\/1207"}],"wp:attachment":[{"href":"https:\/\/blogs.sun.ac.za\/mod\/wp-json\/wp\/v2\/media?parent=54"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}