1var searchhelper = {}; 2 3// Load Google Custom Search script 4(function() { 5 var gcse = document.createElement('script'); 6 gcse.type = 'text/javascript'; 7 gcse.async = true; 8 gcse.src = 'https://cse.google.com/cse.js?cx=' + GOOGLE_CSE_CX; 9 var s = document.getElementsByTagName('script')[0]; 10 s.parentNode.insertBefore(gcse, s); 11})(); 12 13webResultsRenderedCallback = function(name, q, promos, results) { 14 // Remove role="main" from Google search result elements to avoid multiple main landmarks 15 var googleResultsDiv = document.getElementById('google-results'); 16 if (googleResultsDiv) { 17 var mainRoleElements = googleResultsDiv.querySelectorAll('[role="main"]'); 18 mainRoleElements.forEach(function(element) { 19 element.removeAttribute('role'); 20 }); 21 } 22}; 23 24window.__gcse || (window.__gcse = {}); 25window.__gcse.searchCallbacks = { 26 web: { 27 rendered: 'webResultsRenderedCallback', 28 }, 29}; 30 31// Perform Google search 32searchhelper.perform_google = function(queryText) { 33 if (typeof(google) != 'undefined') { 34 if (SEARCH_SITE_URL) { 35 google.search.cse.element.render({ 36 div: "google-results", 37 tag: 'searchresults-only', 38 gname: 'google-results', 39 attributes: { as_sitesearch: SEARCH_SITE_URL } 40 }); 41 } 42 else { 43 google.search.cse.element.render({ 44 div: "google-results", 45 tag: 'searchresults-only', 46 gname: 'google-results' 47 }); 48 } 49 var element = google.search.cse.element.getElement('google-results'); 50 element.execute(queryText); 51 } 52}; 53 54// Initialize search when DOM is ready 55if (document.readyState === 'loading') { 56 document.addEventListener('DOMContentLoaded', initSearch); 57} else { 58 initSearch(); 59} 60 61function initSearch() { 62 var searchInput = document.getElementById('google-search-input'); 63 var searchButton = document.getElementById('google-search-button'); 64 var googleResults = document.getElementById('google-results'); 65 var searchResults = document.getElementById('google-search-panel'); 66 67 if (!searchInput || !searchButton || !googleResults) return; 68 69 // Keyword rotation variables 70 var keywordRotation = { 71 active: true, 72 keywords: [], 73 currentIndex: 0, 74 currentText: '', 75 currentChar: 0, 76 typeTimeout: null, 77 waitTimeout: null, 78 79 init: function() { 80 var keywordsAttr = searchInput.getAttribute('data-keywords'); 81 if (keywordsAttr) { 82 try { 83 this.keywords = JSON.parse(keywordsAttr); 84 } catch(e) { 85 console.error('Invalid keywords JSON:', e); 86 return; 87 } 88 } 89 if (this.keywords.length > 0) { 90 this.startRotation(); 91 } 92 }, 93 94 startRotation: function() { 95 var self = this; 96 // Wait 1.5s before starting first keyword 97 setTimeout(function() { 98 self.nextKeyword(); 99 }, 1500); 100 }, 101 102 shouldStop: function() { 103 // Stop if input has user-entered text or is focused 104 return searchInput.value.length > 0 || document.activeElement === searchInput; 105 }, 106 107 nextKeyword: function() { 108 if (this.shouldStop()) { 109 this.scheduleNext(); 110 return; 111 } 112 113 this.currentText = this.keywords[this.currentIndex]; 114 this.currentChar = 0; 115 searchInput.placeholder = '|'; 116 this.typeNextChar(); 117 }, 118 119 typeNextChar: function() { 120 var self = this; 121 122 if (this.shouldStop()) { 123 this.scheduleNext(); 124 return; 125 } 126 127 var humanize = Math.round(Math.random() * 180) + 40; 128 129 this.typeTimeout = setTimeout(function() { 130 if (self.shouldStop()) { 131 self.scheduleNext(); 132 return; 133 } 134 135 self.currentChar++; 136 var displayText = self.currentText.substring(0, self.currentChar); 137 searchInput.placeholder = displayText + '|'; 138 139 if (self.currentChar < self.currentText.length) { 140 self.typeNextChar(); 141 } else {
142 // Finished typing this keyword, remove cursor and wait 5s 143 searchInput.placeholder = self.currentText; 144 self.waitTimeout = setTimeout(function() { 145 self.currentIndex = (self.currentIndex + 1) % self.keywords.length; 146 self.nextKeyword(); 147 }, 5000); 148 } 149 }, humanize); 150 }, 151 152 scheduleNext: function() { 153 var self = this; 154 // Check again in 4 seconds to see if we can resume 155 clearTimeout(this.typeTimeout); 156 clearTimeout(this.waitTimeout); 157 this.waitTimeout = setTimeout(function() { 158 if (!self.shouldStop()) { 159 self.nextKeyword(); 160 } else { 161 self.scheduleNext(); 162 } 163 }, 4000); 164 }, 165 166 stop: function() { 167 clearTimeout(this.typeTimeout); 168 clearTimeout(this.waitTimeout); 169 } 170 }; 171 172 // Initialize keyword rotation 173 keywordRotation.init(); 174 175 // Handle search button click 176 searchButton.addEventListener('click', function() { 177 var query = searchInput.value.trim(); 178 179 // If input is empty but placeholder has a value, use placeholder 180 if (!query && searchInput.placeholder) { 181 query = searchInput.placeholder.replace('|', '').trim(); 182 searchInput.value = query; 183 } 184 185 if (query.length > 2) { 186 googleResults.innerHTML = ""; 187 googleResults.style.display = "block"; 188 if (searchResults) { 189 searchResults.classList.add("google-search-active"); 190 searchResults.style.display = "block"; 191 } 192 searchhelper.perform_google(query); 193 } 194 }); 195 196 // Handle Enter key in search input 197 searchInput.addEventListener('keypress', function(e) { 198 if (e.keyCode === 13) { 199 e.preventDefault(); 200 var query = searchInput.value.trim(); 201 if (query.length > 2) { 202 searchButton.click(); 203 } 204 return false; 205 } 206 }); 207 208 // Close search when clicking outside 209 document.addEventListener('click', function(event) { 210 if (searchResults && searchResults.classList.contains("google-search-active")) { 211 var target = event.target; 212 var searchBanner = document.getElementById('google-search-banner'); 213 214 // Check if click is outside search area 215 if (searchBanner && !searchBanner.contains(target) && !searchResults.contains(target)) { 216 searchInput.value = ""; 217 searchInput.placeholder = DEFAULT_SEARCH_PLACEHOLDER; 218 if (googleResults) googleResults.innerHTML = ""; 219 searchResults.classList.remove("google-search-active"); 220 searchResults.style.display = "none"; 221 } 222 } 223 }); 224}
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.