1// Fix galleries 2 3jQuery(document).ready(function($) { 4 5 // Check if required CSS and JS assets exist in the DOM 6 const hasStylesheet = document.getElementById('cot-gallery-style-css') !== null; 7 const hasScript = document.getElementById('cot-gallery-js-js') !== null; 8 9 function addStylesheet(id, href, version = '6.8.2', media = 'all') { 10 11 // Check if stylesheet already exists 12 13 if ( hasStylesheet ) return 14 15 const link = document.createElement('link'); 16 link.id = id; 17 link.rel = 'stylesheet'; 18 link.href = href + '?ver=' + version; 19 link.media = media; 20 document.head.appendChild(link); 21 22 } 23 24 function addScript(id, src, version = '6.8.2') { 25 26 // Check if script already exists 27 if ( hasScript ) return 28 29 const script = document.createElement('script'); 30 script.id = id; 31 script.src = src + '?ver=' + version; 32 document.body.appendChild(script); 33 34 script.onload = function() { 35 36 $('.cotImageGallery').each(function() { 37 if (typeof window.CotImageGallery === 'function') { 38 39 const imagesConfig = this.id.replace('cotImageGallery_', 'imagesgalleryJSON_'); 40 let galleryConfig = { 41 'id': this.id, 42 'title': 'Image Gallery - Photo Gallery ', 43 'images': window[imagesConfig], 44 'url': '' 45 } 46 new window.CotImageGallery( galleryConfig ); 47 } 48 }); 49 50 } 51 52 } 53 54 // Only add assets if .cotImageGallery element exists on the page 55 if ($('.cotImageGallery').length > 0) { 56 addStylesheet( 57 'cot-gallery-style-css', 58 'https://www.toronto.ca/resources/cot_imagegallery/cotimagegallery.css' 59 ); 60 61 addScript( 62 'cot-gallery-js-js', 63 'https://www.toronto.ca/resources/cot_imagegallery/cotimagegallery.js' 64 ); 65 } 66 67 68 function extractQuotedValue(str, quoteStartIndex) { 69 // quoteStartIndex points to the opening quote char (either ' or ") 70 const quote = str[quoteStartIndex]; 71 let i = quoteStartIndex + 1; 72 let out = ''; 73 let escaped = false; 74 while (i < str.length) { 75 const ch = str[i]; 76 if (escaped) { 77 out += ch; 78 escaped = false; 79 } else if (ch === '\\') { 80 escaped = true; 81 } else if (ch === quote) { 82 return { value: out, endIndex: i }; 83 } else { 84 out += ch; 85 } 86 i++; 87 } 88 return null; // unterminated string 89 } 90 91 $('.cotImageGallery').each(function (index, el) { 92 93 const $el = $(el); 94 const oldScript = $el.next('script')[0]; 95 const galleryId = el.id || ('cotImageGallery_' + index); 96 const imagesConfig = galleryId.replace('cotImageGallery_', 'imagesgalleryJSON_'); 97 98 // Default title if none found 99 let titleValue = 'Image Gallery - Photo Gallery'; 100 let imagesJsonData = '[]'; // Default empty array 101 102 if (oldScript && oldScript.textContent) { 103 const txt = oldScript.textContent; 104 105 // Extract the imagesgalleryJSON_ variable declaration 106 const jsonVarMatch = txt.match(new RegExp(`var\\s+${imagesConfig}\\s*=\\s*(\\[.*?\\]);`, 's')); 107 if (jsonVarMatch) { 108 try { 109 // Parse the JSON data to escape alt and caption properties 110 const parsedData = JSON.parse(jsonVarMatch[1]); 111 112 // Process each image object to escape alt and caption 113 const processedData = parsedData.map(image => { 114 if (image.alt && typeof image.alt === 'string') { 115 // Escape single quotes and backslashes in alt text 116 //image.alt = image.alt.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); 117 image.alt = image.alt.replace(/&/g, "&'"); 118 } 119 if (image.caption && typeof image.caption === 'string') { 120 // Escape single quotes and backslashes in caption text 121 //image.caption = image.caption.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); 122 image.caption = image.caption.replace(/&/g, "&'"); 123 } 124 return image; 125 }); 126 127 imagesJsonData = JSON.stringify(processedData); 128 } catch (e) { 129 // If parsing fails, use original data 130 imagesJsonData = jsonVarMatch[1]; 131 } 132 } 133 134 // Find "title" key (with either single or double quotes)
135 const keyMatch = txt.match(/['"]title['"]\s*:/); 136 if (keyMatch) { 137 // position of colon after the key 138 const colonIndex = txt.indexOf(':', keyMatch.index + keyMatch[0].length - 1); 139 if (colonIndex !== -1) { 140 // skip whitespace to find opening quote 141 let i = colonIndex + 1; 142 while (i < txt.length && /\s/.test(txt[i])) i++; 143 if (i < txt.length && (txt[i] === '\'' || txt[i] === '"')) { 144 const extracted = extractQuotedValue(txt, i); 145 if (extracted) { 146 titleValue = extracted.value; 147 } 148 } else { 149 // title may be unquoted (rare). Attempt to read until comma/}) 150 const m = txt.slice(i).match(/([^\r\n,}]+)/); 151 if (m) { 152 titleValue = m[1].trim().replace(/[,}]+$/, '').trim(); 153 } 154 } 155 } 156 } 157 } 158 159 // Use JSON.stringify to safely produce a JS string literal (includes surrounding quotes) 160 const safeTitleLiteral = JSON.stringify(titleValue); 161 162 // Build new script content including the JSON data variable 163 const scriptContent = ( 164 `var ${imagesConfig} = ${imagesJsonData}; 165 window.addEventListener('DOMContentLoaded', function(){ 166 try { 167 var ${galleryId} = new CotImageGallery({ 168 id: '${galleryId}', 169 autorefresh: false, 170 title: ${safeTitleLiteral}, 171 images: ${imagesConfig}, 172 url: '' 173 }); 174 } catch (e) { 175 console && console.error && console.error('Failed to init CotImageGallery for ${galleryId}:', e); 176 } 177 });` 178 ); 179 180 // Create new script and replace 181 const newScript = document.createElement('script'); 182 newScript.textContent = scriptContent; 183 184 if (oldScript && oldScript.parentNode) { 185 oldScript.parentNode.replaceChild(newScript, oldScript); 186 } else { 187 // append near the element if no old script found 188 $el.after(newScript); 189 } 190 191 // Optionally initialize immediately if CotImageGallery constructor is already available 192 if (typeof window.CotImageGallery === 'function') { 193 try { 194 new window.CotImageGallery({ 195 id: galleryId, 196 title: titleValue, 197 images: window[imagesConfig], 198 url: '' 199 }); 200 } catch (e) { 201 /* ignore init error; script will run on DOMContentLoaded */ 202 } 203 } 204 }); 205 206}); 207 208// Add top bar for older versions 209 210jQuery( document ).ready( function( $ ) { 211 212if ( !$( 'body' ).hasClass( 'cot-ver-v1-5-0a' ) ) return; 213 214// Top Bar - Added by Kevin 215 216const addContact311 = () => { 217 218 if ( jQuery( 'body' ).hasClass( 'event-template' ) ) return false; 219 220 $( '#top-bar' ).append( '<div class="top-bar--contact-311"><a href="/311" data-wt_params="WT.link_type=calltoaction">Contact 311</a></div>' ) 221 222 setTimeout( function() { 223 224 $( '#main-nav > ul > li.menu-list-item:last-of-type .dropdown a:first-of-type' ).attr( 'data-wt_params', 'WT.link_type=calltoaction' ) 225 226 $( '#main-nav > ul > li.menu-list-item:last-of-type button' ).removeAttr( 'aria-expanded' ).text( 'Contact 311' ).click( (event) => { 227 228 event.stopPropagation() 229 230 const dropdown = $( event.target ).parent().next() 231 232 $( 'a:first-of-type', dropdown )[0].click() 233 234 }) 235 236 }, 1000 ) 237 238} 239 240addContact311(); 241 242}) 243 244setTimeout( function() { 245 if ( $( 'body' ).hasClass( 'event-template' ) ) { 246 const excludedPageIDs = [ 1005453, 946390 ]; 247 const isExcluded = excludedPageIDs.some( id => $( 'body' ).hasClass( 'page-id-' + id ) ); 248 249 if ( !isExcluded ) { 250 const jqueryScript = $( 'script[src*="/wp-content/themes/cot/js/jquery-2.2.4.min.js"]' ); 251 const newJqueryScript = $( '<script type="text/javascript" src="/wp-content/themes/cot/js/jquery-3.6.0.min.js"></script>' ); 252 jqueryScript.replaceWith( newJqueryScript ); 253 const bootstrapScript = $( 'script[src*="/wp-content/themes/cot/js/bootstrap.3.4.1.min.js"]' ); 254 const newBootstrapScript = $( '<script type="text/javascript" src="/wp-content/themes/cot/js/bootstrap.min.js"></script>' ); 255 bootstrapScript.replaceWith( newBootstrapScript ); 256 } 257 if ( $( '#fepe_nuit_blanche_archive_container' ).length ) { 258 const appScript = $( 'script[src="/resources/fepe_nuit_blanche_archive/scripts/main1.3.1.js"]' ); 259 const newAppScript = $( '<script type="text/javascript" src="/resources/fepe_nuit_blanche_archive/scripts/main1.3.1.js"></script>' ); 260 appScript.replaceWith( newAppScript ); 261 } 262 } 263}, 500 );
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.