1(function() { 2 "use strict"; 3 4 var CONFIG = { 5 siteUrl: _spPageContextInfo.webAbsoluteUrl, 6 listTitle: "ServicioCiudadano", 7 select: "Title,FileRef,Url,Publicado,Orden", 8 orderBy: "Orden", 9 ascending: true, 10 itemsPerPage: 100 11 }; 12 13 var state = { 14 items: [], 15 visibleRows: 1, 16 columns: 5, 17 expanded: false 18 }; 19 20 var grid = null; 21 var moreWrapper = null; 22 var moreButton = null; 23 24 function escapeHtml(value) { 25 return String(value == null ? "" : value).replace(/[&<>"']/g, function(char) { 26 return { 27 "&": "&", 28 "<": "<", 29 ">": ">", 30 '"': """, 31 "'": "'" 32 }[char]; 33 }); 34 } 35 36 function isPublished(value) { 37 if (value === true || value === 1) { 38 return true; 39 } 40 41 if (value === false || value === 0 || value === null || value === undefined) { 42 return false; 43 } 44 45 var normalized = String(value).trim().toLowerCase(); 46 47 return normalized === "true" || 48 normalized === "1" || 49 normalized === "sÃ" || 50 normalized === "si" || 51 normalized === "yes"; 52 } 53 54 function getUrl(value) { 55 if (value && typeof value === "object") { 56 return value.Url || value.url || "#"; 57 } 58 59 return typeof value === "string" && value.trim() 60 ? value 61 : "#"; 62 } 63 64 function createCard(item) { 65 var title = escapeHtml(item.Title || "Servicio"); 66 var image = escapeHtml(item.FileRef || ""); 67 var url = escapeHtml(getUrl(item.Url)); 68 69 return ( 70 '<a class="sc-card" href="' + url + '" target="_blank" rel="noopener noreferrer">' + 71 '<span class="sc-card-image">' + 72 ( 73 image
74 ? '<img src="' + image + '" alt="' + title + '" loading="lazy">' 75 : '<span class="sc-card-image-placeholder"></span>' 76 ) + 77 '</span>' + 78 '<span class="sc-card-content">' + 79 '<span class="sc-card-title">' + title + '</span>' + 80 '<span class="sc-card-action">' + 81 'Consultar servicio' + 82 '<i class="fa-solid fa-arrow-right"></i>' + 83 '</span>' + 84 '</span>' + 85 '</a>' 86 ); 87 } 88 89 function getColumns() { 90 if (window.innerWidth <= 600) { 91 return 1; 92 } 93 94 if (window.innerWidth <= 900) { 95 return 2; 96 } 97 98 if (window.innerWidth <= 1150) { 99 return 3; 100 } 101 102 if (window.innerWidth <= 1350) { 103 return 4; 104 } 105 106 return 5; 107 } 108 109 function getVisibleItems() { 110 var visibleCount = state.visibleRows * state.columns; 111 112 return state.items.slice(0, visibleCount); 113 } 114 115 function render() { 116 if (!grid) { 117 return; 118 } 119 120 state.columns = getColumns(); 121 122 var visibleItems = getVisibleItems(); 123 124 grid.innerHTML = visibleItems.map(createCard).join(""); 125 126 updateButton(); 127 } 128 129 function updateButton() { 130 if (!moreWrapper || !moreButton) { 131 return; 132 } 133 134 var totalItems = state.items.length; 135 var totalRows = Math.ceil(totalItems / state.columns); 136 137 if (totalRows <= 1) { 138 moreWrapper.style.display = "none"; 139 return; 140 } 141 142 moreWrapper.style.display = "flex"; 143 144 var text = moreButton.querySelector(".sc-more-text"); 145 var icon = moreButton.querySelector(".sc-more-icon i"); 146 147 if (!text || !icon) { 148 return; 149 } 150 151 if (state.expanded) { 152 text.textContent = "Ocultar"; 153 icon.className = "fa-solid fa-chevron-up"; 154 moreButton.classList.add("is-expanded"); 155 } else { 156 text.textContent = "Ver más"; 157 icon.className = "fa-solid fa-chevron-down"; 158 moreButton.classList.remove("is-expanded"); 159 } 160 } 161 162 function showMore() { 163 var totalRows = Math.ceil(state.items.length / state.columns); 164 165 if (state.visibleRows < totalRows) { 166 state.visibleRows++; 167 } 168 169 if (state.visibleRows >= totalRows) { 170 state.expanded = true; 171 } 172 173 render(); 174 } 175 176 function hideMore() { 177 state.visibleRows = 1; 178 state.expanded = false; 179 180 render(); 181 182 var section = document.getElementById("servicioCiudadano"); 183 184 if (section) { 185 section.scrollIntoView({ 186 behavior: "smooth", 187 block: "start" 188 }); 189 } 190 } 191 192 function handleMoreClick() { 193 if (state.expanded) { 194 hideMore(); 195 } else { 196 showMore(); 197 } 198 } 199 200 function loadServices() { 201 var endpoint = 202 CONFIG.siteUrl + 203 "/_api/web/lists/getbytitle('" + 204 encodeURIComponent(CONFIG.listTitle) + 205 "')/items?$select=" + 206 CONFIG.select + 207 "&$orderby=" + 208 CONFIG.orderBy + 209 " " + 210 (CONFIG.ascending ? "asc" : "desc") + 211 "&$top=" + 212 CONFIG.itemsPerPage; 213 214 fetch(endpoint, { 215 method: "GET", 216 headers: { 217 "Accept": "application/json;odata=verbose" 218 }, 219 credentials: "same-origin" 220 }) 221 .then(function(response) { 222 if (!response.ok) { 223 throw new Error("HTTP " + response.status); 224 } 225 226 return response.json(); 227 }) 228 .then(function(data) { 229 var items = 230 data && 231 data.d && 232 Array.isArray(data.d.results) 233 ? data.d.results 234 : []; 235 236 state.items = items 237 .filter(function(item) { 238 return isPublished(item.Publicado); 239 }) 240 .sort(function(a, b) { 241 return (Number(a.Orden) || 0) - (Number(b.Orden) || 0); 242 }); 243 244 state.visibleRows = 1; 245 state.expanded = false; 246 247 render(); 248 }) 249 .catch(function(error) { 250 console.error( 251 "ServicioCiudadano: error cargando servicios.", 252 error 253 ); 254 255 state.items = []; 256 257 if (grid) { 258 grid.innerHTML = 259 '<div class="sc-empty">' + 260 '<span>No fue posible cargar los servicios.</span>' + 261 '</div>'; 262 } 263 264 if (moreWrapper) { 265 moreWrapper.style.display = "none"; 266 } 267 }); 268 } 269
270 function init() { 271 grid = document.getElementById("servicioCiudadanoGrid"); 272 moreWrapper = document.getElementById("servicioCiudadanoMore"); 273 moreButton = document.getElementById("scMoreButton"); 274 275 if (!grid) { 276 return; 277 } 278 279 if (moreButton) { 280 moreButton.addEventListener("click", handleMoreClick); 281 } 282 283 window.addEventListener("resize", function() { 284 var oldColumns = state.columns; 285 var newColumns = getColumns(); 286 287 if (oldColumns !== newColumns) { 288 state.columns = newColumns; 289 state.visibleRows = 1; 290 state.expanded = false; 291 292 render(); 293 } 294 }); 295 296 loadServices(); 297 } 298 299 if (document.readyState === "loading") { 300 document.addEventListener("DOMContentLoaded", init); 301 } else { 302 init(); 303 } 304 305})();
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.