PageSourceSearch

https://suporte.xpeducacao.com.br/hc/theming_assets/2023450/16394539029531/script.js?digest=52507859096603

js xpeducacao.com.br collected 2026-10-02 17:20:23 UTC 17,137 bytes, 545 lines download raw bytes

1(function () {
2  'use strict';
3
4  // Key map
5  const ENTER = 13;
6  const ESCAPE = 27;
7  const SPACE = 32;
8  const UP = 38;
9  const DOWN = 40;
10  const TAB = 9;
11
12  function toggleNavigation(toggle, menu) {
13    const isExpanded = menu.getAttribute("aria-expanded") === "true";
14    menu.setAttribute("aria-expanded", !isExpanded);
15    toggle.setAttribute("aria-expanded", !isExpanded);
16  }
17
18  function closeNavigation(toggle, menu) {
19    menu.setAttribute("aria-expanded", false);
20    toggle.setAttribute("aria-expanded", false);
21    toggle.focus();
22  }
23
24  // Navigation
25
26  window.addEventListener("DOMContentLoaded", () => {
27    const menuButton = document.querySelector(".header .menu-button-mobile");
28    const menuList = document.querySelector("#user-nav-mobile");
29
30    menuButton.addEventListener("click", (event) => {
31      event.stopPropagation();
32      toggleNavigation(menuButton, menuList);
33    });
34
35    menuList.addEventListener("keyup", (event) => {
36      if (event.keyCode === ESCAPE) {
37        event.stopPropagation();
38        closeNavigation(menuButton, menuList);
39      }
40    });
41
42    // Toggles expanded aria to collapsible elements
43    const collapsible = document.querySelectorAll(
44      ".collapsible-nav, .collapsible-sidebar"
45    );
46
47    collapsible.forEach((element) => {
48      const toggle = element.querySelector(
49        ".collapsible-nav-toggle, .collapsible-sidebar-toggle"
50      );
51
52      element.addEventListener("click", () => {
53        toggleNavigation(toggle, element);
54      });
55
56      element.addEventListener("keyup", (event) => {
57        console.log("escape");
58        if (event.keyCode === ESCAPE) {
59          closeNavigation(toggle, element);
60        }
61      });
62    });
63
64    // If multibrand search has more than 5 help centers or categories collapse the list
65    const multibrandFilterLists = document.querySelectorAll(
66      ".multibrand-filter-list"
67    );
68    multibrandFilterLists.forEach((filter) => {
69      if (filter.children.length > 6) {
70        // Display the show more button
71        const trigger = filter.querySelector(".see-all-filters");
72        trigger.setAttribute("aria-hidden", false);
73
74        // Add event handler for click
75        trigger.addEventListener("click", (event) => {
76          event.stopPropagation();
77          trigger.parentNode.removeChild(trigger);
78          filter.classList.remove("multibrand-filter-list--collapsed");
79        });
80      }
81    });
82  });
83
84  function Dropdown(toggle, menu) {
85    this.toggle = toggle;
86    this.menu = menu;
87
88    this.menuPlacement = {
89      top: menu.classList.contains("dropdown-menu-top"),
90      end: menu.classList.contains("dropdown-menu-end"),
91    };
92
93    this.toggle.addEventListener("click", this.clickHandler.bind(this));
94    this.toggle.addEventListener("keydown", this.toggleKeyHandler.bind(this));
95    this.menu.addEventListener("keydown", this.menuKeyHandler.bind(this));
96  }
97
98  Dropdown.prototype = {
99    get isExpanded() {
100      return this.menu.getAttribute("aria-expanded") === "true";
101    },
102
103    get menuItems() {
104      return Array.prototype.slice.call(
105        this.menu.querySelectorAll("[role='menuitem']")
106      );
107    },
108
109    dismiss: function () {
110      if (!this.isExpanded) return;
111
112      this.menu.setAttribute("aria-expanded", false);
113      this.menu.classList.remove("dropdown-menu-end", "dropdown-menu-top");
114    },
115
116    open: function () {
117      if (this.isExpanded) return;
118
119      this.menu.setAttribute("aria-expanded", true);
120      this.handleOverflow();
121    },
122
123    handleOverflow: function () {
124      var rect = this.menu.getBoundingClientRect();
125
126      var overflow = {
127        right: rect.left < 0 || rect.left + rect.width > window.innerWidth,
128        bottom: rect.top < 0 || rect.top + rect.height > window.innerHeight,
129      };
130
131      if (overflow.right || this.menuPlacement.end) {
132        this.menu.classList.add("dropdown-menu-end");
133      }
134
135      if (overflow.bottom || this.menuPlacement.top) {
136        this.menu.classList.add("dropdown-menu-top");
137      }
138
139      if (this.menu.getBoundingClientRect().top < 0) {
140        this.menu.classList.remove("dropdown-menu-top");
141      }
142    },
143
144    focusNextMenuItem: function (currentItem) {
145      if (!this.menuItems.length) return;
146
147      var currentIndex = this.menuItems.indexOf(currentItem);
148      var nextIndex =
149        currentIndex === this.menuItems.length - 1 || currentIndex < 0
150          ? 0
151          : currentIndex + 1;
152
153      this.menuItems[nextIndex].focus();
154    },
155
156    focusPreviousMenuItem: function (currentItem) {
157      if (!this.menuItems.length) return;
158
159      var currentIndex = this.menuItems.indexOf(currentItem);
160      var previousIndex =
161        currentIndex <= 0 ? this.menuItems.length - 1 : currentIndex - 1;
162
163      this.menuItems[previousIndex].focus();
164    },
165
166    clickHandler: function () {
167      if (this.isExpanded) {
168        this.dismiss();
169      } else {
170        this.open();
171      }
172    },
173
174    toggleKeyHandler: function (e) {
175      switch (e.keyCode) {
176        case ENTER:
177        case SPACE:
178        case DOWN:
179          e.preventDefault();
180          this.open();
181          this.focusNextMenuItem();
182          break;
183        case UP:
184          e.preventDefault();
185          this.open();
186          this.focusPreviousMenuItem();
187          break;
188        case ESCAPE:
189          this.dismiss();
190          this.toggle.focus();
191          break;
192      }
193    },
194
195    menuKeyHandler: function (e) {
196      var firstItem = this.menuItems[0];
197      var lastItem = this.menuItems[this.menuItems.length - 1];
198      var currentElement = e.target;
199
200      switch (e.keyCode) {
201        case ESCAPE:
202          this.dismiss();
203          this.toggle.focus();
204          break;
205        case DOWN:
206          e.preventDefault();
207          this.focusNextMenuItem(currentElement);
208          break;
209        case UP:
210          e.preventDefault();
211          this.focusPreviousMenuItem(currentElement);
212          break;
213        case TAB:
214          if (e.shiftKey) {
215            if (currentElement === firstItem) {
216              this.dismiss();
217            } else {
218              e.preventDefault();
219              this.focusPreviousMenuItem(currentElement);
220            }
221          } else if (currentElement === lastItem) {
222            this.dismiss();
223          } else {
224            e.preventDefault();
225            this.focusNextMenuItem(currentElement);
226          }
227          break;
228        case ENTER:
229        case SPACE:
230          e.preventDefault();
231          currentElement.click();
232          break;
233      }
234    },
235  };
236
237  // Drodowns
238
239  window.addEventListener("DOMContentLoaded", () => {
240    const dropdowns = [];
241    const dropdownToggles = document.querySelectorAll(".dropdown-toggle");
242
243    dropdownToggles.forEach((toggle) => {
244      const menu = toggle.nextElementSibling;
245      if (menu && menu.classList.contains("dropdown-menu")) {
246        dropdowns.push(new Dropdown(toggle, menu));
247      }
248    });
249
250    document.addEventListener("click", (event) => {
251      dropdowns.forEach((dropdown) => {
252        if (!dropdown.toggle.contains(event.target)) {
253          dropdown.dismiss();
254        }
255      });
256    });
257  });
258
259  // Share
260
261  window.addEventListener("DOMContentLoaded", () => {
262    const links = document.querySelectorAll(".share a");
263    links.forEach((anchor) => {
264      anchor.addEventListener("click", (event) => {
265        event.preventDefault();
266        window.open(anchor.href, "", "height = 500, width = 500");
267      });
268    });
269  });
270
271  // Vanilla JS debounce function, by Josh W. Comeau:
272  // https://www.joshwcomeau.com/snippets/javascript/debounce/
273  function debounce(callback, wait) {
274    let timeoutId = null;
275    return (...args) => {
276      window.clearTimeout(timeoutId);
277      timeoutId = window.setTimeout(() => {
278        callback.apply(null, args);
279      }, wait);
280    };
281  }
282
283  // Define variables for search field
284  let searchFormFilledClassName = "search-has-value";
285  let searchFormSelector = "form[role='search']";
286
287  // Clear the search input, and then return focus to it
288  function clearSearchInput(event) {
289    event.target
290      .closest(searchFormSelector)
291      .classList.remove(searchFormFilledClassName);
292
293    let input;
294    if (event.target.tagName === "INPUT") {
295      input = event.target;
296    } else if (event.target.tagName === "BUTTON") {
297      input = event.target.previousElementSibling;
298    } else {
299      input = event.target.closest("button").previousElementSibling;
300    }
301    input.value = "";
302    input.focus();
303  }
304
305  // Have the search input and clear button respond
306  // when someone presses the escape key, per:
307  // https://twitter.com/adambsilver/status/1152452833234554880
308  function clearSearchInputOnKeypress(event) {
309    const searchInputDeleteKeys = ["Delete", "Escape"];
310    if (searchInputDeleteKeys.includes(event.key)) {
311      clearSearchInput(event);
312    }
313  }
314
315  // Create an HTML button that all users -- especially keyboard users --
316  // can interact with, to clear the search input.
317  // To learn more about this, see:
318  // https://adrianroselli.com/2019/07/ignore-typesearch.html#Delete
319  // https://www.scottohara.me/blog/2022/02/19/custom-clear-buttons.html
320  function buildClearSearchButton(inputId) {
321    const button = document.createElement("button");
322    button.setAttribute("type", "button");
323    button.setAttribute("aria-controls", inputId);
324    button.classList.add("clear-button");
325    const buttonLabel = window.searchClearButtonLabelLocalized;
326    const icon = `<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' focusable='false' role='img' viewBox='0 0 12 12' aria-label='${buttonLabel}'><path stroke='currentColor' stroke-linecap='round' stroke-width='2' d='M3 9l6-6m0 6L3 3'/></svg>`;
327    button.innerHTML = icon;
328    button.addEventListener("click", clearSearchInput);
329    button.addEventListener("keyup", clearSearchInputOnKeypress);
330    return button;
331  }
332
333  // Append the clear button to the search form
334  function appendClearSearchButton(input, form) {
335    const searchClearButton = buildClearSearchButton(input.id);
336    form.append(searchClearButton);
337    if (input.value.length > 0) {
338      form.classList.add(searchFormFilledClassName);
339    }
340  }
341
342  // Add a class to the search form when the input has a value;
343  // Remove that class from the search form when the input doesn't have a value.
344  // Do this on a delay, rather than on every keystroke.
345  const toggleClearSearchButtonAvailability = debounce((event) => {
346    const form = event.target.closest(searchFormSelector);
347    form.classList.toggle(
348      searchFormFilledClassName,
349      event.target.value.length > 0
350    );
351  }, 200);
352
353  // Search
354
355  window.addEventListener("DOMContentLoaded", () => {
356    // Set up clear functionality for the search field
357    const searchForms = [...document.querySelectorAll(searchFormSelector)];
358    const searchInputs = searchForms.map((form) =>
359      form.querySelector("input[type='search']")
360    );
361    searchInputs.forEach((input) => {
362      appendClearSearchButton(input, input.closest(searchFormSelector));
363      input.addEventListener("keyup", clearSearchInputOnKeypress);
364      input.addEventListener("keyup", toggleClearSearchButtonAvailability);
365    });
366  });
367
368  const key = "returnFocusTo";
369
370  function saveFocus() {
371    const activeElementId = document.activeElement.getAttribute("id");
372    sessionStorage.setItem(key, "#" + activeElementId);
373  }
374
375  function returnFocus() {
376    const returnFocusTo = sessionStorage.getItem(key);
377    if (returnFocusTo) {
378      sessionStorage.removeItem("returnFocusTo");
379      const returnFocusToEl = document.querySelector(returnFocusTo);
380      returnFocusToEl && returnFocusToEl.focus && returnFocusToEl.focus();
381    }
382  }
383
384  // Forms
385
386  window.addEventListener("DOMContentLoaded", () => {
387    // In some cases we should preserve focus after page reload
388    returnFocus();
389
390    // show form controls when the textarea receives focus or back button is used and value exists
391    const commentContainerTextarea = document.querySelector(
392      ".comment-container textarea"
393    );
394    const commentContainerFormControls = document.querySelector(
395      ".comment-form-controls, .comment-ccs"
396    );
397
398    if (commentContainerTextarea) {
399      commentContainerTextarea.addEventListener(
400        "focus",
401        function focusCommentContainerTextarea() {
402          commentContainerFormControls.style.display = "block";
403          commentContainerTextarea.removeEventListener(
404            "focus",
405            focusCommentContainerTextarea
406          );
407        }
408      );
409
410      if (commentContainerTextarea.value !== "") {
411        commentContainerFormControls.style.display = "block";
412      }
413    }
414
415    // Expand Request comment form when Add to conversation is clicked
416    const showRequestCommentContainerTrigger = document.querySelector(
417      ".request-container .comment-container .comment-show-container"
418    );
419    const requestCommentFields = document.querySelectorAll(
420      ".request-container .comment-container .comment-fields"
421    );
422    const requestCommentSubmit = document.querySelector(
423      ".request-container .comment-container .request-submit-comment"
424    );
425
426    if (showRequestCommentContainerTrigger) {
427      showRequestCommentContainerTrigger.addEventListener("click", () => {
428        showRequestCommentContainerTrigger.style.display = "none";
429        Array.prototype.forEach.call(requestCommentFields, (element) => {
430          element.style.display = "block";
431        });
432        requestCommentSubmit.style.display = "inline-block";
433
434        if (commentContainerTextarea) {
435          commentContainerTextarea.focus();
436        }
437      });
438    }
439
440    // Mark as solved button
441    const requestMarkAsSolvedButton = document.querySelector(
442      ".request-container .mark-as-solved:not([data-disabled])"
443    );
444    const requestMarkAsSolvedCheckbox = document.querySelector(
445      ".request-container .comment-container input[type=checkbox]"
446    );
447    const requestCommentSubmitButton = document.querySelector(
448      ".request-container .comment-container input[type=submit]"
449    );
450
451    if (requestMarkAsSolvedButton) {
452      requestMarkAsSolvedButton.addEventListener("click", () => {
453        requestMarkAsSolvedCheckbox.setAttribute("checked", true);
454        requestCommentSubmitButton.disabled = true;
455        requestMarkAsSolvedButton.setAttribute("data-disabled", true);
456        requestMarkAsSolvedButton.form.submit();
457      });
458    }
459
460    // Change Mark as solved text according to whether comment is filled
461    const requestCommentTextarea = document.querySelector(
462      ".request-container .comment-container textarea"
463    );
464
465    const usesWysiwyg =
466      requestCommentTextarea &&
467      requestCommentTextarea.dataset.helper === "wysiwyg";
468
469    function isEmptyPlaintext(s) {
470      return s.trim() === "";
471    }
472
473    function isEmptyHtml(xml) {
474      const doc = new DOMParser().parseFromString(`<_>${xml}</_>`, "text/xml");
475      const img = doc.querySelector("img");
476      return img === null && isEmptyPlaintext(doc.children[0].textContent);
477    }
478
479    const isEmpty = usesWysiwyg ? isEmptyHtml : isEmptyPlaintext;
480
481    if (requestCommentTextarea) {
482      requestCommentTextarea.addEventListener("input", () => {
483        if (isEmpty(requestCommentTextarea.value)) {
484          if (requestMarkAsSolvedButton) {
485            requestMarkAsSolvedButton.innerText =
486              requestMarkAsSolvedButton.getAttribute("data-solve-translation");
487          }
488        } else {
489          if (requestMarkAsSolvedButton) {
490            requestMarkAsSolvedButton.innerText =
491              requestMarkAsSolvedButton.getAttribute(
492                "data-solve-and-submit-translation"
493              );
494          }
495        }
496      });
497    }
498
499    const selects = document.querySelectorAll(
500      "#request-status-select, #request-organization-select"
501    );
502
503    selects.forEach((element) => {
504      element.addEventListener("change", (event) => {
505        event.stopPropagation();
506        saveFocus();
507        element.form.submit();
508      });
509    });
510
511    // Submit requests filter form on search in the request list page
512    const quickSearch = document.querySelector("#quick-search");
513    if (quickSearch) {
514      quickSearch.addEventListener("keyup", (event) => {
515        if (event.keyCode === ENTER) {
516          event.stopPropagation();
517          saveFocus();
518          quickSearch.form.submit();
519        }
520      });
521    }
522
523    // Submit organization form in the request page
524    const requestOrganisationSelect = document.querySelector(
525      "#request-organization select"
526    );
527
528    if (requestOrganisationSelect) {
529      requestOrganisationSelect.addEventListener("change", () => {
530        requestOrganisationSelect.form.submit();
531      });
532    }
533
534    // If there are any error notifications below an input field, focus that field
535    const notificationElm = document.querySelector(".notification-error");
536    if (
537      notificationElm &&
538      notificationElm.previousElementSibling &&
539      typeof notificationElm.previousElementSibling.focus === "function"
540    ) {
541      notificationElm.previousElementSibling.focus();
542    }
543  });
544
545})();

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.