PageSourceSearch

https://archivists.ca/resources/Theme/WildApricotTextManager/wildapricot-textmanager.js

js archivists.ca collected 2026-10-02 04:51:02 UTC 10,872 bytes, 344 lines download raw bytes

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.