PageSourceSearch

https://www.arbo.org/dist-v1/js/common.js?rnd=15593230672068133319

js arbo.org collected 2026-09-24 21:50:46 UTC 15,867 bytes, 527 lines download raw bytes

1$(document).ready(function () {
2  $.fn.serializeFormJSON = function () {
3    var o = {};
4    var a = this.serializeArray();
5    $.each(a, function () {
6      if (o[this.name]) {
7        if (!o[this.name].push) {
8          o[this.name] = [o[this.name]];
9        }
10        o[this.name].push(this.value || "");
11      } else {
12        o[this.name] = this.value || "";
13      }
14    });
15    return o;
16  };
17
18  $.fn.showBs = function () {
19    $(this).removeClass("d-none");
20  };
21
22  $.fn.hideBs = function () {
23    $(this).addClass("d-none");
24  };
25
26  $.fn.resetForm = function () {
27    $(this)[0].reset();
28  };
29
30  $.fn.updateProgress = function (progress) {
31    if (progress <= 100) {
32      $(this).attr("aria-valuenow", progress);
33      $(this)
34        .find(".progress-bar")
35        .css("width", progress + "%");
36    }
37  };
38});
39
40//d-none
41
42class Utils {
43  constructor() {}
44
45  static modalShow(selector) {
46    const el = document.querySelector(selector);
47    const modal = bootstrap.Modal.getOrCreateInstance(el);
48    modal.show();
49  }
50
51  static modalHide(selector) {
52    const el = document.querySelector(selector);
53    const modal = bootstrap.Modal.getInstance(el);
54    if (modal) {
55      if (el.contains(document.activeElement)) {
56        document.activeElement.blur(); // remove focus from inside the modal
57      }
58      modal.hide();
59    }
60  }
61
62  static joinStringsWith(glue, items) {
63    const filtered = items.filter(function (item) {
64      return item !== undefined && item !== null && item.trim() !== "";
65    });
66    if (filtered.length === 0) {
67      return "N/A";
68    }
69    return filtered.join(glue);
70  }
71
72  static joinStringsWithAndReturnNull(glue, items) {
73    const filtered = items.filter(function (item) {
74      return item !== undefined && item !== null && item.trim() !== "";
75    });
76    if (filtered.length === 0) {
77      return null;
78    }
79    return filtered.join(glue);
80  }
81
82  static strOrNA(item) {
83    if (item !== undefined && item !== null && item.trim() !== "") {
84      return item.trim();
85    }
86    return "N/A";
87  }
88
89  static isNullOrEmpty(item) {
90    if (item === undefined || item === null || item.trim() === "") {
91      return true;
92    }
93    return false;
94  }
95
96  static getLeadingZeros(str) {
97    str = str + "";
98    if (str.length >= 5) {
99      return str;
100    }
101    return "0".repeat(5 - str.length) + str;
102  }
103
104
105  static formatPhone(phone, isHtml) {
106    if (phone === undefined || phone === null || phone.trim() === "") {
107      return "N/A";
108    }
109
110    if (isHtml) {
111      let formattedPhone = Utils.escapeHtml(phone).replace(/[^0-9+]/g, "");
112      //add dashes to the phone number for better readability
113      if (formattedPhone.length === 10) {
114        formattedPhone =
115          formattedPhone.substring(0, 3) +
116          "-" +
117          formattedPhone.substring(3, 6) +
118          "-" +
119          formattedPhone.substring(6);
120      } else if (formattedPhone.length === 11 && formattedPhone.startsWith("1")) {
121        formattedPhone =
122          formattedPhone.substring(0, 1) +
123          "-" +
124          formattedPhone.substring(1, 4) +
125          "-" +
126          formattedPhone.substring(4, 7) +
127          "-" +
128          formattedPhone.substring(7);
129      }
130
131      return "<a href=\"tel:" + phone.replace(/[^0-9+]/g, "") + "\" class=\"text-nowrap\">" + formattedPhone  + "</a>";
132    }
133
134    return Utils.escapeHtml(phone);
135  }
136
137  static makeVisible(selector) {
138    $(selector).removeClass("d-none");
139    //$(selector).addClass("d-block");
140  }
141
142  static makeInvisible(selector) {
143    //$(selector).removeClass("d-block");
144    $(selector).addClass("d-none");
145  }
146
147  static setupFiles(path, subfolder, selector = ".async-file") {
148    //let selector = ".async-file";
149    $(selector).filepond({
150      allowMultiple: false,
151      allowImagePreview: false,
152      maxFileSize: "30MB",
153      credits: "",
154      maxFiles: 1,
155      allowDrop: false,
156      required: true,
157      labelIdle:
158        " <span class='filepond--label-action''> Browse your files</span>",
159      server: {
160        process: function (
161          fieldName,
162          file,
163          metadata,
164          load,
165          error,
166          progress,
167          abort,
168          transfer,
169          options,
170        ) {
171          const request = new XMLHttpRequest();
172          $.ajax({
173            type: "POST",
174            url: path,
175            data: { name: file.name, folder: subfolder },
176            success: function (ajaxData, textStatus, jqXHR) {
177              if (textStatus === "success") {
178                if (ajaxData.status === "success") {
179                  console.log(ajaxData);
180                  // fieldName is the name of the input field
181                  // file is the actual file object to send
182                  const formData = new FormData();
183
184                  request.open(
185                    ajaxData.attributes.method,
186                    ajaxData.attributes.action,
187                  );
188
189                  for (const property in ajaxData.inputs) {
190                    formData.append(property, ajaxData.inputs[property]);
191                  }
192
193                  formData.append("file", file, file.name);
194
195                  // Should call the progress method to update the progress to 100% before calling load
196                  // Setting computable to false switches the loading indicator to infinite mode
197                  request.upload.onprogress = (e) => {
198                    progress(e.lengthComputable, e.loaded, e.total);
199                    //console.log(e);
200                  };
201
202                  // Should call the load method when done and pass the returned server file id
203                  // this server file id is then used later on when reverting or restoring a file
204                  // so your server knows which file to return without exposing that info to the client
205                  request.onload = function () {
206                    if (request.status >= 200 && request.status < 300) {
207                      //load(request.responseText);
208                      load(ajaxData.inputs.key);
209                      //console.log(this);
210                    } else {
211                      // Can call the error method if something is wrong, should exit after
212                      error("oh no");
213                    }
214                  };
215                  request.send(formData);
216                } else {
217                }
218              }
219            },
220            dataType: "json",
221          });
222
223          return {
224            abort: function () {
225              request.abort();
226              abort();
227            },
228          };
229        },
230        remove: (source, load, error) => {
231          load();
232        },
233        revert: (uniqueFileId, load, error) => {
234          //console.log("revert", uniqueFileId);
235          //console.log(this);
236          load();
237        },
238      },
239    });
240    $(selector).on("FilePond:removefile", function () {
241      //console.log("FilePond:removefile");
242      if ($(this).next(".d-none").length > 0) {
243        $(this).next(".d-none").val("");
244      }
245    });
246    $(selector).on("FilePond:processfile", function () {
247      //console.log("FilePond:processfile");
248      if ($(this).find("input[type='hidden']").length > 0) {
249        let value = $(this).find("input[type='hidden']").val();
250        $(this).next(".d-none").val(value);
251        $(this).next(".d-none").parsley().reset();
252      }
253    });
254    $(selector).on("FilePond:updatefiles", function () {
255      //console.log("FilePond:updatefiles");
256      if ($(this).find("input[type='hidden']").length > 0) {
257        let value = $(this).find("input[type='hidden']").val();
258        $(this).next(".d-none").val(value);
259        $(this).next(".d-none").parsley().reset();
260      }
261    });
262  }
263
264  static setupFilesForTempWithCallbacks(
265    path,
266    selector = ".async-file",
267    callback = () => {},
268  ) {
269    $(selector).filepond({
270      allowMultiple: false,
271      allowImagePreview: false,
272      maxFileSize: "30MB",
273      credits: "",
274      maxFiles: 1,
275      allowDrop: false,
276      required: true,
277      labelIdle:
278        " <span class='filepond--label-action''> Browse your files</span>",
279      server: {
280        process: function (
281          fieldName,
282          file,
283          metadata,
284          load,
285          error,
286          progress,
287          abort,
288          transfer,
289          options,
290        ) {
291          const request = new XMLHttpRequest();
292          request.open("POST", path);
293          const formData = new FormData();
294
295          formData.append("files[]", file, file.name);
296
297          // Should call the progress method to update the progress to 100% before calling load
298          // Setting computable to false switches the loading indicator to infinite mode
299          request.upload.onprogress = (e) => {
300            progress(e.lengthComputable, e.loaded, e.total);
301            //console.log(e);
302          };
303
304          // Should call the load method when done and pass the returned server file id
305          // this server file id is then used later on when reverting or restoring a file
306          // so your server knows which file to return without exposing that info to the client
307          request.onload = function () {
308            if (request.status >= 200 && request.status < 300) {
309              const retData = JSON.parse(this.responseText);
310              console.log(retData);
311              load(retData.id);
312
313              if (typeof callback === "function") {
314                callback(retData.id);
315              }
316            } else {
317              // Can call the error method if something is wrong, should exit after
318              error("oh no");
319            }
320          };
321          request.send(formData);
322          return {
323            abort: function () {
324              request.abort();
325              abort();
326            },
327          };
328        },
329        remove: (source, load, error) => {
330          load();
331        },
332        revert: (uniqueFileId, load, error) => {
333          //console.log("revert", uniqueFileId);
334          //console.log(this);
335          load();
336        },
337      },
338    });
339    $(selector).on("FilePond:removefile", function () {
340      //console.log("FilePond:removefile");
341      if ($(this).next(".d-none").length > 0) {
342        $(this).next(".d-none").val("");
343      }
344    });
345    $(selector).on("FilePond:processfile", function () {
346      //console.log("FilePond:processfile");
347      if ($(this).find("input[type='hidden']").length > 0) {
348        let value = $(this).find("input[type='hidden']").val();
349        $(this).next(".d-none").val(value);
350        $(this).next(".d-none").parsley().reset();
351      }
352    });
353    $(selector).on("FilePond:updatefiles", function () {
354      //console.log("FilePond:updatefiles");
355      if ($(this).find("input[type='hidden']").length > 0) {
356        let value = $(this).find("input[type='hidden']").val();
357        $(this).next(".d-none").val(value);
358        $(this).next(".d-none").parsley().reset();
359      }
360    });
361  }
362
363  static resetFilesByClass(selector) {
364    $("." + selector).each(function () {
365      const pond = FilePond.find($(this)[0]);
366      pond.removeFiles();
367    });
368  }
369
370  static resetFileById(selector) {
371    const pond = FilePond.find($("#" + selector)[0]);
372    pond.removeFiles();
373  }
374
375  //this is the id of the FilePond element
376  static getFileInfoById(fileElementId) {
377    const selector = "#" + fileElementId;
378    const filePondFile = FilePond.find($(selector)[0]).getFile();
379    if (filePondFile) {
380      const fileInfo = {
381        fileExtension: filePondFile.fileExtension,
382        fileSize: filePondFile.fileSize,
383        fileType: filePondFile.fileType,
384        filename: filePondFile.filename,
385        filenameWithoutExtension: filePondFile.filenameWithoutExtension,
386        id: filePondFile.id,
387        origin: filePondFile.origin,
388        serverId: filePondFile.serverId,
389      };
390      return fileInfo;
391    }
392    return null;
393  }
394
395  //this is the id of the FilePond element
396  static initFileWithData(fileElementId, fileInfo) {
397    const selector = "#" + fileElementId;
398    if (fileInfo) {
399      FilePond.find($(selector)[0]).removeFile();
400      FilePond.find($(selector)[0])
401        .addFile(fileInfo.serverId, {
402          type: "local",
403          file: {
404            name: fileInfo.filename,
405            size: fileInfo.fileSize,
406            type: fileInfo.fileType,
407          },
408        })
409        .then(function (file) {
410          $(selector).next("input.d-none").val(fileInfo.serverId);
411        })
412        .catch(function (error) {});
413    }
414  }
415
416  static parseDataToCSV(dataArray, filename) {
417    const csv = Papa.unparse(dataArray);
418    const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
419    const link = document.createElement("a");
420    if (link.download !== undefined) {
421      // feature detection
422      const url = URL.createObjectURL(blob);
423      link.setAttribute("href", url);
424      link.setAttribute("download", filename);
425      link.style.visibility = "hidden";
426      document.body.appendChild(link);
427      link.click();
428      document.body.removeChild(link);
429    }
430  }
431
432  static escapeHtml(s) {
433    return String(s ?? "")
434      .replace(/&/g, "&amp;")
435      .replace(/</g, "&lt;")
436      .replace(/>/g, "&gt;")
437      .replace(/"/g, "&quot;")
438      .replace(/'/g, "&#039;");
439  }
440
441  static unEscapeHtml(s) {
442    return String(s ?? "")
443        .replace(/&nbsp;/g, ' ').replace(/\u00A0/g, ' ');
444  }
445
446
447  static initPopovers(root) {
448    // Dispose existing (prevents duplicates)
449    root.querySelectorAll('[data-bs-toggle="popover"]').forEach((el) => {
450      const inst = bootstrap.Popover.getInstance(el);
451      if (inst) inst.dispose();
452    });
453
454    // Create new
455    root.querySelectorAll('[data-bs-toggle="popover"]').forEach((el) => {
456      new bootstrap.Popover(el, {
457        container: "body",
458        trigger: "hover focus", // or "click"
459        html: false,
460      });
461    });
462  }
463}
464
465class Wizard {
466  constructor(rootId, linkClass, nextBtnId, prevBtnId) {
467    this.rootId = rootId;
468    this.linkClass = linkClass;
469    this.numTabs = $("#" + this.rootId).find(".nav-item").length;
470    this.nextBtnId = nextBtnId;
471    this.prevBtnId = prevBtnId;
472    //Make the first tab active
473    $("." + this.linkClass).each(function (index) {
474      if (index === 0) {
475        $(this).addClass("active");
476        $(this).removeClass("locked");
477      } else {
478        $(this).removeClass("active");
479        $(this).addClass("locked");
480      }
481    });
482
483    //Needed to prevent clicking on locked tabs
484    $("." + this.linkClass).on("show.bs.tab", function (event) {
485      if ($(this).hasClass("locked")) {
486        event.preventDefault();
487      }
488    });
489  }
490
491  getCurrentTabIndex() {
492    let currentTab = $("#" + this.rootId).find(".nav-link.active");
493    let currentIndex = currentTab.parent().index();
494    return currentIndex;
495  }
496
497  gotoNext() {
498    let currentTab = $("#" + this.rootId).find(".nav-link.active");
499    let nextParent = currentTab.parent().next();
500    if (nextParent.length > 0) {
501      let nextTab = nextParent.find(".nav-link").get(0);
502      $(nextTab).removeClass("locked"); // unlock next
503      $(currentTab).addClass("locked"); // lock current
504      new bootstrap.Tab(nextTab).show(); // move forward
505      if (this.prevBtnId) {
506        $("#" + this.prevBtnId).showBs();
507      }
508    }
509  }
510
511  gotoPrev() {
512    let currentTab = $("#" + this.rootId).find(".nav-link.active");
513    let prevParent = currentTab.parent().prev();
514    let currentIndex = currentTab.parent().index();
515    if (prevParent.length > 0) {
516      let prevTab = prevParent.find(".nav-link").get(0);
517      $(prevTab).removeClass("locked"); // unlock previous
518      $(currentTab).addClass("locked"); // lock current
519      new bootstrap.Tab(prevTab).show(); // move backward
520      if (currentIndex === 1) {
521        if (this.prevBtnId) {
522          $("#" + this.prevBtnId).hideBs();
523        }
524      }
525    }
526  }
527}

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.