1// Wild Apricot Text Manager Library 2// v0.7 3 4/* Change Log: 5 v0.7 Add IE11 support 6 v0.6 Add support for SCSS-style variables, fix bug in cookies 7 v0.5 Add support for :after and :before pseudo-elements in CSS 8 v0.4 Add support for dynamic menu CSS 9 v0.3 Added CSS management and overlay to hide script behavior 10 v0.2 Added alert for incorrect saved CSV file 11 v0.1 Initial Release 12*/ 13 14var list = []; 15var array; 16var scss_dict = {}; 17var data_url = "/resources/Theme/WildApricotTextManager/wildapricot-textmanager-config.csv"; 18 19$(document).ready(function () { 20 21 console.log("[watm] Wild Apricot Text Manager loaded"); 22 23 // Set Defaults 24 if (typeof textManagerDataURL != 'undefined') 25 data_url = textManagerDataURL; 26 if (typeof textManagerProductionMode == 'undefined') 27 textManagerProductionMode = false; 28 if (typeof textManagerMultilingualMode == 'undefined') 29 textManagerMultilingualMode = false; 30 if (typeof primaryLanguageButtonName == 'undefined') 31 primaryLanguageButtonName = "English"; 32 if (typeof alterativeLanguageButtonName == 'undefined') 33 alterativeLanguageButtonName = "Français"; 34 if (typeof alterativeLanguageClassName == 'undefined') 35 alterativeLanguageClassName = ".french"; 36 if (typeof primaryLanguageClassName == 'undefined') 37 primaryLanguageClassName = ".english"; 38 if (typeof languageButtonHtmlID == 'undefined') 39 languageButtonHtmlID = "languageButton"; 40 41 // Multiligual Mode 42 if (textManagerMultilingualMode) 43 // Use Content Gadget if exists 44 if ($('#languageButton').length) 45 $('#languageButton').append('<div><button type="button" id="languageToggle">Toggle Second Language</button></div>'); 46 // Else show default language toggle 47 else 48 $('#mLayout').prepend('<div><button type="button" id="languageToggle">Toggle Second Language</button></div>'); 49 50 51 // Production Mode 52 if (textManagerProductionMode) 53 setCookie('ReplaceText','True'); 54 // Development Mode 55 else 56 $('#mLayout').prepend('<div><button type="button" id="textToggle">Toggle Replacement Text</button></div>'); 57 58 // Set Cookie for English Text Replacement 59 $('#textToggle').click(function() { 60 if (getCookie('ReplaceText') == "True") 61 setCookie('ReplaceText','False'); 62 else 63 setCookie('ReplaceText','True'); 64 location.reload(); 65 }); 66 67 // Set Development Button Name 68 if (getCookie('ReplaceText') == "True") 69 $('#textToggle').text("Text Manager Disable"); 70 else 71 $('#textToggle').text("Text Manager Enable"); 72 73 74 // Set Cookie for Second Language Replacement 75 $('#languageToggle').click(function() { 76 if (getCookie('SecondLanguage') == "True") 77 setCookie('SecondLanguage','False'); 78 else 79 setCookie('SecondLanguage','True'); 80 location.reload(); 81 }); 82 83 84 85 if (getCookie('SecondLanguage') == "True") 86 $('#languageToggle').text(primaryLanguageButtonName); // "Second Language On" 87 else 88 $('#languageToggle').text(alterativeLanguageButtonName); // "Second Language Off" 89 90 91 92 93 94// Load only if Formstack isn't detected. 95if(!$('form.fsForm')[ 0 ]) 96{ 97 // Open file server side 98 $.ajax({ 99 url: data_url, 100 //url: newpath_data_url, 101 async: true, 102 success: function (csvd) { 103 try { 104 array = $.csv.toArrays(csvd); 105 } 106 catch (err) { 107 alert(err.message + " Did you Save as .CSV UTF-8?"); 108 } 109 }, 110 dataType: "text", 111 complete: function () { 112 arrayToStruct(array); 113 if (getCookie('ReplaceText') == "True") 114 list.map(replaceText); 115 } 116 117 }); 118 119} 120 121 122}); 123 124 125// Page finished loading 126$(window).bind("load", function() { 127 $('#textmanager_overlay').css('display', 'none'); // Remove the white overlay 128 129 // Trigger Wild Apricot's resizeMenu() 130 if (detectIE()) { // IE 11 131 var resizeEvent = window.document.createEvent('UIEvents'); 132 resizeEvent .initUIEvent('resize', true, false, window, 0); 133 window.dispatchEvent(resizeEvent); 134 } 135 else // All other browsers 136 window.dispatchEvent(new Event('resize')); 137 138 if (getCookie('ReplaceText') == "True") 139 list.map(replaceText); // Replace text again to rewrite event handlers for the hover in menu 140 141}); 142 143function setCookie(key, value) { 144 var expires = new Date(); 145 expires.setTime(expires.getTime() + (1 * 24 * 60 * 60 * 1000)); 146 document.cookie = key + '=' + value + ';path=/;expires=' + expires.toUTCString(); 147} 148 149function getCookie(key) { 150 var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)'); 151 return keyValue ? keyValue[2] : null; 152} 153 154 155 156function replaceText(data) { 157 158 // Language Show/Hide Custom Content Block 159 if (getCookie('SecondLanguage') == "True") { 160 var replacement_text = data.second_language_text; 161 $(alterativeLanguageClassName).show(); 162 $(primaryLanguageClassName).hide(); 163 } 164 else { 165 var replacement_text = data.english_text; 166 $(alterativeLanguageClassName).hide(); 167 $(primaryLanguageClassName).show(); 168 } 169 170 if (data.function == "hide") 171 $(data.query).hide(); 172 173 // SCSS 174 if (data.function.toLowerCase() == "scss") { 175 var key, value; 176 key = data.query.split('=')[0]; // String before = 177 value = data.query.split('=')[1]; // String after = 178 scss_dict[key] = value; 179 console.log("[watm] SCSS Variable Added: " + key + "=" + value); 180 } 181 182 // Check to see if any replacement text in the column 183 if (replacement_text.length > 0) { 184 185 // Delay these replacements by 1 second, and keep polling every second 186 if (data.function == "delay") { 187 //setTimeout(function(){ $(data.query).text(replacement_text);}, 1000); 188 //delay_list.push(data); 189 setInterval(function(){ $(data.query).text(replacement_text);}, 1000); 190 } 191 192 // Replace text on buttons 193 if (data.function == "button") 194 $(data.query).val(replacement_text); 195 196 // Replace text in search boxes 197 if (data.function == "placeholder") 198 $(data.query).attr("placeholder", replacement_text); 199 200 // Special function to replace substring after 1s delay 201 // Used for shopping cart "Member price" 202 if (data.function == "replace_delay") { 203 setTimeout(function(){ 204 node = $(data.query)[ 0 ]; 205 if (node) 206 node.innerText = node.innerText.replace(data.default_text, replacement_text); 207 }, 1000); 208 } 209 210 // Search and replace 211 if (data.function == "replace" || data.function == "replace_element") { 212 var begin_node = document.body; 213 // Walk a subset of the DOM based on Query 214 if (data.query.length > 0) { 215 var query_array = data.query.split(" "); 216 // Multiple Query 217 if (query_array.length > 0) 218 for (query in query_array) { 219 begin_node = $(query_array[query])[ 0 ]; // Get first DOM element 220 if (begin_node) 221 walkText(begin_node, data, replacement_text); 222 } 223 // Single Query 224 else { 225 begin_node = $(data.query)[ 0 ]; // Get first DOM element 226 if (begin_node) 227 walkText(begin_node, data, replacement_text); 228 } 229 } 230 // Empty Query so walk the whole page 231 else 232 walkText(begin_node, data, replacement_text); 233 } 234 235 // Replace text on all other elements 236 if (data.function == "text") 237 $(data.query).text(replacement_text); 238 239 } 240 241 // CSS 242 if (data.function != "inactive" && data.style.length > 0 && data.style[0] == '{') { 243 try { 244 245 // SCSS 246 if (data.style.includes('$')) { 247 var regex = /\$[a-zA-Z0-9_-]+/g; 248 var keys = data.style.match(regex); 249 for (c = 0; c < keys.length; c++) { 250 var key = keys[c]; 251 var value = scss_dict[key]; 252 console.log("[watm] SCSS Debug " + c + " Key:" + key + " Value:" + value); 253 data.style = data.style.replace(key, value); 254 } 255 } 256 257 258 if (data.query.includes(":before") || data.query.includes(":after")) // Pseudoelements 259 $('head').append("<style>" + data.query + data.style + "</style>"); 260 else if (data.function == "mouseover") // Hover 261 $(data.query).each(function(){ $(this).mouseover(function(){ $(this).css(JSON.parse(data.style)); }); }); 262 else if (data.function == "mouseout") // End hover 263 $(data.query).each(function(){ $(this).mouseout(function(){ $(this).css(JSON.parse(data.style)); }); }); 264 else 265 $(data.query).css(JSON.parse(data.style)); 266 } 267 catch(err) { 268 console.log("[watm] Error Row " + data.row + " -- " + data.style); 269 console.error(err); 270 } 271 } 272} 273 274 function walkText(node, data, text) { 275 try { 276 if (node.nodeType == 3) { 277 if (node.data.search(data.default_text) > -1) 278 if (data.function == "replace_element") 279 node.data = text; 280 else if (data.function == "replace") 281 node.data = node.data.replace(data.default_text, text); 282 } 283 if (node.nodeType == 1 && node.nodeName != "SCRIPT") 284 for (var i = 0; i < node.childNodes.length; i++) 285 walkText(node.childNodes[i], data, text); 286 } 287 catch(err) { 288 console.log("[watm] Error Row " + data.row + " -- " + data.default_text); 289 console.error(err); 290 } 291} 292 293function arrayToStruct(array) { 294 for(var row in array) { 295 var data = { 296 container : array[row][0], 297 default_text : array[row][1], 298 english_text : array[row][2], 299 second_language_text : array[row][3], 300 note : array[row][4], 301 function : array[row][5], 302 query : array[row][6],
303 style : array[row][7], 304 row : row 305 } 306 list.push(data); 307 } 308} 309 310// Adds IE 11 Support 311if (!String.prototype.includes) { 312 Object.defineProperty(String.prototype, 'includes', { 313 value: function(search, start) { 314 if (typeof start !== 'number') { 315 start = 0 316 } 317 318 if (start + search.length > this.length) { 319 return false 320 } else { 321 return this.indexOf(search, start) !== -1 322 } 323 } 324 }) 325} 326 327function detectIE() { 328 var ua = window.navigator.userAgent; 329 var msie = ua.indexOf('MSIE '); 330 if (msie > 0) { 331 // IE 10 or older => return version number 332 return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); 333 } 334 335 var trident = ua.indexOf('Trident/'); 336 if (trident > 0) { 337 // IE 11 => return version number 338 var rv = ua.indexOf('rv:'); 339 return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); 340 } 341 342 // other browser 343 return false; 344}
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.