1<!DOCTYPE html> 2<html lang="en-GB"> 3<head> 4<!-- start of head--> 5 <title>About St Martin's Church Guernsey</title> 6 <link rel="shortcut icon" href="/_data/site/102/templates/current/favicon.ico" type="image/x-icon" /> 7 <meta charset="utf-8"> 8 <meta name="dcterms.rightsHolder" content="Khoo Systems Limited"> 9 <meta name="dcterms.dateCopyrighted" content="2014"> 10 <meta name="robots" content="all"> 11 <meta name="description" content="We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshipping God"> 12 <meta name="csrf_token" content="stmartinsguernsey:0xB21492B7847B11F19A5EF23C9435931F:::1791079665:5ab7d6d4669d28a7da69c5fdd559a676"> 13 <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet"> 14 15<meta name="viewport" content="width=device-width, initial-scale=1.0"> 16<meta property="og:type" content="website"> 17<meta property="og:site_name" content="St Martin's Parish Church Guernsey"> 18<meta property="og:image" content="https://www.stmartinschurch.gg/cache/img/102/folder|159&sz500x500&cp&tn&ql&fm&bo&bc&sg05d861764a&ft1674495236&bannerWhatsOn.jpg"> 19<meta property="og:title" content="About St Martin's Church Guernsey"> 20<meta property="og:description" content="We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshipping God"> 21<meta property="og:url" content="https://www.stmartinschurch.gg/159/Welcome"> 22<meta name="twitter:card" content="summary_large_image"> 23<meta name="twitter:title" content="About St Martin's Church Guernsey"> 24<meta name="twitter:description" content="We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshipping God"> 25<meta name="twitter:image" content="https://www.stmartinschurch.gg/cache/img/102/folder|159&sz500x500&cp&tn&ql&fm&bo&bc&sg05d861764a&ft1674495236&bannerWhatsOn.jpg">
26<script type='application/ld+json'> 27{"@type":"Organization","@context":"https://schema.org","@id":"https://www.stmartinschurch.gg/#organization","sameAs":["https://www.facebook.com/StMartinsGuernsey/","https://www.youtube.com/@stmartinschurchguernsey8426/videos"],"name":"St Martin's Parish Church Guernsey","url":"https://www.stmartinschurch.gg/","contactPoint":[{"@type":"ContactPoint","telephone":"+441481231197","contactType":"customer support"}],"logo":"https://www.stmartinschurch.gg/_data/site/102/templates/current/mobile_logo.svg"} 28</script>
28 29
30<script type='application/ld+json'> 31{"@type":"WebSite","@context":"https://schema.org","@id":"https://www.stmartinschurch.gg/#website","url":"https://www.stmartinschurch.gg/","name":"St Martin's Parish Church Guernsey","publisher":{"@id":"https://www.stmartinschurch.gg/#organization"}} 32</script>
32 33<!-- Start_Google_structured_data_breadcrumb -->
34<script type="application/ld+json"> 35{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://www.stmartinschurch.gg/","name":"Home"}},{"@type":"ListItem","position":2,"item":{"@id":"https://www.stmartinschurch.gg/159/Welcome","name":"Welcome","image":"https://www.stmartinschurch.gg/cache/img/102/folder|159&sz200x200&cp&tn&ql&fm&bo&bc&sg4a2521b319&ft1674495236&bannerWhatsOn.jpg"}}]} 36</script>
36 37<!-- End_Google_structured_data_breadcrumb --> 38<link rel='canonical' href='https://www.stmartinschurch.gg/159/Welcome'> 39 40 41 42<link rel='stylesheet' type='text/css' href='/js/plugins/cookieconsent/dist/cookieconsent.css?v=5'> 43<link rel='stylesheet' type='text/css' href='/js/jquery-plugins/jquery-ui/jquery-ui.custom.css?v=5'> 44<link rel='stylesheet' type='text/css' href='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.theme.css'> 45<link rel='stylesheet' type='text/css' href='https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css'> 46<link rel='stylesheet' type='text/css' href='https://cdn.jsdelivr.net/npm/shortcut-buttons-flatpickr/dist/themes/light.min.css'> 47<link rel='stylesheet' type='text/css' href='/css/default.css?v=5'> 48<link rel='stylesheet' type='text/css' href='/modules/folder/css/folder.css?v=5'> 49<link rel='stylesheet' type='text/css' href='/modules/contact/css/contact.css?v=5'> 50<link rel='stylesheet' type='text/css' href='/modules/news/css/news.css?v=5'> 51<link rel='stylesheet' type='text/css' href='/modules/pg/css/pg.css?v=5'> 52<link rel='stylesheet' type='text/css' href='/modules/search/css/search.css?v=5'> 53<link rel='stylesheet' type='text/css' href='/modules/user/css/user.css?v=5'> 54<link rel='stylesheet' type='text/css' href='/modules/attribute/css/attribute.css?v=5'> 55<link rel='stylesheet' type='text/css' href='/modules/calendar/css/calendar.css?v=5'> 56<link rel='stylesheet' type='text/css' href='/modules/directory/css/directory.css?v=5'> 57<link rel='stylesheet' type='text/css' href='/modules/crm/css/crm.css?v=5'> 58<link rel='stylesheet' type='text/css' href='/modules/sermon/css/sermon.css?v=5'> 59<link rel='stylesheet' type='text/css' href='/css/search-autocomplete.css?v=5'> 60<link rel='stylesheet' type='text/css' href='/js/plugins/swiper/swiper-bundle.min.css?v=5'> 61 62<link rel='stylesheet' type='text/css' href='/cache/css/102/modern-form.css?v=5.1781178477'> 63<link rel='stylesheet' type='text/css' href='/cache/css/102/responsive.css?v=5.1781178477'> 64<link rel='stylesheet' type='text/css' href='/cache/css/102/menu.css?v=5.1781178477'> 65<link rel='stylesheet' type='text/css' href='/cache/css/102/ecampaign.css?v=5.1781178477'> 66<link rel='stylesheet' type='text/css' href='/cache/css/102/calevent.css?v=5.1781178477'> 67<link rel='stylesheet' type='text/css' href='/cache/css/102/template_fonts.css?v=5.1781178477'> 68<link rel='stylesheet' type='text/css' href='/cache/css/102/template.css?v=5.1781178477'> 69<link rel='stylesheet' type='text/css' href='/cache/css/102/template_custom.css?v=5.1781178477'> 70 71
72<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
vendor: 1 bytes, line 72
72
73<script defer src='https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js'></script>
vendor: 1 bytes, line 73
73
74<script defer src='https://unpkg.com/[email protected]/dist/htmx.min.js'></script>
vendor: 1 bytes, line 74
74
75<script defer src='https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js'></script>
vendor: 1 bytes, line 75
75
76<script defer src='https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js'></script>
vendor: 1 bytes, line 76
76
77<script defer src='https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js'></script>
vendor: 1 bytes, line 77
77
78<script defer src='https://cdn.jsdelivr.net/npm/tippy.js@6/dist/tippy-bundle.umd.js'></script>
78 79<link rel="stylesheet" href="https://use.typekit.net/vdl8nqz.css">
80<script src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js'></script>
vendor: 1 bytes, line 80
80
81<script defer src='/js/jquery-plugins/jquery.validate/jquery.validate.min.js?v=5'></script>
vendor: 1 bytes, line 81
81
82<script defer src='/js/jquery-plugins/jquery.validate/additional/creditcard.js?v=5'></script>
vendor: 1 bytes, line 82
82
83<script defer src='/js/component/matchHeight.js?v=5'></script>
vendor: 1 bytes, line 83
83
84<script defer src='/js/plugins/headroom/headroom.min.js?v=5'></script>
vendor: 1 bytes, line 84
84
85<script defer src='/js/plugins/lazyload/lazyload.js?v=5'></script>
vendor: 1 bytes, line 85
85
86<script> 87document.addEventListener("DOMContentLoaded",function(){lazyload();}); 88</script>
vendor: 1 bytes, line 88
88
89<script defer src='https://cdn.jsdelivr.net/npm/flatpickr'></script>
vendor: 1 bytes, line 89
89
90<script defer src='https://cdn.jsdelivr.net/npm/shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr.min.js'></script>
vendor: 1 bytes, line 90
90
91<script defer src='/js/plugins/base64.js?v=5'></script>
vendor: 1 bytes, line 91
91
92<script src='/js/default.js?v=5'></script>
vendor: 1 bytes, line 92
92
93<script defer src='/modules/menu/js/menu_h.js?v=5'></script>
vendor: 1 bytes, line 93
93
94<script defer src='/modules/menu/js/menu_hamb.js?v=5'></script>
vendor: 1 bytes, line 94
94
95<script defer src='/modules/menu/js/menu_mega.js?v=5'></script>
vendor: 1 bytes, line 95
95
96<script defer src='/js/component/searchAutocomplete.js?v=5'></script>
vendor: 1 bytes, line 96
96
97<script defer src='/js/plugins/swiper/swiper-bundle.min.js?v=5'></script>
vendor: 1 bytes, line 97
97
98<script defer src='/js/component/swiperLegacyAlias.js?v=5'></script>
98 99 100 101 102<link rel='alternate' type='application/rss+xml' href='/159/xml/' title='RSS feed'> 103<link rel='alternate' type='application/rss+xml' href='/159/podfeed/' title='Podcast feed'> 104<!--end of head --> 105</head> 106<body class="body-template body-159 _body-159 body-folder body-layout-preset-848 browser-chrome engine-webkit majorver-128 ver-128.0.0.0 is-desktop geo-CA body-menu-hamb body-menu-hamb-left body-menu-hamb-slide "> 107 108 <div id="q-app"> 109 <div id="shim"></div> 110 <div id='menu-hamb' class='left slide'><a id='menu-icon-close'> <svg style="fill:#fff" version="1.1" id="hamb-icon" class="hamb-icon-svg" 111 xmlns="http://www.w3.org/2000/svg" 112 xmlns:xlink="http://www.w3.org/1999/xlink" 113 x="0px" y="0px" width="45px" height="45px" 114 viewBox="0 0 128 128" 115 enable-background="new 0 0 128 128" xml:space="preserve"> 116 <g> 117 <rect style="fill:#5CA4A9;" id="hamb-icon-close-box" x="0" y="0" width="128" height="128"/> 118 <g transform="matrix(0.5, 0, 0, 0.5, 32,32)"> 119 <g id="close" transform="translate(18.000000, 18.000000)"> 120 <path d="M92,9.19938255 L82.8006174,0 L46,36.8006174 L9.19938255,0 L0,9.19938255 L36.8006174,46 L0,82.8006174 L9.19938255,92 L46,55.1993826 L82.8006174,92 L92,82.8006174 L55.1993826,46 L92,9.19938255 Z" id="Shape"></path> 121 </g> 122 </g> 123 </g> 124 </svg></a><ul class=' n0'><li class='n159 selected open show-items folder' data-nav-id='159'><div><a class='' href='/159/Welcome' >Welcome</a><a class='menu-branch ' href='/159/ajax/menu_branch.php' rel='nofollow'> </a></div><ul class=' n159'><li class='n91 hide-items folder' data-nav-id='91'><div><a class='' href='/91/Our-Team' >Who's Who</a></div></li><li class='n1 contact' data-nav-id='1'><div><a class='' href='/1/Im-New' >I'm New!</a></div></li><li class='n501 pg' data-nav-id='501'><div><a class='' href='/501/Our-Vision' >Our Vision</a></div></li><li class='n601 hide-items folder' data-nav-id='601'><div><a class='' href='/601/Life-Events' >Life Events</a></div></li></ul></li><li class='n13 show-items folder' data-nav-id='13'><div><a class=' ajax-branch' href='/13/Whats-On' >What's On</a><a class='menu-branch ajax' href='/13/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n350 show-items folder' data-nav-id='350'><div><a class=' ajax-branch' href='/350/Community' >Community</a><a class='menu-branch ajax' href='/350/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n602 show-items folder' data-nav-id='602'><div><a class=' ajax-branch' href='/602/Safeguarding' >Safeguarding</a><a class='menu-branch ajax' href='/602/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n422 show-items folder' data-nav-id='422'><div><a class=' ajax-branch' href='/422/Resources' >Resources</a><a class='menu-branch ajax' href='/422/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n359 pg' data-nav-id='359'><div><a class='' href='/359/Give' >Give</a></div></li><li class='n311 hide-items folder' data-nav-id='311'><div><a class='' href='/311/Contact-Us' >Contact</a></div></li></ul></div> 125 <div id="header-fixed" class="header--fixed"> 126
126<script> 127 $(function(){ 128 var header = document.querySelector("#header-fixed"); 129 if (window.location.hash) { 130 header.classList.add("slide--up"); 131 } 132 var headroom = new Headroom(header, { 133 tolerance: { 134 down : 10, 135 up : 20 136 }, 137 offset: 100 138 }).init(); 139 }) 140 </script>
140 <header id="header-mob"> 141 <div id="header-mob-box"> 142 <a id="header-mob-logo" href="/"></a> 143 <div id="mobile-actions"> 144 145 146 <svg id='mobile-search-icon' viewBox='0 0 100 100' x-data @click="$dispatch('toggle-mobile-search')"><path d='M94.5,88.6L76.8,70.9c6.6-7.4,10.7-17.2,10.7-27.9c0-23.1-18.8-41.9-41.9-41.9S3.7,19.9,3.7,43s18.8,41.9,41.9,41.9c8.2,0,15.8-2.4,22.2-6.4l18.4,18.4c1.1,1.1,2.6,1.7,4.1,1.7c1.5,0,3-0.6,4.1-1.7C96.8,94.6,96.8,90.9,94.5,88.6z M15.4,43c0-16.6,13.5-30.2,30.2-30.2S75.7,26.4,75.7,43S62.2,73.2,45.6,73.2S15.4,59.7,15.4,43z'/></svg> 147</div> 148<div id="mobile-search" 149 x-data="{ open: false }" 150 :class="{ 'is-open': open }" 151 @toggle-mobile-search.window="open = !open"> 152 <form action="/search/index.htm" method="post"> 153 <img id="icon-search" src="/media/icons/search.svg" alt="" width="18" height="18"> 154 <input name="p_search" 155 value="" 156 type="text" 157 placeholder="Search" 158 autocomplete="off" 159 data-autocomplete-url="/search/ajax/search_select.php" 160 data-autocomplete-min="3" 161 data-autocomplete-delay="100" /> 162 <img id="close-search" src="/media/icons/close.svg" alt="Close" width="18" height="18" @click="open = false"> 163 </form> 164</div> 165 166<div id='menu-icon-wrapper'><div id='menu-icon'> <svg style="fill:#fff" version="1.1" id="hamb-icon" class="hamb-icon-svg" 167 xmlns="http://www.w3.org/2000/svg" 168 xmlns:xlink="http://www.w3.org/1999/xlink" 169 x="0px" y="0px" width="45px" height="45px" 170 viewBox="0 0 128 128" 171 enable-background="new 0 0 128 128" xml:space="preserve"> 172 <g> 173 <rect style="fill:#5CA4A9;" id="hamb-icon-box" x="0" y="0" width="128" height="128"/> 174 <g transform="matrix(0.6, 0, 0, 0.6, 25.6,25.6)"> 175 <rect x="24" y="60" width="80" height="8"/> 176 <rect x="24" y="36" width="80" height="8"/> 177 <rect x="24" y="84" width="80" height="8"/> 178 </g> 179 </g> 180 </svg></div></div> 181 </div> 182 <div id='mobile-trolley-notification'></div> 183 </header> 184 </div> 185 186 <div id="template-content" class="template template-box"> 187 <a id="staging-box" href="/_default/staging.htm">You are viewing this site in staging mode. Click in this bar to return to normal site.</a> 188 <div id="svg-icons"><!-- --></div> 189 <div id="notify-container"><div id="basic-template"><a class="ui-notify-close ui-notify-cross" href="#">x</a>#{notify_img}<div class="notify-title">#{notify_title}</div><div class="notify-text">#{notify_text}</div></div></div> 190 <div class="modal-dlg" id="modal-dlg"><!-- --></div> 191 <div class="modal-dlg" id="modal-dlg1"><!-- --></div> 192 <div class="modal-dlg" id="modal-dlg2"><!-- --></div> 193 194 <div id="browser-box"> 195 <div id="page-box"> 196 <div id="header-nav"> 197 <header id="header"> 198 <div id="header-inner" class="section-inner"><!-- --> 199 <div id="header-box"> 200 <div class="header-logo"><a href="/"><img src="/_data/site/102/_default/stMartinsLogoDark2.png" alt="St Martins Church" width="400" height="109" /></a></div> 201<div class="header-search"><form id="search" class="search" action="/search/index.htm" method="post"> 202 <input id="p-search" 203 name="p_search" 204 value="" 205 type="text" 206 placeholder="Search" 207 autocomplete="off" 208 data-autocomplete-url="/search/ajax/search_select.php" 209 data-autocomplete-min="3" 210 data-autocomplete-delay="100" /> 211</form> 212</div> 213 </div> 214 </div> 215 </header> 216 <nav id="h-nav"> 217 <div id="h-nav-inner" class="section-inner"> 218 <div id="h-nav-box"> 219 <div id='menu-h'><ul class=' n0'><li class='n159 selected open show-items folder' data-nav-id='159'><div><a class='' href='/159/Welcome' >Welcome</a><a class='menu-branch ' href='/159/ajax/menu_branch.php' rel='nofollow'> </a></div><ul class=' n159'><li class='n91 hide-items folder' data-nav-id='91'><div><a class='' href='/91/Our-Team' >Who's Who</a></div></li><li class='n1 contact' data-nav-id='1'><div><a class='' href='/1/Im-New' >I'm New!</a></div></li><li class='n501 pg' data-nav-id='501'><div><a class='' href='/501/Our-Vision' >Our Vision</a></div></li><li class='n601 hide-items folder' data-nav-id='601'><div><a class='' href='/601/Life-Events' >Life Events</a></div></li></ul></li><li class='n13 show-items folder' data-nav-id='13'><div><a class=' ajax-branch' href='/13/Whats-On' >What's On</a><a class='menu-branch ajax' href='/13/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n350 show-items folder' data-nav-id='350'><div><a class=' ajax-branch' href='/350/Community' >Community</a><a class='menu-branch ajax' href='/350/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n602 show-items folder' data-nav-id='602'><div><a class=' ajax-branch' href='/602/Safeguarding' >Safeguarding</a><a class='menu-branch ajax' href='/602/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n422 show-items folder' data-nav-id='422'><div><a class=' ajax-branch' href='/422/Resources' >Resources</a><a class='menu-branch ajax' href='/422/ajax/menu_branch.php' rel='nofollow'> </a></div></li><li class='n359 pg' data-nav-id='359'><div><a class='' href='/359/Give' >Give</a></div></li><li class='n311 hide-items folder' data-nav-id='311'><div><a class='' href='/311/Contact-Us' >Contact</a></div></li></ul></div> 220 </div> 221 </div> 222 </nav> 223 224 225 </div> 226 <section id="body-header"><!-- --> 227 <div id="body-header-inner" class="section-inner" ><!-- --> 228 <div id="body-header-box"> 229 <div><div class='pg-gallery-fw'><div id="gallery-fw-wrapper-159" class='gallery-fw-wrapper-preset-493 breakout'> 230 231 <div id="gallery-fw-159" class='swiper slick-slider cycle-slideshow gallery-fw-preset-493'> 232 <div class='swiper-wrapper slick-list slick-track'>
233 <div class='swiper-slide slick-slide cycle-slide im-wrapper ' style='background-image:url(/cache/img/102/folder|159&sz3000x3000&cp&tn&ql&fm&bo&bc&sg39b3ec2a71&ft1674495236&bannerWhatsOn.jpg)' data-title='' data-description='<h1 class='pg-title'>Welcome</h1>' data-link=""></div> 234 </div> 235 236 237 </div> 238 239</div> 240 241<style> 242 .gallery-fw-wrapper-preset-493 {min-height:500px} 243.gallery-fw-wrapper-preset-493 .im-wrapper {background-size:cover;background-position:center center;width:100%;height:500px} 244#gallery-fw-159 {width:100%;min-width:0px;max-width:100%;overflow:hidden} 245.gallery-fw-wrapper-preset-493 .swiper-button-prev {background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='15 6 9 12 15 18'/></svg>") no-repeat scroll 20% 50% rgba(0, 0, 0, 0);background-size:30px 30px;left:0px;margin-top:0px} 246.gallery-fw-wrapper-preset-493 .swiper-button-next {background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='9 6 15 12 9 18'/></svg>") no-repeat scroll 80% 50% rgba(0, 0, 0, 0);background-size:30px 30px;right:0px;margin-top:0px} 247.gallery-fw-wrapper-preset-493 .swiper-button-prev:hover, #gallery-fw-159 .swiper-button-next:hover {opacity:1} 248.gallery-fw-wrapper-preset-493 .swiper-button-prev, #gallery-fw-159 .swiper-button-next {cursor:pointer;position:absolute;height:100%;opacity:0;top:0px;width:20%;z-index:800} 249.gallery-fw-wrapper-preset-493 .swiper-pagination {text-align:center;position:absolute;overflow:hidden;z-index:500} 250.gallery-fw-wrapper-preset-493 .swiper-pagination-bullet {font-family:arial;font-size:50px;width:16px;height:16px;display:inline-block;color:#ddd;cursor:pointer} 251.gallery-fw-wrapper-preset-493 .swiper-pagination-bullet-active {color:#D69746} 252.gallery-fw-wrapper-preset-493 .gallery-fw-caption {pointer-events:none} 253.gallery-fw-wrapper-preset-493 .gallery-fw-caption .caption-description {pointer-events:none;background-color:rgba(78,81,102,0.75);color:#ffffff;height:100%} 254.gallery-fw-wrapper-preset-493 .im-wrapper.video {overflow:hidden} 255.gallery-fw-wrapper-preset-493 .im-wrapper.video video {width:100%;object-fit:cover;min-height:500px;max-height:500px} 256@media only screen and (max-width: 767px ) { 257#gallery-fw-wrapper-159 {min-height:500px} 258.gallery-fw-wrapper-preset-493 .im-wrapper {height:500px} 259.gallery-fw-wrapper-preset-493 .im-wrapper.video video {object-fit:cover;min-height:500px;max-height:500px} 260} 261 262</style>
263<script> 264(function(){ 265 var navId = 159; 266 function init() { 267 var root = document.getElementById('gallery-fw-' + navId); 268 if (!root || typeof Swiper === 'undefined') return; 269 270 var swiper = new Swiper(root, {"speed":1000,"effect":"fade","direction":"horizontal","fadeEffect":{"crossFade":true},"autoplay":false}); 271 if (typeof window.swiperLegacyAlias === 'function') window.swiperLegacyAlias(swiper); 272 273 /* Swiper auto-attaches click listeners to navigation buttons, 274 * but on opacity:0 buttons inside our breakout/table-cell 275 * layout those clicks were observed not firing. Bind 276 * explicit handlers as a guarantee. Calling slideNext/Prev 277 * while a transition is in flight is safe (Swiper's own 278 * isLocked/animating guards handle the no-op). */ 279 var prevBtn = root.querySelector('.swiper-button-prev'); 280 var nextBtn = root.querySelector('.swiper-button-next'); 281 if (prevBtn) prevBtn.addEventListener('click', function(e){ e.stopPropagation(); swiper.slidePrev(); }); 282 if (nextBtn) nextBtn.addEventListener('click', function(e){ e.stopPropagation(); swiper.slideNext(); }); 283 284 var captionEnabled = (0 === 1); 285 if (captionEnabled) { 286 var caption = root.parentNode.querySelector('.gallery-fw-caption .caption-description'); 287 if (caption) { 288 /* The first slide's caption is rendered server-side (FwImage.php), 289 * so interactive content in it (e.g. a lightbox CTA) is present at 290 * parse time and bound by page-level init like any static markup â 291 * no initial JS injection here. On slide change we swap in the new 292 * slide's caption from data-description and fire a bubbling 293 * 'gallery:caption-changed' event so any plugin that binds to 294 * injected markup can rescan. The widget stays framework-agnostic 295 * and never hard-codes a plugin selector. */ 296 var setCaption = function(s) { 297 var slides = root.querySelectorAll('.swiper-slide:not(.swiper-slide-duplicate)'); 298 var active = slides[s.realIndex]; 299 caption.innerHTML = (active && active.dataset.description) ? active.dataset.description : ''; 300 caption.dispatchEvent(new CustomEvent('gallery:caption-changed', { bubbles: true, detail: { slide: s.realIndex } })); 301 }; 302 swiper.on('slideChangeTransitionStart', setCaption); 303 } 304 } 305 306 root.querySelectorAll('video.bgvid').forEach(function(v){ 307 var p = v.play(); 308 if (p && p.catch) p.catch(function(){}); 309 }); 310 311 root.addEventListener('click', function(e){ 312 if (e.target.closest('.swiper-button-prev, .swiper-button-next, .swiper-pagination')) return; 313 var link = e.target.closest('.linkable'); 314 if (!link) return; 315 var href = link.dataset.link; 316 if (href) window.location = href; 317 }); 318 } 319 320 if (document.readyState === 'loading') { 321 document.addEventListener('DOMContentLoaded', init); 322 } else { 323 init(); 324 } 325})(); 326</script>
326 327</div></div> 328 </div> 329 </div> 330 </section> 331 332 <main id="main-content"> 333 <div id="main-content-inner" class="section-inner"> 334 <div id="main-content-box"> 335 336 <div id="c-col" class='no-lc no-rc '> 337 <div id="c-col-inner"> 338 <div id="c-col-box"> 339 <div id="content-header"><!-- --> 340 341 </div> 342 343 <div id="content-box" class="layout-0xDCE258AC847B11F19A5EF23C9435931F"> 344 <!-- content --> 345<div class="item-folder "> 346<h1 class='pg-title'>Welcome</h1> 347 348 349<div class='pg-gallery-av'></div> 350<div class='pg-body'><p>We would like to welcome you to St Martin’s Parish Church. We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshiping God</p> 351<p> </p></div> 352<div class='pg-folder '>
352<script> 353function ajax_load_more(scroll){ 354 $.ajax({ 355 url: '/159/ajax/folder_page.php', 356 data: {'idx': '0xB21492B7847B11F19A5EF23C9435931F', 'npage': npage }, 357 type: "GET", 358 dataType: "html", 359 success: function(data) { 360 if (data) { scroll.replaceWith(data);} 361 lazyload(); 362 if (typeof folder_tile_match_height === 'function'){ 363 folder_tile_match_height(); 364 } 365 } 366 }) 367} 368</script>
368 369 370<!-- BEGIN scriptBlock -->
371<script> 372 $(function(){ 373 374 }) 375</script>
375 376<!-- END scriptBlock --> 377 378<style> 379.folder-preset-563 .folder-view li .caption {color:#fff;min-height:100%} 380.folder-preset-563 .folder-view li .caption h3 {color:#fff} 381.folder-preset-563 .folder-view li .caption.over.top {top:0px} 382.folder-preset-563 .folder-view li .caption.over.bottom {bottom:0px} 383.folder-preset-563 .folder-view li .caption.fade {visibility:hidden;opacity:0;-webkit-transition:-webkit-visibility 0s linear 0.5s,opacity 0.5s linear;-moz-transition:-moz-visibility 0s linear 0.5s,opacity 0.5s linear;transition:visibility 0s linear 0.5s,opacity 0.5s linear} 384.folder-preset-563 .folder-view li:hover .caption.fade {visibility:visible;opacity:1;transition-delay:0s} 385.folder-preset-563 .folder-view li:hover .caption.over.slide, 386 .folder-preset-563 .folder-view li.mobi-clicked .caption {height:100%} 387.mobile .folder-preset-563 .folder-view li:hover .caption.over.slide {height:0px} 388.mobile .folder-preset-563 .folder-view li.mobi-clicked .caption, 389 .mobile .folder-preset-563 .folder-view li.mobi-clicked .caption.over.slide {height:100%} 390.folder-preset-563 .folder-tile {margin-right:-50px} 391.folder-preset-563 .folder-tile .img-block {border-style:solid} 392.folder-preset-563 .folder-tile li {width:100%;padding-right:50px} 393.folder-preset-563 .folder-tile li.item::after {content:"";display:block;height:50px;width:100%} 394 395@media screen and (min-width: 670px) { 396.folder-preset-563 .folder-tile li {width:50%} 397.folder-preset-563 .folder-tile li:nth-of-type(1n+1) {clear:none} 398.folder-preset-563 .folder-tile li:nth-of-type(2n+1) {clear:both} 399} 400@media screen and (min-width: 768px) { 401.folder-preset-563 .folder-tile li {width:33.333333333333%} 402.folder-preset-563 .folder-tile li:nth-of-type(2n+1) {clear:none} 403.folder-preset-563 .folder-tile li:nth-of-type(3n+1) {clear:both} 404} 405 406.folder-preset-563 .folder-list li .column-image {order:0} 407.folder-preset-563 .folder-list li.img-inline .img-wrapper {margin-bottom:0px} 408.folder-preset-563 .folder-list .column-image.hidden {display:none} 409.folder-preset-563 .folder-list li div.img-block {border-style:solid} 410.folder-preset-563 .folder-list li.img-inline div.img-block {width:100%;border-style:solid} 411.folder-preset-563 .folder-list li.img-inline div.img-block img {max-height:0px} 412.folder-preset-563 .folder-list li.item::after {height:0px} 413 414 415@media screen and (max-width: 767px) { 416.folder-preset-563 .folder-list li div.img-block {width:100%;height:auto} 417.folder-preset-563 .folder-list li div.img-block .img {background-size:contain} 418} 419 420</style> 421 422<div class="folder-wrapper folder-wrapper-159 folder-preset-563 "> 423<div class="folder-options "> 424 425 426</div> 427<ul class="folder-view folder-folder folder-tile "> 428 <li id="nav_91" class="item item-91 item-folder img-use-crop item-idx-0xB214A404847B11F19A5EF23C9435931F" data-idx="0xB214A404847B11F19A5EF23C9435931F"> 429 430<div class="fg-row"> 431 <div class="columns medium-3 large-3 column-image "> 432 <!-- BEGIN imgBlock --> 433 <div class="img-wrapper"> 434 <div class="img-block" href="/lib/static/img.php?im=/_data/site/102/folder/91/bannerTeam.jpg&sz=1500x1500&cp=1"> 435 <a class="pg-link" href="/91/Our-Team" > 436 <div class="img img-alt lazyload" style="" data-src=""></div> 437 <div class="img img-main lazyload" style="background-image:url('/_.gif');" data-src="/cache/img/102/folder|91&sz520x480&cp1&tn&ql&fm&bo&bc&sgb69affd8af&ft1671464957&bannerTeam.jpg"></div> 438 <img src='/cache/img/102/folder|91&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sgb69affd8af&ft1671464957&bannerTeam.jpg' alt='Meet the Team'> 439 </a> 440</div> 441 <div class="caption over top" onclick="window.location.href='/91/Our-Team'"> 442 <div class="caption-text-wrapper"> 443 <a class='pg-title-link pg-link' href='/91/Our-Team' ><h3 class='pg-title-list'>Meet the Team</h3></a> 444 445 <a href="/91/Our-Team">Find out more</a> 446 </div> 447 </div> 448 </div> 449 <!-- END imgBlock --> 450 </div> 451 <div class="folder-details columns medium-9 large-9 column-body"> 452 <a class='pg-title-link pg-link' href='/91/Our-Team' ><h3 class='pg-title-list'>Meet the Team</h3></a> 453 454 455 </div> 456</div> 457 </li> 458 <li id="nav_1" class="item item-1 item-contact img-use-crop item-idx-0xB214D8AF847B11F19A5EF23C9435931F" data-idx="0xB214D8AF847B11F19A5EF23C9435931F"> 459 460<div class="fg-row"> 461 <div class="columns medium-3 large-3 column-image "> 462 <!-- BEGIN imgBlock --> 463 <div class="img-wrapper"> 464 <div class="img-block" href="/lib/static/img.php?im=/_data/site/102/contact/1/bannerNew.jpg&sz=1500x1500&cp=1"> 465 <a class="pg-link" href="/1/Im-New" > 466 <div class="img img-alt lazyload" style="" data-src=""></div> 467 <div class="img img-main lazyload" style="background-image:url('/_.gif');" data-src="/cache/img/102/contact|1&sz520x480&cp1&tn&ql&fm&bo&bc&sg73d6e27f98&ft1674144086&bannerNew.jpg"></div> 468 <img src='/cache/img/102/contact|1&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sg73d6e27f98&ft1674144086&bannerNew.jpg' alt='I'm New!'> 469 </a> 470</div> 471 <div class="caption over top" onclick="window.location.href='/1/Im-New'"> 472 <div class="caption-text-wrapper"> 473 <a class='pg-title-link pg-link' href='/1/Im-New' ><h3 class='pg-title-list'>I'm New!</h3></a> 474 475 <a href="/1/Im-New">Find out more</a> 476 </div> 477 </div> 478 </div> 479 <!-- END imgBlock --> 480 </div> 481 <div class="folder-details columns medium-9 large-9 column-body"> 482 <a class='pg-title-link pg-link' href='/1/Im-New' ><h3 class='pg-title-list'>I'm New!</h3></a> 483 484 485 </div> 486</div> 487 </li> 488 <li id="nav_501" class="item item-501 item-pg img-use-crop item-idx-0xB214A5FC847B11F19A5EF23C9435931F" data-idx="0xB214A5FC847B11F19A5EF23C9435931F"> 489 490<div class="fg-row"> 491 <div class="columns medium-3 large-3 column-image "> 492 <!-- BEGIN imgBlock --> 493 <div class="img-wrapper"> 494 <div class="img-block" href="/lib/static/img.php?im=/_data/site/102/pg/501/bannerBoat2.jpg&sz=1500x1500&cp=1"> 495 <a class="pg-link" href="/501/Our-Vision" > 496 <div class="img img-alt lazyload" style="" data-src=""></div> 497 <div class="img img-main lazyload" style="background-image:url('/_.gif');" data-src="/cache/img/102/pg|501&sz520x480&cp1&tn&ql&fm&bo&bc&sg06b28b7c20&ft16741451
49750&bannerBoat2.jpg"></div> 498 <img src='/cache/img/102/pg|501&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sg06b28b7c20&ft1674145150&bannerBoat2.jpg' alt='Our Vision'> 499 </a> 500</div> 501 <div class="caption over top" onclick="window.location.href='/501/Our-Vision'"> 502 <div class="caption-text-wrapper"> 503 <a class='pg-title-link pg-link' href='/501/Our-Vision' ><h3 class='pg-title-list'>Our Vision</h3></a> 504 505 <a href="/501/Our-Vision">Find out more</a> 506 </div> 507 </div> 508 </div> 509 <!-- END imgBlock --> 510 </div> 511 <div class="folder-details columns medium-9 large-9 column-body"> 512 <a class='pg-title-link pg-link' href='/501/Our-Vision' ><h3 class='pg-title-list'>Our Vision</h3></a> 513 514 515 </div> 516</div> 517 </li> 518 <li id="nav_601" class="item item-601 item-folder img-use-crop item-idx-0xB21505DB847B11F19A5EF23C9435931F" data-idx="0xB21505DB847B11F19A5EF23C9435931F"> 519 520<div class="fg-row"> 521 <div class="columns medium-3 large-3 column-image "> 522 <!-- BEGIN imgBlock --> 523 <div class="img-wrapper"> 524 <div class="img-block" href="/lib/static/img.php?im=/_data/site/102/folder/601/bannerLifeEvents.jpg&sz=1500x1500&cp=1"> 525 <a class="pg-link" href="/601/Life-Events" > 526 <div class="img img-alt lazyload" style="" data-src=""></div> 527 <div class="img img-main lazyload" style="background-image:url('/_.gif');" data-src="/cache/img/102/folder|601&sz520x480&cp1&tn&ql&fm&bo&bc&sgadfd90ac7c&ft1674471147&bannerLifeEvents.jpg"></div> 528 <img src='/cache/img/102/folder|601&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sgadfd90ac7c&ft1674471147&bannerLifeEvents.jpg' alt='Life Events'> 529 </a> 530</div> 531 <div class="caption over top" onclick="window.location.href='/601/Life-Events'"> 532 <div class="caption-text-wrapper"> 533 <a class='pg-title-link pg-link' href='/601/Life-Events' ><h3 class='pg-title-list'>Life Events</h3></a> 534 535 <a href="/601/Life-Events">Find out more</a> 536 </div> 537 </div> 538 </div> 539 <!-- END imgBlock --> 540 </div> 541 <div class="folder-details columns medium-9 large-9 column-body"> 542 <a class='pg-title-link pg-link' href='/601/Life-Events' ><h3 class='pg-title-list'>Life Events</h3></a> 543 544 545 </div> 546</div> 547 </li> 548 549</ul> 550</div> 551</div> 552</div> 553<!-- end of content --> 554 555 </div> 556 557 <div id="content-footer"><!-- --> 558 559 </div> 560 </div> 561 </div> 562 </div> 563 564 </div> 565 </div> 566 </main> 567 <section id="body-footer"> 568 <div id="body-footer-inner" class="section-inner" ><!-- --> 569 <div id="body-footer-box"> 570 571 </div> 572 </div> 573 </section> 574 <footer id="footer"> 575 <div id="footer-inner" class="section-inner"> 576 <div id="footer-box"> 577 <div class="block-fw footer-bg"> 578<div class="section-inner"> 579<div class="section-upper"> 580<div class="footer-logo"><a href="/"><img src="/_data/site/102/_default/stMartinsWhite2.png" alt="St Martin's Church Guernsey Logo" /></a></div> 581</div> 582<p class="social-icons"><a title="facebook" href="https://www.facebook.com/StMartinsGuernsey/" target="_blank" rel="noopener noreferrer">ï</a> <a title="youtube" href="https://www.youtube.com/@stmartinschurchguernsey8426" target="_blank" rel="noopener noreferrer">ï ª</a></p> 583<p style="text-align: center;"><a href="/649/Vacancies">Vacancies</a> | <a href="/639/Contact-Us">Contact</a> | <a href="/603/Safeguarding">Safeguarding</a> | <a href="/634/Privacy-Policy">Privacy</a> | <a href="/8/Sitemap">Sitemap</a></p> 584<hr /> 585<div class="section-lower"><h6>Copyright ©
585<script type="text/javascript">// <![CDATA[ 586document.write(new Date().getFullYear()); 587// ]]></script>
587 St Martin's Church Guernsey. All Rights Reserved. Home page banner image by <a href="https://nickrowepoetry.rocks/" target="_blank" ref="noreferrer noopener">@nickrowepoetry</a>. Designed & Powered by the <a href="https://www.churchpages.co.uk" target="_blank" ref="noreferrer noopener" title="creator link">Church Pages Initiative</a></h6></div> 588</div> 589</div> 590 <span class="powered">Powered by 591 <a href='https://www.khooseller.co.uk' target='_blank'>Khooweb e-commerce website specialists</a> 592 </span> 593 </div> 594 </div> 595 </footer> 596 </div> 597 </div> 598 </div> 599 600 </div> 601 602 603
603<script src='/js/plugins/cookieconsent/dist/cookieconsent.umd.js?v=5'></script>
vendor: 1 bytes, line 603
603
604<script src='/js/alpine-init.js?v=5'></script>
vendor: 1 bytes, line 604
604
605<script> 606$(function() { 607$("#h-nav").sticky({ topSpacing: 0 }); 608}); 609 610</script>
vendor: 1 bytes, line 610
610
611<script defer src='/js/plugins/popupwindow.js?v=5'></script>
vendor: 1 bytes, line 611
611
612<script src='/js/stores/modals.js?v=5'></script>
vendor: 1 bytes, line 612
612
613<script src='/js/stores/slideout.js?v=5'></script>
vendor: 1 bytes, line 613
613
614<script src='/js/component/slideoutForm.js?v=5'></script>
vendor: 1 bytes, line 614
614
615<script src='/js/component/fieldValidator.js?v=5'></script>
vendor: 1 bytes, line 615
615
616<script src='/js/component/suggestionBox.js?v=5'></script>
vendor: 1 bytes, line 616
616
617<script src='/js/component/progressBar.js?v=5'></script>
vendor: 1 bytes, line 617
617
618<script src='/js/component/successRedirect.js?v=5'></script>
vendor: 1 bytes, line 618
618
619<script src='/js/component/tinyMceModalForm.js?v=5'></script>
vendor: 1 bytes, line 619
619
620<script defer src='/js/default_body.js?v=5'></script>
vendor: 1 bytes, line 620
620
621<script type="text/plain" data-category="analytics"> 622<!-- Matomo --> 623 var _paq = _paq || []; 624 _paq.push(['trackPageView']); 625 _paq.push(['enableLinkTracking']); 626 (function() { 627 var u="https://stats.khoosys.net/"; 628 _paq.push(['setTrackerUrl', u+'piwik.php']); 629 _paq.push(['setSiteId', '348']); 630 631 var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; 632 g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); 633 })(); 634<!-- End Matomo Code --> 635</script>
635 636 637
637<script src="/_default/js/cookie_consent.htm"></script>
637 638 <!-- ========================================================================= --> 639 <!-- Modal HTML. We wrap it in a simple x-data --> 640 <!-- to ensure it's part of Alpine's initialization process. --> 641 <!-- ========================================================================= --> 642 <div id="alpine-modal-root" class="modern-form" 643 x-data 644 @htmx:before-request="$store.modals.clearTopModalErrors()" 645 @htmx:after-request="$store.modals.handleHtmxResponse(event)" 646 @htmx:after-swap="$store.modals.bindFormValidation(event)" 647 @close-top-modal="$store.modals.close()" 648 > 649 <!-- The x-data makes this an official Alpine component --> 650 651 <!-- BEGIN: Global Alpine/HTMX Modal Stack --> 652 <template x-if="$store.modals.stack.length > 0"> <!-- THE CRITICAL FIX IS HERE --> 653 <div class="modal-stack-container" 654 @keydown.escape="$store.modals.close()" 655 x-cloak> 656 657 <!-- The blackout background. Closes the topmost modal when clicked. 658 Tracks the topmost modal's z-base (minus 1) so that a modal 659 elevated above an "-above-modal" slideout (z-base 2200) dims 660 the slideout too; the default case stays at 1999 via the CSS. --> 661 <div class="blackout-bg" @click="$store.modals.close()" 662 :style="$store.modals.top && $store.modals.top.zBase ? { zIndex: $store.modals.top.zBase - 1 } : {}"></div> 663 664 <!-- This loops through each open modal and "stacks" them visually. 665 Each modal carries its own z-base (2000 by default, 2200 when 666 opened from inside an elevated slideout â see modals.js open()), 667 keeping the dialog above the modal blackout, the slideout panel 668 (1100 / 2100 elevated), and the legacy \Core\Dialog stack (1001). --> 669 <template x-for="(modal, index) in $store.modals.stack" :key="index"> 670 <div class="dialog-wrapper" :style="{ zIndex: (modal.zBase || 2000) + index }"> 671 <div class="dialog" :style="{ maxWidth: modal.width || '600px' }"> 672 673 <!-- ======================================================= --> 674 <!-- BEGIN: "HTML" Dynamic Content from HTMX PRESET --> 675 <!-- ======================================================= --> 676 <template x-if="modal.type === 'html'"> 677 <div x-html="modal.content"></div> 678 </template> 679 680 <!-- ======================================================= --> 681 <!-- BEGIN: "FORM" PRESET --> 682 <!-- ======================================================= --> 683 <template x-if="modal.type === 'form'"> 684 <div class="dialog-content-wrapper" x-data="{ 685 title: modal.title || 'Dialog', 686 confirmText: modal.confirmText || 'Save Changes', 687 loadUrl: modal.loadUrl || '', 688 postUrl: modal.postUrl || '', 689 context: modal.context || '', 690 clearError(event) { 691 const fieldName = event.target.name; 692 // If there's a name and a corresponding error exists, delete it. 693 if (fieldName && $store.modals.top?.errors?.[fieldName]) { 694 delete $store.modals.top.errors[fieldName]; 695 // If it was a field-specific error, the form is no longer fatal. 696 // A general fatal error will remain. 697 if ($store.modals.top.isFatal && !$store.modals.top.errors.general) { 698 $store.modals.top.isFatal = false;
699 } 700 $store.modals._renderErrors(); 701 } 702 } 703 }" 704 x-init="Alpine.nextTick(() => htmx.process($el))"> 705 706 <!-- 1. The Header (Now part of the shell) --> 707 <div class="dialog-header"> 708 <div class="dialog-title" x-text="title"></div> 709 <button class="dialog-close" @click="$store.modals.close()">×</button> 710 </div> 711 712 <!-- 2. The Form --> 713 <!-- Bind fieldValidator once the body's htmx content lands so 714 .required / .digits / min / max are enforced client-side, 715 mirroring the slideout and legacy Core\Dialog. Without this 716 a `max` on a text/digits input is never checked (native 717 HTML5 max only applies to type=number). bind() self-guards 718 against the repeat afterSwaps of nested panel loads. 719 720 `context` (which can be a JSON array of every ticked row id) 721 travels as a hidden input, NOT hx-vals: hx-vals is inherited 722 by descendants, so the .dialog-body loader GET below would 723 carry the whole id list in its query string and 414 on a 724 large selection. htmx collects enclosing-form inputs on the 725 form's own POST only. --> 726 <form 727 :hx-post="postUrl" 728 hx-swap="none" 729 hx-disabled-elt="find .button-submit" 730 x-init="$el.addEventListener('htmx:afterSwap', () => window.fieldValidator && window.fieldValidator.bind($el))" 731 @input.debounce.250ms="clearError($event)" 732 > 733 <input type="hidden" name="context" :value="context" /> 734 <div class="validation-error -general" 735 data-error-general 736 style="display:none"> 737 </div> 738 <div class="dialog-body modern-form" 739 :hx-get="loadUrl" 740 hx-trigger="load" 741 hx-swap="innerHTML" 742 hx-indicator="#loading-spinner" 743 > 744 <div id="loading-spinner" class="htmx-indicator"> 745 <div class="loader"></div> 746 </div> 747 </div> 748 749 <div class="dialog-buttons"> 750 <button type="button" class="button-cancel" @click="$store.modals.close()">Cancel</button> 751 <button type="submit" class="button-submit" :disabled="$store.modals.top?.isFatal" x-text="confirmText"></button> 752 </div> 753 </form> 754 </div> 755 </template> 756 757 <!-- ======================================================= --> 758 <!-- BEGIN: "CONFIRM" PRESET --> 759 <!-- ======================================================= --> 760 <template x-if="modal.type === 'confirm'"> 761 <div class="dialog-content-wrapper"> 762 <div class="dialog-header"> 763 <div class="dialog-title" x-text="modal.title || 'Confirm'"></div> 764 <button class="dialog-close" @click="$store.modals.close()">×</button> 765 </div> 766 <div class="dialog-body"> 767 <template x-if="modal.bodyHtml"><div x-html="modal.bodyHtml"></div></template> 768 <template x-if="!modal.bodyHtml"><div class="info-box" x-text="modal.message || modal.description || 'Are you sure?'"></div></template> 769 </div> 770 <div class="dialog-buttons"> 771 <button class="button-cancel" @click="$store.modals.close()">
772 <span x-text="modal.cancelText || 'Cancel'"></span> 773 </button> 774 <button :class="modal.destructive ? 'button-destructive' : 'button-submit'" 775 @click=" 776 if (modal.okUrl) { window.location.assign(modal.okUrl); return; } 777 // Function-valued onOk (inline callers) or string method on targetId (Node::modalConfirm) 778 if (typeof modal.onOk === 'function') { 779 modal.onOk(...(modal.okArgs || [])); 780 } else if (modal.targetId && modal.onOk) { 781 const targetEl = document.getElementById(modal.targetId); 782 if (targetEl && Alpine.$data(targetEl) && typeof Alpine.$data(targetEl)[modal.onOk] === 'function') { 783 Alpine.$data(targetEl)[modal.onOk](...(modal.okArgs || [])); 784 } 785 } 786 $store.modals.close(); 787 "> 788 <span x-text="modal.okText || modal.confirmText || 'OK'"></span> 789 </button> 790 </div> 791 </div> 792 </template> 793 794 <!-- ======================================================= --> 795 <!-- BEGIN: "INPUT CONFIRM" PRESET --> 796 <!-- ======================================================= --> 797 <template x-if="modal.type === 'inputConfirm'"> 798 <!-- This component has its own internal state for the user's input --> 799 <div class="dialog-content-wrapper" x-data="{ userInput: '' }"> 800 <div class="dialog-header"> 801 <div class="dialog-title" x-text="modal.title || 'Confirm Action'"></div> 802 <button class="dialog-close" @click="$store.modals.close()">×</button> 803 </div> 804 <div class="dialog-body modern-form"> 805 <!-- The main instructional message --> 806 <div class="info-box" x-text="modal.message || 'Please confirm your action.'"></div> 807 808 <!-- The input field for validation --> 809 <div class="form-section"> 810 <div class="form-group"> 811 <input type="text" 812 x-model="userInput" 813 :placeholder="modal.requiredText" 814 autocomplete="off" 815 @keydown.enter.prevent="if (userInput === modal.requiredText) $refs.submitButton.click()" 816 /> 817 <label x-text="modal.label || 'Please type the confirmation text'"></label> 818 </div> 819 </div> 820 </div> 821 <div class="dialog-buttons"> 822 <button class="button-cancel" @click="$store.modals.close()">
823 <span x-text="modal.cancelText || 'Cancel'"></span> 824 </button> 825 <button class="button-submit" 826 x-ref="submitButton" 827 :disabled="userInput !== modal.requiredText" 828 @click=" 829 if (modal.targetId && modal.onOk) { 830 const targetEl = document.getElementById(modal.targetId); 831 if (targetEl) Alpine.$data(targetEl)[modal.onOk](...(modal.okArgs || [])); 832 } 833 $store.modals.close(); 834 "> 835 <span x-text="modal.okText || 'OK'"></span> 836 </button> 837 </div> 838 </div> 839 </template> 840 841 <!-- ======================================================= --> 842 <!-- BEGIN: "PROGRESS" PRESET (NEW!) --> 843 <!-- ======================================================= --> 844 <template x-if="modal.type === 'progress'"> 845 <!-- 846 This block is now pure structure. The logic comes from the progressBar component. 847 It's initialized with the taskId passed in the modal data object. 848 --> 849 <div class="dialog-content-wrapper" 850 x-data="progressBar(modal.taskId, modal.stoppable)" 851 x-cloak 852 > 853 <!-- The rest of your template remains exactly the same! --> 854 <div class="dialog-header"><div class="dialog-title">Processing...</div></div> 855 <div class="dialog-body"> 856 <p class="progress-status" x-text="statusText" 857 :class="hasFailed ? 'validation-error' : ''"></p> 858 <div style="background: #eee; border-radius: 5px; overflow: hidden;"> 859 <div style="width: 0%; background: #3498db; height: 20px; text-align: center; color: white; transition: width 0.3s ease;" 860 :style="{ width: percent + '%' }" 861 x-text="percent > 0 ? percent + '%' : ''" 862 :class="hasFailed ? 'bg-fail' : ''" 863 > 864 </div> 865 </div> 866 </div> 867 <div class="dialog-buttons"> 868 <!-- The new, optional Stop button --> 869 <button type="button" class="button-destructive" 870 x-show="isStoppable && !isComplete && !hasFailed && !isStopping" 871 @click="stopTask()"> 872 Stop 873 </button> 874 875 <!-- A disabled version for better UX after clicking stop --> 876 <button type="button" class="button-destructive" disabled 877 x-show="isStopping"> 878 Stopping... 879 </button> 880 881 <!-- Your existing close button for failures --> 882 <button type="button" class="button-cancel" 883 x-show="hasFailed" 884 @click="closeAndCleanup()"> 885 Close 886 </button> 887 </div> 888 </div> 889 </template> 890 891 <!-- ======================================================= --> 892 <!-- BEGIN: "SUCCESS REDIRECT" PRESET (NEW!) --> 893 <!-- ======================================================= --> 894 <template x-if="modal.type === 'success'"> 895 <!-- 896 The component is initialized with the message and URL from the modal data. 897 The @destroy directive ensures we clean up timers if the user hits Escape. 898 --> 899 <div class="dialog-content-wrapper" 900 x-data="successRedirect(modal.message, modal.redirectUrl)" 901 @destroy="destroy()" 902 x-cloak> 903 904 <div class="dialog-header"><div class="dialog-title">Success</div></div> 905 <div class="dialog-body" style="text-align: center;"> 906 <!-- You can add a success icon here if you like --> 907 <p style="margin: 1rem 0;" x-text="message"></p> 908 </div> 909 <div class="dialog-buttons"> 910 <!-- The OK button calls redirectNow() and shows the countdown --> 911 <button type="button" class="button-submit" @click="redirectNow()"> 912 OK <span x-text="'(' + countdown + ')'"></span> 913 </button> 914 </div> 915 </div> 916 </template> 917 918 </div> 919 </div> 920 </template> 921 </div> 922 </template> 923 <!-- END: Global Alpine/HTMX Modal Stack --> 924 925 </div> <!-- End of #alpine-modal-root --> 926 927 <!-- ======================================================= --> 928 <!-- SLIDE-OUT PANEL COMPONENT (Styled for .modern-form) --> 929 <!-- ======================================================= --> 930 <div id="slideout-panel-root" class="modern-form" 931 x-data="slideoutForm()" 932 > 933 <!-- Overlay --> 934 <div 935 class="slideout-overlay" 936 :class="{ '-below-topbar': $store.slideout.panelClass && $store.slideout.panelClass.indexOf('-below-topbar') !== -1, '-above-modal': $store.slideout.panelClass && $store.slideout.panelClass.indexOf('-above-modal') !== -1 }" 937 @click="handleOverlayClick()" 938 x-show="$store.slideout.isOpen && (!$store.slideout.panelClass || $store.slideout.panelClass.indexOf('-object-map-list') === -1)" 939 x-cloak 940 x-transition:enter="transition-opacity ease-in-out duration-300" 941 x-transition:enter-start="opacity-0" 942 x-transition:enter-end="opacity-100" 943 x-transition:leave="transition-opacity ease-in-out duration-300" 944 x-transition:leave-start="opacity-100" 945 x-transition:leave-end="opacity-0" 946 ></div> 947 948 <!-- Panel --> 949 <div 950 class="slideout-panel" 951 :class="$store.slideout.panelClass.split(' ').filter(function (c) { return c; })" 952 x-show="$store.slideout.isOpen" 953 x-cloak 954 x-transition:enter="transition ease-in-out duration-300 transform" 955 x-transition:enter-start="translate-x-full" 956 x-transition:enter-end="translate-x-0" 957 x-transition:leave="transition ease-in-out duration-300 transform" 958 x-transition:leave-start="translate-x-0" 959 x-transition:leave-end="translate-x-full" 960 :style="$store.slideout.currentWidth ? { width: $store.slideout.currentWidth } : {}" 961 > 962 <!-- Header --> 963 <div class="slideout-header"> 964 <div class="slideout-header-text"> 965 <h2 class="slideout-title" x-text="$store.slideout.title"></h2> 966 <p class="slideout-subtitle" x-show="$store.slideout.subt
966itle" x-text="$store.slideout.subtitle" x-cloak></p> 967 </div> 968 <div id="slideout-head-slot" class="slideout-head-slot"></div> 969 <button @click="handleCloseClick()" class="slideout-close" aria-label="Close panel">×</button> 970 </div> 971 972 <!-- Content shell: teleports (e.g. object-map list) mount as direct children; 973 htmx fragments load into #slideout-htmx-body so innerHTML swaps never 974 wipe Alpine teleport anchors on this container. --> 975 <div id="slideout-content" class="slideout-body"> 976 <div id="slideout-htmx-body" 977 hx-trigger="load-slideout-content" 978 hx-swap="innerHTML"></div> 979 </div> 980 981 <!-- Footer (form type only) --> 982 <div class="slideout-footer" 983 x-show="$store.slideout.type === 'form' && !$store.slideout.hideFooter" 984 x-cloak> 985 <div id="slideout-foot-left" class="slideout-foot-left"></div> 986 <div class="slideout-foot-actions"> 987 <button type="button" class="btn button-cancel" @click="cancelExplicit()">Cancel</button> 988 <div id="slideout-foot-actions"></div> 989 </div> 990 </div> 991 </div> 992 993 <iframe id="slideout-save-frame" name="slideout-save-frame" class="slideout-save-frame" 994 title="slideout save target" tabindex="-1" aria-hidden="true"></iframe> 995 </div> 996 997 <!-- ======================================================= --> 998 <!-- GUIDED TUTORIAL OVERLAY (engine mount point) --> 999 <!-- The player builds its dim-backdrop + popover in <body>; --> 1000 <!-- this stable root just hosts the Alpine component. --> 1001 <!-- Only mounted where the tutorial JS bundle is registered --> 1002 <!-- (admin) â the slot is left empty otherwise; see _foot.php --> 1003 <!-- ======================================================= --> 1004 1005 <!-- Hidden form â submits same data as original --> 1006 <form id="advanced-search-hidden-form" x-data 1007 hx-post="/index.php?url=/159/index.htm" 1008 hx-target="#search-results-content" 1009 hx-select="#search-results-content" 1010 hx-swap="innerHTML" 1011 style="display:none;" 1012 @htmx:after-request="handleSearchSuccess(event, 'advanced-search-template')" 1013 > 1014 <div id="advanced-search-hidden-fields"></div> 1015 </form> 1016 1017 <template id="show-loader-loader-template"> 1018 <div style="padding:80px 0; text-align:center; font-size:1.1em; color:#666;"> 1019 <div class="loader" style="display:inline-block; vertical-align:middle;"></div> 1020 </div> 1021 </template>
1022<script> 1023 document.body.addEventListener('htmx:beforeRequest', function(evt) { 1024 // Get the element that will be updated (the swap target) 1025 let target = evt.detail.target; 1026 1027 // If no explicit target, fall back to the requesting element 1028 if (!target) { 1029 const requestConfig = evt.detail.xhr; // not directly available, so use the elt 1030 target = evt.target; 1031 } 1032 1033 // Also check if the actual swap target (from hx-target on trigger) has the class 1034 // HTMX sets evt.detail.target to the swap target element 1035 if (target && target.classList && target.classList.contains('show-loader')) { 1036 const loaderTemplate = document.getElementById('show-loader-loader-template'); 1037 1038 if (loaderTemplate && !target.querySelector('.htmx-loader-overlay')) { // prevent duplicates 1039 const overlay = document.createElement('div'); 1040 overlay.className = 'htmx-loader-overlay'; 1041 1042 Object.assign(overlay.style, { 1043 position: 'absolute', 1044 top: '0', 1045 left: '0', 1046 width: '100%', 1047 height: '100%', 1048 zIndex: '100', 1049 display: 'flex', 1050 alignItems: 'center', 1051 justifyContent: 'center', 1052 backgroundColor: 'rgba(255,255,255,0.8)', 1053 backdropFilter: 'blur(2px)' 1054 }); 1055 1056 const loaderClone = loaderTemplate.content.cloneNode(true); 1057 overlay.appendChild(loaderClone); 1058 1059 // Ensure relative positioning 1060 const computedStyle = window.getComputedStyle(target); 1061 if (computedStyle.position === 'static') { 1062 target.style.position = 'relative'; 1063 } 1064 1065 target.appendChild(overlay); 1066 } 1067 } 1068 }); 1069</script>
1069 1070
1070<script> 1071 /** 1072 * Creates a reusable object with logic for managing a selection. 1073 * This is a "mixin" that we will add to our main Alpine component. 1074 * @param {string} initialId - The ID of the item that should be selected on page load. 1075 */ 1076 function selectionManagerMixin(initialId) { 1077 return { 1078 // --- Properties --- 1079 initialSelectedId: initialId, 1080 1081 // --- Methods --- 1082 initializeSelection() { 1083 // Explicit initial id (detail-context pages) â select unconditionally. 1084 if (this.initialSelectedId && this.initialSelectedId !== '0') { 1085 setTimeout(() => { 1086 Alpine.store('selection').select(this.initialSelectedId); 1087 }, 0); 1088 return; 1089 } 1090 // Read the URL-persisted selection first: clear() strips ?selrow from the 1091 // URL synchronously (history.replaceState), so reading it after is too late. 1092 let selrow = ''; 1093 try { selrow = new URL(window.location.href).searchParams.get('selrow') || ''; } catch (e) {} 1094 1095 // No server-preselected row on this page. The selection store is a 1096 // global singleton shared across pages, so a selection set on another 1097 // screen (e.g. a product-variant idx picked on the custom-price page) 1098 // survives an admin partial-swap navigation. Clear it so detail panes 1099 // keyed on $store.selection.selectedId don't fetch with a foreign id. 1100 Alpine.store('selection').clear(); 1101 1102 // Then restore that selection so a refresh keeps the same row â but only 1103 // if that row is actually present in the current list. 1104 if (selrow) this._restorePersistedSelection(selrow); 1105 }, 1106 1107 // A single setTimeout(0) races row render: on list pages where htmx/Alpine 1108 // reprocess the table after load, the row can appear a tick or two later, so 1109 // the one-shot lookup missed it and the selection was silently dropped (looked 1110 // like the selection "jumped" away on refresh). Retry across a few frames until 1111 // the row exists, then select it; give up quietly if it never shows (e.g. it's 1112 // on another paginated page). 1113 _restorePersistedSelection(selrow, attempt = 0) { 1114 const sel = (window.CSS && CSS.escape) ? CSS.escape(selrow) : selrow; 1115 if (document.querySelector('tr[data-selected-id="' + sel + '"]')) { 1116 Alpine.store('selection').select(selrow); 1117 return; 1118 } 1119 if (attempt < 20) { 1120 setTimeout(() => this._restorePersistedSelection(selrow, attempt + 1), 30); 1121 } 1122 } 1123 }; 1124 } 1125 1126 /** 1127 * The primary handler for after ANY search request completes. 1128 * It updates the search tags and resets the selection pane. 1129 */ 1130 function handleSearchSuccess(event, templateId) { 1131 if (event.detail.successful) { 1132 1133 // Get the raw payload from the request 1134 const payload = event.detail.requestConfig.parameters; 1135 1136 1137 // Set the tags using the payload 1138 Alpine.store('searchTags').setFromPayload(payload, templateId); 1139 1140 Alpine.store('selection').clear(); 1141 } else { 1142 console.error('[handleSearchSuccess] A search request was not successful.'); 1143 } 1144 } 1145 1146 1147 window.handleAdvancedSearchSuccess = (event, templateId) => { 1148 if (event.detail.successful) { 1149 Alpine.store('slideout').close(); 1150 } 1151 handleSearchSuccess(event, templateId); 1152 }; 1153 1154 /** 1155 * openBulkModal â click handler for Node::modalBulkForm bulk-action menus. 1156 * 1157 * Three scopes: 1158 * - 'selection' : collect .p-check:checked values from #list_form, bail 1159 * out with a notify if none selected, open a form modal 1160 * with the checked-id count in the loadUrl query string 1161 * and the ids JSON-encoded in `context` for the action 1162 * handler. 1163 * - 'selection-into' : like 'selection', but also needs the single 1164 * highlighted row (`$store.selection.selectedId`) as a 1165 * *target*. Bails with a notify if nothing is highlighted, 1166 * substitutes the `__TARGET__` sentinel in loadUrl/postUrl 1167 * with the target idx (so it lands in the URL path segment 1168 * the action handler reads), and excludes the target from 1169 * the checked-id sources. Used by the customer "Merge 1170 * into selected" action. 1171 * - 'results' : no id collection; the current URL's query string is 1172 * appended to both loadUrl and postUrl so the action 1173 * handler can re-run the current search. `context` is 1174 * the literal string 'results'. 1175 * 1176 * Pairs with: 1177 * - Node::modalBulkForm() â renders the trigger @click handler. 1178 * - fetch/html/*_bulk.php loader â reads ?count= / query-string 1179 * params to render the confirmation message. 1180 * - fetch/action/*_bulk.php handler â reads $oREQUEST->context to 1181 * distinguish selection (JSON array of ids) vs results (literal 1182 * 'results' plus query-string search params). 1183 */ 1184 window.openBulkModal = function(title, loadUrl, postUrl, confirmText, scope, width) { 1185 scope = scope || 'selection'; 1186 1187 if (scope === 'selection' || scope === 'selection-into') { 1188 // 'selection-into' also needs the highlighted row as a target: inject 1189 // it into the URL path (__TARGET__ sentinel) up front, then fall 1190 // through to the same checked-source collection as plain 'selection'. 1191 let target = null; 1192 if (scope === 'selection-into') { 1193 target = Alpine.store('selection')?.selectedId; 1194 if (!target) { 1195 notify('No target customer', 'Click a customer row to pick the merge target first.', 'info'); 1196 return; 1197 } 1198 loadUrl = loadUrl.replaceAll('__TARGET__', target); 1199 postUrl = postUrl.replaceAll('__TARGET__', target); 1200 } 1201 1202 const listForm = document.getElementById('list_form'); 1203 const rootSel = listForm ? listForm.querySelectorAll('input.p-check:checked') 1204 : document.querySelectorAll('#list_form input.p-check:checked'); 1205 let checked = Array.from(rootSel).map(el => el.value); 1206 // the target can't be a source (can't merge a customer into itself) 1207 if (target) checked = checked.filter(id => id !== target); 1208 if (checked.length === 0) { 1209 notify('Nothing selected', 'Tick a checkbox on at least one row first.', 'info'); 1210 return; 1211 } 1212 const sep = loadUrl.includes('?') ? '&' : '?'; 1213 Alpine.store('modals').open({ 1214 type: 'form', 1215 title: title, 1216 loadUrl: loadUrl + sep + 'count=' + checked.length, 1217 postUrl: postUrl, 1218 confirmText: confirmText || 'Confirm', 1219 context: JSON.stringify(checked), 1220 width: width || '' 1221 }); 1222 return; 1223 } 1224 1225 if (scope === 'results') { 1226 // The active filters live in the searchTags store â they're posted 1227 // to the list via htmx.ajax() without ever being written to the URL 1228 // (only chip *removal* prunes the URL). So window.location.search is 1229 // stale and would make the bulk action operate on a different set 1230 // than the one on screen. Prefer the store's authoritative filter 1231 // set; fall back to the URL for legacy pages that don't use tags. 1232 const tagStore = Alpine.store('searchTags'); 1233 let extra; 1234 if (tagStore && Array.isArray(tagStore.tags) && tagStore.tags.length) { 1235 const params = new URLSearchParams();
1236 tagStore.tags.forEach(t => { 1237 if (t.value !== '' && t.value != null) params.append(t.name, t.value); 1238 }); 1239 extra = params.toString(); 1240 } else { 1241 const qs = window.location.search || ''; 1242 extra = qs ? qs.replace(/^\?/, '') : ''; 1243 } 1244 const loadSep = loadUrl.includes('?') ? '&' : '?'; 1245 const postSep = postUrl.includes('?') ? '&' : '?'; 1246 Alpine.store('modals').open({ 1247 type: 'form', 1248 title: title, 1249 loadUrl: loadUrl + (extra ? loadSep + extra : ''), 1250 postUrl: postUrl + (extra ? postSep + extra : ''), 1251 confirmText: confirmText || 'Confirm', 1252 context: 'results', 1253 width: width || '' 1254 }); 1255 return; 1256 } 1257 1258 console.error('openBulkModal: unknown scope', scope); 1259 }; 1260 1261 /** 1262 * Global submit_list bridge for legacy Jaxon list-action dialogs. 1263 * 1264 * Pages converted to the modern htmx + modern-form blueprint no longer 1265 * include the old hidden `<form id="unisearch-form">` that the legacy 1266 * `submit_list({list_action, list_dlg_data})` callback used to populate 1267 * and submit. This shim is invoked by the Jaxon dialog `cbfunction` and 1268 * builds a temporary POST form on the fly so existing PHP `list_action` 1269 * handlers (delete / merge / email / set_group / etc.) keep working 1270 * without per-page rewires. 1271 * 1272 * If a page defines its own `submit_list` (e.g. orders.htm, which has 1273 * its own hidden legacy form for backwards compat), that wins. 1274 */ 1275 if (typeof window.submit_list !== 'function') { 1276 window.submit_list = function(data) { 1277 const checked = []; 1278 document.querySelectorAll('input.p-check:checked').forEach(el => { 1279 checked.push(el.value); 1280 }); 1281 1282 const form = document.createElement('form'); 1283 form.method = 'post'; 1284 form.action = location.pathname + location.search; 1285 form.style.display = 'none'; 1286 1287 const addField = (name, value) => { 1288 const input = document.createElement('input'); 1289 input.type = 'hidden'; 1290 input.name = name; 1291 input.value = value; 1292 form.appendChild(input); 1293 }; 1294 1295 addField('list_action', data.list_action || ''); 1296 addField('list_dlg_data', data.list_dlg_data || ''); 1297 addField('p_checked', checked.join(',')); 1298 1299 document.body.appendChild(form); 1300 form.submit(); 1301 }; 1302 } 1303 </script>
1303 1304 <!-- alpine-init.js is served via AssetCatalog ('alpine.init', registered in 1305 _default ModuleInterface::registerIncludes) so it gets the cache-buster 1306 version query. Do not re-add a hardcoded script tag here. --> 1307</body> 1308</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.