From dd1a5a49167463fff39b1f195e00c666689857ac Mon Sep 17 00:00:00 2001 From: freyavs Date: Sun, 30 Jul 2023 16:45:25 +0200 Subject: [PATCH 1/7] convert util.js to util.ts, remove jquery --- app/assets/javascripts/copy.ts | 7 +- app/assets/javascripts/util.ts | 270 +++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/util.ts diff --git a/app/assets/javascripts/copy.ts b/app/assets/javascripts/copy.ts index 9c4404639e..ced5023480 100644 --- a/app/assets/javascripts/copy.ts +++ b/app/assets/javascripts/copy.ts @@ -4,11 +4,10 @@ import { ready, tooltip } from "util.js"; export async function initClipboard(): Promise { await ready; const selector = ".btn"; - const delay = 1000; const clip = new ClipboardJS(selector); - const targetOf = (e): any => $($(e.trigger).data("clipboard-target")); - clip.on("success", e => tooltip(targetOf(e), I18n.t("js.copy-success"), delay)); - clip.on("error", e => tooltip(targetOf(e), I18n.t("js.copy-fail"), delay)); + const targetOf = (e): Element => document.querySelector(e.trigger.dataset["clipboard-target"]); + clip.on("success", e => tooltip(targetOf(e), I18n.t("js.copy-success"))); + clip.on("error", e => tooltip(targetOf(e), I18n.t("js.copy-fail"))); } /** diff --git a/app/assets/javascripts/util.ts b/app/assets/javascripts/util.ts new file mode 100644 index 0000000000..69a979b414 --- /dev/null +++ b/app/assets/javascripts/util.ts @@ -0,0 +1,270 @@ +import { isInIframe } from "iframe"; +import { Dutch } from "flatpickr/dist/l10n/nl"; +import { CustomLocale } from "flatpickr/dist/types/locale"; +import flatpickr from "flatpickr"; + +/** + * Create a function that will delay all subsequent calls on the same timer. + * You don't necessarily have to call the delayer with the same function. + * + * In the first example, the typical usage is illustrated. The second example + * illustrates what happens with multiple delayers, each with their own timer. + * + * There is also a pre-made delayer available with a global timer, see `delay`. + * @example + * const delay = createDelayer(); + * delay(() => console.log(1), 100); + * delay(() => console.log(2), 100); + * // prints 2, since the first invocation is cancelled + * + * @example + * const delay1 = createDelayer(); + * const delay2 = createDelayer(); + * delay1(() => console.log(1), 100); + * delay2(() => console.log(2), 100); + * // prints 1 and then 2, since both have their own timer. + * + * @return {function(TimerHandler, number): void} + */ +function createDelayer(): (callback: () => void, ms?: number) => void { + const timer = 0; + return (callback, ms) => { + clearTimeout(timer); + setTimeout(callback, ms); + }; +} + +/* + * Function to delay some other function until it isn't + * called for "ms" ms. This runs on a global timer, meaning + * the actual function doesn't matter. If you want a delay + * specifically for one function, you need to first create + * your own "delayer" with `createDelayer`. + */ +const delay = createDelayer(); + +function updateURLParameter(_url: string, param: string, paramVal: string): string { + const url = new URL(_url, window.location.origin); + if (paramVal) { + url.searchParams.set(param, paramVal); + } else { + url.searchParams.delete(param); + } + return url.toString(); +} + +function updateArrayURLParameter(_url: string, param: string, _paramVals: string[]): string { + const paramVals = new Set(_paramVals); // remove duplicate items + // convert "%5B%5D" back to "[]" + const url = new URL(_url.replace(/%5B%5D/g, "[]"), window.location.origin); + url.searchParams.delete(`${param}[]`); + paramVals.forEach(paramVal => { + url.searchParams.append(`${param}[]`, paramVal); + }); + return url.toString(); +} + +function getURLParameter(name: string, _url: string): string { + const url = new URL(_url ?? window.location.href, window.location.origin); + return url.searchParams.get(name); +} + +function getArrayURLParameter(name: string, _url: string): string[] { + const url = new URL(_url ?? window.location.href, window.location.origin); + return url.searchParams.getAll(`${name}[]`); +} + +function checkTimeZone(offset: number): void { + if (offset !== new Date().getTimezoneOffset()) { + document.querySelector("#time-zone-warning").classList.remove("hidden"); + } +} + +function checkIframe(): void { + if (isInIframe()) { + document.querySelector("#iframe-warning").classList.remove("hidden"); + } +} + +// TODO: remove? +// add CSRF token to each ajax-request +async function initCSRF(): Promise { + await ready; + $.ajaxSetup({ + "headers": { + "X-CSRF-Token": $("meta[name='csrf-token']").attr("content"), + }, + }); +} + +/** + * @param {Document | Element} root + */ +function initTooltips(root = document): void { + // First remove dead tooltips + const tooltips = root.querySelectorAll(".tooltip"); + for (const tooltip of tooltips) { + tooltip.remove(); + } + + // Then reinitialize tooltips + const elements = root.querySelectorAll("[data-bs-toggle=\"tooltip\"]") as NodeListOf; + for (const element of elements) { + const tooltip = window.bootstrap.Tooltip.getOrCreateInstance(element); + if (element.title) { + tooltip.setContent({ ".tooltip-inner": element.title }); + element.removeAttribute("title"); + } + } +} + +function tooltip(target: Element | null, message: string, disappearAfter = 3000): void { + if (target) { + const originalTitle = target.getAttribute("data-original-title"); + target.setAttribute("data-original-title", message); + target.setAttribute("title", message); + setTimeout(() => { + if (originalTitle) { + target.setAttribute("title", originalTitle); + target.setAttribute("data-original-title", originalTitle); + } else { + target.removeAttribute("title"); + target.removeAttribute("data-original-title"); + } + }, disappearAfter); + } +} + +function fetch(url: URL | RequestInfo, options: RequestInit = {}): Promise { + const headers = options.headers || {}; + headers["x-csrf-token"] = headers["x-csrf-token"] || (document.querySelector("meta[name=\"csrf-token\"]") as HTMLMetaElement).content; + headers["x-requested-with"] = headers["x-requested-with"] || "XMLHttpRequest"; + options["headers"] = headers; + return window.fetch(url, options); +} + +/** + * Make an element invisible by applying "visibility: hidden". + * + * @param {HTMLElement} element The element to hide. + */ +function makeInvisible(element: HTMLElement): void { + element.style.visibility = "hidden"; +} + +/** + * Make an element visible by applying "visibility: visible". + * + * @param {HTMLElement} element The element to show. + */ +function makeVisible(element: HTMLElement): void { + element.style.visibility = "visible"; +} + +/** + * Set the title of the webpage. + * + * @param {string} title The new title. + */ +function setDocumentTitle(title: string): void { + document.title = title; +} + +type DatePickerOptions = { + wrap?: boolean, + enableTime?: boolean, + dateFormat?: string, + altInput?: boolean, + altFormat?: string, + locale?: CustomLocale +}; + +/** + * Initiates a datepicker using flatpicker + * @param {string} selector - The selector of div containing the input field and buttons + * @param {object} options - optional, Options object as should be provided to the flatpicker creation method + * @return {flatpickr} the created flatpicker + */ +function initDatePicker(selector: string, options: DatePickerOptions = {}): object { + function init(): object { + if (I18n.locale === "nl") { + options.locale = Dutch; + } + return flatpickr(selector, options); + } + + return init(); +} + +/** + * This promise will resolve when the dom content is fully loaded + * This could mean immediately if the dom is already loaded + */ +const ready = new Promise(resolve => { + if (document.readyState !== "loading") { + resolve(); + } else { + document.addEventListener("DOMContentLoaded", () => resolve()); + } +}); + +// source https://github.com/janl/mustache.js/blob/master/mustache.js#L73 +const entityMap = { + "&": "&", + "<": "<", + ">": ">", + "\"": """, + "'": "'", + "/": "/", + "`": "`", + "=": "=" +}; + +function htmlEncode(str: string): string { + return String(str).replace(/[&<>"'`=/]/g, function (s) { + return entityMap[s]; + }); +} + +/** + * Returns the first parent of an element that has at least all of the given classes. + * Returns null if no such parent exists. + * @param {Element} element - Iterate over the parents of this element + * @param {string} classNames - The class names to search for, separated by white space + * @return {?Element} The parent containing the classes + */ +function getParentByClassName(element: Element, classNames: string): Element { + let parent = element.parentElement; + while (parent) { + if (classNames.split(/\s+/).every(className => parent.classList.contains(className))) { + return parent; + } + parent = parent.parentElement; + } + return null; +} + +// insert `cached` function here after move to typescript +// the function is currently in `app/assets/javascripts/mark.ts` + +export { + createDelayer, + delay, + fetch, + updateURLParameter, + updateArrayURLParameter, + getURLParameter, + getArrayURLParameter, + checkTimeZone, + checkIframe, + initCSRF, + tooltip, + initTooltips, + makeInvisible, + makeVisible, + setDocumentTitle, + initDatePicker, + ready, + htmlEncode, + getParentByClassName, +}; From 84f89cc54822bc8fe5a6e07304cec5241fd120a8 Mon Sep 17 00:00:00 2001 From: freyavs Date: Tue, 1 Aug 2023 22:09:57 +0200 Subject: [PATCH 2/7] remove util.js, change to utilities.ts, refactor imports --- .../annotations/annotation_marker.ts | 2 +- .../annotations/annotations_toggles.ts | 2 +- .../annotations/code_listing_row.ts | 2 +- .../annotations/create_annotation_button.ts | 2 +- .../annotations/hidden_annotations_dot.ts | 2 +- .../components/annotations/user_annotation.ts | 2 +- .../javascripts/components/copy_button.ts | 2 +- .../components/course_labels_search_bar.ts | 2 +- .../javascripts/components/datalist_input.ts | 2 +- .../javascripts/components/meta/i18n_mixin.ts | 2 +- .../javascripts/components/progress_bar.ts | 2 +- .../components/search/filter_button.ts | 2 +- .../components/search/search_actions.ts | 2 +- .../components/search/search_field.ts | 2 +- .../components/sign_in_search_bar.ts | 2 +- app/assets/javascripts/copy.ts | 2 +- app/assets/javascripts/course.ts | 2 +- app/assets/javascripts/drag_and_drop.ts | 2 +- app/assets/javascripts/evaluation.ts | 2 +- app/assets/javascripts/exercise.ts | 2 +- .../javascripts/favorite_course_buttons.ts | 2 +- app/assets/javascripts/feedback/actions.ts | 2 +- app/assets/javascripts/feedback/score.ts | 2 +- app/assets/javascripts/institution.ts | 2 +- app/assets/javascripts/lti.ts | 2 +- app/assets/javascripts/notification.ts | 2 +- app/assets/javascripts/pythia_submission.ts | 2 +- app/assets/javascripts/question_table.ts | 2 +- app/assets/javascripts/repository.ts | 2 +- app/assets/javascripts/score_item.ts | 2 +- app/assets/javascripts/search.ts | 2 +- app/assets/javascripts/series.ts | 2 +- .../javascripts/state/SavedAnnotations.ts | 2 +- app/assets/javascripts/state/SearchQuery.ts | 2 +- .../javascripts/state/UserAnnotations.ts | 2 +- app/assets/javascripts/submission.ts | 2 +- app/assets/javascripts/util.js | 252 ------------------ .../javascripts/{util.ts => utilities.ts} | 6 +- .../visualisations/series_graph.ts | 2 +- app/javascript/packs/announcement.js | 2 +- app/javascript/packs/application_pack.js | 2 +- app/javascript/packs/course.js | 2 +- app/javascript/packs/evaluation.js | 2 +- app/javascript/packs/frame.js | 2 +- app/javascript/packs/series.js | 2 +- .../components/search/search_actions.test.ts | 2 +- .../{util.test.js => utilities.test.js} | 2 +- 47 files changed, 48 insertions(+), 300 deletions(-) delete mode 100644 app/assets/javascripts/util.js rename app/assets/javascripts/{util.ts => utilities.ts} (98%) rename test/javascript/{util.test.js => utilities.test.js} (98%) diff --git a/app/assets/javascripts/components/annotations/annotation_marker.ts b/app/assets/javascripts/components/annotations/annotation_marker.ts index 293f5445c1..f1913eda9c 100644 --- a/app/assets/javascripts/components/annotations/annotation_marker.ts +++ b/app/assets/javascripts/components/annotations/annotation_marker.ts @@ -3,7 +3,7 @@ import { render, html, LitElement, TemplateResult } from "lit"; import tippy, { Instance as Tippy, createSingleton } from "tippy.js"; import { AnnotationData, annotationState, compareAnnotationOrders, isUserAnnotation } from "state/Annotations"; import { StateController } from "state/state_system/StateController"; -import { createDelayer } from "util.js"; +import { createDelayer } from "utilities"; const setInstancesDelayer = createDelayer(); /** diff --git a/app/assets/javascripts/components/annotations/annotations_toggles.ts b/app/assets/javascripts/components/annotations/annotations_toggles.ts index 6e3bdb14e2..670ca8fd41 100644 --- a/app/assets/javascripts/components/annotations/annotations_toggles.ts +++ b/app/assets/javascripts/components/annotations/annotations_toggles.ts @@ -3,7 +3,7 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { html, TemplateResult, PropertyValues } from "lit"; import { annotationState } from "state/Annotations"; import { i18nMixin } from "components/meta/i18n_mixin"; -import { initTooltips } from "util.js"; +import { initTooltips } from "utilities"; /** * This component represents the toggles to show/hide annotations. diff --git a/app/assets/javascripts/components/annotations/code_listing_row.ts b/app/assets/javascripts/components/annotations/code_listing_row.ts index 622f0150d2..32f66aebb9 100644 --- a/app/assets/javascripts/components/annotations/code_listing_row.ts +++ b/app/assets/javascripts/components/annotations/code_listing_row.ts @@ -6,7 +6,7 @@ import "components/annotations/annotations_cell"; import "components/annotations/annotation_marker"; import "components/annotations/hidden_annotations_dot"; import { i18nMixin } from "components/meta/i18n_mixin"; -import { initTooltips } from "util.js"; +import { initTooltips } from "utilities"; import { PropertyValues } from "@lit/reactive-element"; import { userState } from "state/Users"; import { AnnotationData, annotationState, compareAnnotationOrders } from "state/Annotations"; diff --git a/app/assets/javascripts/components/annotations/create_annotation_button.ts b/app/assets/javascripts/components/annotations/create_annotation_button.ts index 22d99b8bf8..ac1cbed99a 100644 --- a/app/assets/javascripts/components/annotations/create_annotation_button.ts +++ b/app/assets/javascripts/components/annotations/create_annotation_button.ts @@ -2,7 +2,7 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { customElement, property } from "lit/decorators.js"; import { html, PropertyValues, TemplateResult } from "lit"; import { userAnnotationState } from "state/UserAnnotations"; -import { initTooltips } from "util.js"; +import { initTooltips } from "utilities"; // The image has to be created before the event is fired // otherwise safari will not create the drag image diff --git a/app/assets/javascripts/components/annotations/hidden_annotations_dot.ts b/app/assets/javascripts/components/annotations/hidden_annotations_dot.ts index ff656cb30c..40fea7ef6e 100644 --- a/app/assets/javascripts/components/annotations/hidden_annotations_dot.ts +++ b/app/assets/javascripts/components/annotations/hidden_annotations_dot.ts @@ -5,7 +5,7 @@ import { MachineAnnotationData, machineAnnotationState } from "state/MachineAnno import { UserAnnotationData, userAnnotationState } from "state/UserAnnotations"; import { i18nMixin } from "components/meta/i18n_mixin"; import { PropertyValues } from "@lit/reactive-element/development/reactive-element"; -import { initTooltips } from "util.js"; +import { initTooltips } from "utilities"; import { annotationState, compareAnnotationOrders } from "state/Annotations"; /** diff --git a/app/assets/javascripts/components/annotations/user_annotation.ts b/app/assets/javascripts/components/annotations/user_annotation.ts index 5f61cc10e4..7e4ab6c393 100644 --- a/app/assets/javascripts/components/annotations/user_annotation.ts +++ b/app/assets/javascripts/components/annotations/user_annotation.ts @@ -7,7 +7,7 @@ import { i18nMixin } from "components/meta/i18n_mixin"; import { AnnotationForm } from "components/annotations/annotation_form"; import { createRef, Ref, ref } from "lit/directives/ref.js"; import "components/saved_annotations/new_saved_annotation"; -import { initTooltips } from "util.js"; +import { initTooltips } from "utilities"; import "components/saved_annotations/saved_annotation_icon"; import { annotationState } from "state/Annotations"; diff --git a/app/assets/javascripts/components/copy_button.ts b/app/assets/javascripts/components/copy_button.ts index 55d06f87f7..27981aea12 100644 --- a/app/assets/javascripts/components/copy_button.ts +++ b/app/assets/javascripts/components/copy_button.ts @@ -1,7 +1,7 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { html, PropertyValues, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { initTooltips, ready } from "util.js"; +import { initTooltips, ready } from "utilities"; /** * A button that copies the text content of a given element to the clipboard. diff --git a/app/assets/javascripts/components/course_labels_search_bar.ts b/app/assets/javascripts/components/course_labels_search_bar.ts index 610a119513..cf49724d62 100644 --- a/app/assets/javascripts/components/course_labels_search_bar.ts +++ b/app/assets/javascripts/components/course_labels_search_bar.ts @@ -2,7 +2,7 @@ import { customElement, property } from "lit/decorators.js"; import { html, TemplateResult } from "lit"; import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { Option } from "components/datalist_input"; -import { ready } from "util.js"; +import { ready } from "utilities"; import "components/datalist_input"; /** * This component represents a list of the selected course labels diff --git a/app/assets/javascripts/components/datalist_input.ts b/app/assets/javascripts/components/datalist_input.ts index 3419037e33..9e42d540ff 100644 --- a/app/assets/javascripts/components/datalist_input.ts +++ b/app/assets/javascripts/components/datalist_input.ts @@ -4,7 +4,7 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { ref, Ref, createRef } from "lit/directives/ref.js"; import { watchMixin } from "components/meta/watch_mixin"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; -import { htmlEncode } from "util.js"; +import { htmlEncode } from "utilities"; export type Option = {label: string, value: string, extra?: string}; diff --git a/app/assets/javascripts/components/meta/i18n_mixin.ts b/app/assets/javascripts/components/meta/i18n_mixin.ts index 19d4342349..c33dc58759 100644 --- a/app/assets/javascripts/components/meta/i18n_mixin.ts +++ b/app/assets/javascripts/components/meta/i18n_mixin.ts @@ -1,5 +1,5 @@ import { LitElement } from "lit"; -import { ready } from "util.js"; +import { ready } from "utilities"; type Constructor = new (...args: any[]) => LitElement; diff --git a/app/assets/javascripts/components/progress_bar.ts b/app/assets/javascripts/components/progress_bar.ts index 65e821f64e..377bb953f6 100644 --- a/app/assets/javascripts/components/progress_bar.ts +++ b/app/assets/javascripts/components/progress_bar.ts @@ -1,7 +1,7 @@ import { html, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; -import { initTooltips, ready } from "../util"; +import { initTooltips, ready } from "../utilities"; /** * This component displays a progress bar consisting of consecutive divs diff --git a/app/assets/javascripts/components/search/filter_button.ts b/app/assets/javascripts/components/search/filter_button.ts index d1043c698a..d9eae32c78 100644 --- a/app/assets/javascripts/components/search/filter_button.ts +++ b/app/assets/javascripts/components/search/filter_button.ts @@ -1,7 +1,7 @@ import { customElement, property } from "lit/decorators.js"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; -import { initTooltips } from "util.js"; +import { initTooltips } from "utilities"; import { searchQueryState } from "state/SearchQuery"; import { StateController } from "state/state_system/StateController"; diff --git a/app/assets/javascripts/components/search/search_actions.ts b/app/assets/javascripts/components/search/search_actions.ts index edb2baaee7..a843e116bb 100644 --- a/app/assets/javascripts/components/search/search_actions.ts +++ b/app/assets/javascripts/components/search/search_actions.ts @@ -1,7 +1,7 @@ import { html, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; import { Toast } from "toast"; -import { fetch, ready } from "util.js"; +import { fetch, ready } from "utilities"; import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { searchQueryState } from "state/SearchQuery"; diff --git a/app/assets/javascripts/components/search/search_field.ts b/app/assets/javascripts/components/search/search_field.ts index 758f6a7358..0cbd501cce 100644 --- a/app/assets/javascripts/components/search/search_field.ts +++ b/app/assets/javascripts/components/search/search_field.ts @@ -1,6 +1,6 @@ import { customElement, property } from "lit/decorators.js"; import { html, TemplateResult } from "lit"; -import { createDelayer } from "util.js"; +import { createDelayer } from "utilities"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { ref } from "lit/directives/ref.js"; import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; diff --git a/app/assets/javascripts/components/sign_in_search_bar.ts b/app/assets/javascripts/components/sign_in_search_bar.ts index f6cba67d64..f87501af8d 100644 --- a/app/assets/javascripts/components/sign_in_search_bar.ts +++ b/app/assets/javascripts/components/sign_in_search_bar.ts @@ -2,7 +2,7 @@ import { customElement, property } from "lit/decorators.js"; import { html, TemplateResult } from "lit"; import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { Option } from "components/datalist_input"; -import { ready } from "util.js"; +import { ready } from "utilities"; import "components/datalist_input"; /** diff --git a/app/assets/javascripts/copy.ts b/app/assets/javascripts/copy.ts index ced5023480..fb7ca317ad 100644 --- a/app/assets/javascripts/copy.ts +++ b/app/assets/javascripts/copy.ts @@ -1,5 +1,5 @@ import ClipboardJS from "clipboard"; -import { ready, tooltip } from "util.js"; +import { ready, tooltip } from "utilities"; export async function initClipboard(): Promise { await ready; diff --git a/app/assets/javascripts/course.ts b/app/assets/javascripts/course.ts index 5d7e92ec26..bf3c057449 100644 --- a/app/assets/javascripts/course.ts +++ b/app/assets/javascripts/course.ts @@ -1,5 +1,5 @@ import { initDragAndDrop } from "./drag_and_drop"; -import { fetch, getURLParameter } from "./util.js"; +import { fetch, getURLParameter } from "utilities"; import { ScrollSpy } from "./scrollspy"; import { html, render } from "lit"; import { Modal } from "bootstrap"; diff --git a/app/assets/javascripts/drag_and_drop.ts b/app/assets/javascripts/drag_and_drop.ts index a2a9c5f46b..4e69f6f897 100644 --- a/app/assets/javascripts/drag_and_drop.ts +++ b/app/assets/javascripts/drag_and_drop.ts @@ -1,5 +1,5 @@ import dragula from "dragula"; -import { fetch, getParentByClassName } from "util.js"; +import { fetch, getParentByClassName } from "utilities"; /** * Custom type for arguments of the drag and drop initialization diff --git a/app/assets/javascripts/evaluation.ts b/app/assets/javascripts/evaluation.ts index 28505fd3d2..0955aa1076 100644 --- a/app/assets/javascripts/evaluation.ts +++ b/app/assets/javascripts/evaluation.ts @@ -1,4 +1,4 @@ -import { fetch } from "util.js"; +import { fetch } from "utilities"; export function initCheckboxes(): void { document.querySelectorAll(".evaluation-users-table .user-row") diff --git a/app/assets/javascripts/exercise.ts b/app/assets/javascripts/exercise.ts index 97b5bb18f6..b6c193dafe 100644 --- a/app/assets/javascripts/exercise.ts +++ b/app/assets/javascripts/exercise.ts @@ -1,5 +1,5 @@ /* globals ace */ -import { initTooltips, updateURLParameter, fetch } from "util.js"; +import { initTooltips, updateURLParameter, fetch } from "utilities"; import { Toast } from "./toast"; import GLightbox from "glightbox"; import { IFrameMessageData } from "iframe-resizer"; diff --git a/app/assets/javascripts/favorite_course_buttons.ts b/app/assets/javascripts/favorite_course_buttons.ts index fb37258f59..002b289f09 100644 --- a/app/assets/javascripts/favorite_course_buttons.ts +++ b/app/assets/javascripts/favorite_course_buttons.ts @@ -1,5 +1,5 @@ import { Toast } from "./toast"; -import { fetch, getParentByClassName } from "util.js"; +import { fetch, getParentByClassName } from "utilities"; function initFavoriteButtons(doc: Document | HTMLElement = document): void { function init(): void { diff --git a/app/assets/javascripts/feedback/actions.ts b/app/assets/javascripts/feedback/actions.ts index 5842ffa969..a194b778b5 100644 --- a/app/assets/javascripts/feedback/actions.ts +++ b/app/assets/javascripts/feedback/actions.ts @@ -1,4 +1,4 @@ -import { fetch, updateURLParameter } from "util.js"; +import { fetch, updateURLParameter } from "utilities"; import ScoreForm from "feedback/score"; interface ActionOptions { diff --git a/app/assets/javascripts/feedback/score.ts b/app/assets/javascripts/feedback/score.ts index 5b52e6c0ba..86bda2ac4c 100644 --- a/app/assets/javascripts/feedback/score.ts +++ b/app/assets/javascripts/feedback/score.ts @@ -1,4 +1,4 @@ -import { fetch, createDelayer } from "util.js"; +import { fetch, createDelayer } from "utilities"; import FeedbackActions from "feedback/actions"; /** diff --git a/app/assets/javascripts/institution.ts b/app/assets/javascripts/institution.ts index 4cd16ba44e..a941159e62 100644 --- a/app/assets/javascripts/institution.ts +++ b/app/assets/javascripts/institution.ts @@ -1,4 +1,4 @@ -import { fetch } from "./util.js"; +import { fetch } from "utilities"; function initInstitutionSelectionTable(institutionId: number): void { const institutionPanel = document.querySelector("#institution-panel"); diff --git a/app/assets/javascripts/lti.ts b/app/assets/javascripts/lti.ts index 3e94f93931..29e4655bcb 100644 --- a/app/assets/javascripts/lti.ts +++ b/app/assets/javascripts/lti.ts @@ -1,5 +1,5 @@ // Identifiers. -import { fetch, makeInvisible, makeVisible } from "util.js"; +import { fetch, makeInvisible, makeVisible } from "utilities"; import { isInIframe } from "iframe"; const redirectButtonId = "lti_redirect_button"; diff --git a/app/assets/javascripts/notification.ts b/app/assets/javascripts/notification.ts index 86483bcc0d..7866e111d1 100644 --- a/app/assets/javascripts/notification.ts +++ b/app/assets/javascripts/notification.ts @@ -1,5 +1,5 @@ import { FaviconManager } from "favicon"; -import { fetch } from "util.js"; +import { fetch } from "utilities"; import { InactiveTimeout } from "auto_reload"; /** diff --git a/app/assets/javascripts/pythia_submission.ts b/app/assets/javascripts/pythia_submission.ts index 10d4368c46..db79c3bbee 100644 --- a/app/assets/javascripts/pythia_submission.ts +++ b/app/assets/javascripts/pythia_submission.ts @@ -1,6 +1,6 @@ import fscreen from "fscreen"; import { showInfoModal } from "./modal.js"; -import { fetch } from "./util.js"; +import { fetch } from "utilities"; function initPythiaSubmissionShow(submissionCode: string, activityPath: string): void { function init(): void { diff --git a/app/assets/javascripts/question_table.ts b/app/assets/javascripts/question_table.ts index cc018208b8..7100cba026 100644 --- a/app/assets/javascripts/question_table.ts +++ b/app/assets/javascripts/question_table.ts @@ -1,4 +1,4 @@ -import { fetch } from "util.js"; +import { fetch } from "utilities"; import { InactiveTimeout } from "auto_reload"; const questionContainerId = "question-container"; diff --git a/app/assets/javascripts/repository.ts b/app/assets/javascripts/repository.ts index 64cd7758c8..bb7611fc49 100644 --- a/app/assets/javascripts/repository.ts +++ b/app/assets/javascripts/repository.ts @@ -1,4 +1,4 @@ -import { fetch } from "./util.js"; +import { fetch } from "utilities"; import { Toast } from "./toast"; function initAdminsEdit(): void { diff --git a/app/assets/javascripts/score_item.ts b/app/assets/javascripts/score_item.ts index ab5aaa6458..7839fcc039 100644 --- a/app/assets/javascripts/score_item.ts +++ b/app/assets/javascripts/score_item.ts @@ -1,4 +1,4 @@ -import { fetch } from "util.js"; +import { fetch } from "utilities"; export function initInlineEditButton(tableElement: HTMLElement): void { tableElement.querySelectorAll(".edit-button").forEach(item => { diff --git a/app/assets/javascripts/search.ts b/app/assets/javascripts/search.ts index 28e4090d91..20869e73c3 100644 --- a/app/assets/javascripts/search.ts +++ b/app/assets/javascripts/search.ts @@ -1,4 +1,4 @@ -import { createDelayer, fetch, getURLParameter, updateArrayURLParameter, updateURLParameter } from "util.js"; +import { createDelayer, fetch, getURLParameter, updateArrayURLParameter, updateURLParameter } from "utilities"; import { InactiveTimeout } from "auto_reload"; import { LoadingBar } from "components/search/loading_bar"; import { searchQueryState } from "state/SearchQuery"; diff --git a/app/assets/javascripts/series.ts b/app/assets/javascripts/series.ts index 2462017275..134373684b 100644 --- a/app/assets/javascripts/series.ts +++ b/app/assets/javascripts/series.ts @@ -1,6 +1,6 @@ import { Toast } from "./toast"; import { initDragAndDrop } from "./drag_and_drop"; -import { fetch } from "./util.js"; +import { fetch } from "utilities"; import { ViolinGraph } from "visualisations/violin"; import { StackedStatusGraph } from "visualisations/stacked_status"; diff --git a/app/assets/javascripts/state/SavedAnnotations.ts b/app/assets/javascripts/state/SavedAnnotations.ts index 8e7575a48f..9870c2d405 100644 --- a/app/assets/javascripts/state/SavedAnnotations.ts +++ b/app/assets/javascripts/state/SavedAnnotations.ts @@ -1,4 +1,4 @@ -import { updateArrayURLParameter, updateURLParameter, fetch, createDelayer } from "util.js"; +import { updateArrayURLParameter, updateURLParameter, fetch, createDelayer } from "utilities"; import { MapWithDefault } from "map_with_default"; import { userAnnotationState } from "state/UserAnnotations"; import { State } from "state/state_system/State"; diff --git a/app/assets/javascripts/state/SearchQuery.ts b/app/assets/javascripts/state/SearchQuery.ts index f997b3775e..1fe27bc8fb 100644 --- a/app/assets/javascripts/state/SearchQuery.ts +++ b/app/assets/javascripts/state/SearchQuery.ts @@ -1,6 +1,6 @@ import { State } from "state/state_system/State"; import { StateMap } from "state/state_system/StateMap"; -import { updateArrayURLParameter, updateURLParameter } from "util.js"; +import { updateArrayURLParameter, updateURLParameter } from "utilities"; class SearchQueryState extends State { readonly arrayQueryParams: StateMap = new StateMap(); diff --git a/app/assets/javascripts/state/UserAnnotations.ts b/app/assets/javascripts/state/UserAnnotations.ts index c34e4a3328..aa0d3af25d 100644 --- a/app/assets/javascripts/state/UserAnnotations.ts +++ b/app/assets/javascripts/state/UserAnnotations.ts @@ -1,4 +1,4 @@ -import { createDelayer, fetch } from "util.js"; +import { createDelayer, fetch } from "utilities"; import { Notification } from "notification"; import { savedAnnotationState } from "state/SavedAnnotations"; import { State } from "state/state_system/State"; diff --git a/app/assets/javascripts/submission.ts b/app/assets/javascripts/submission.ts index 904b3c3a3b..dd1d94575e 100644 --- a/app/assets/javascripts/submission.ts +++ b/app/assets/javascripts/submission.ts @@ -1,4 +1,4 @@ -import { getParentByClassName } from "util.js"; +import { getParentByClassName } from "utilities"; function initSubmissionShow(parentClass: string, mediaPath: string, token: string): void { function init(): void { diff --git a/app/assets/javascripts/util.js b/app/assets/javascripts/util.js deleted file mode 100644 index 18a5769b5b..0000000000 --- a/app/assets/javascripts/util.js +++ /dev/null @@ -1,252 +0,0 @@ -import { isInIframe } from "iframe"; -import { Dutch } from "flatpickr/dist/l10n/nl"; -import flatpickr from "flatpickr"; - -/** - * Create a function that will delay all subsequent calls on the same timer. - * You don't necessarily have to call the delayer with the same function. - * - * In the first example, the typical usage is illustrated. The second example - * illustrates what happens with multiple delayers, each with their own timer. - * - * There is also a pre-made delayer available with a global timer, see `delay`. - * @example - * const delay = createDelayer(); - * delay(() => console.log(1), 100); - * delay(() => console.log(2), 100); - * // prints 2, since the first invocation is cancelled - * - * @example - * const delay1 = createDelayer(); - * const delay2 = createDelayer(); - * delay1(() => console.log(1), 100); - * delay2(() => console.log(2), 100); - * // prints 1 and then 2, since both have their own timer. - * - * @return {function(TimerHandler, number): void} - */ -function createDelayer() { - let timer = 0; - return function (callback, ms) { - clearTimeout(timer); - timer = setTimeout(callback, ms); - }; -} - -/* - * Function to delay some other function until it isn't - * called for "ms" ms. This runs on a global timer, meaning - * the actual function doesn't matter. If you want a delay - * specifically for one function, you need to first create - * your own "delayer" with `createDelayer`. - */ -const delay = createDelayer(); - -function updateURLParameter(_url, param, paramVal) { - const url = new URL(_url, window.location.origin); - if (paramVal) { - url.searchParams.set(param, paramVal); - } else { - url.searchParams.delete(param); - } - return url.toString(); -} - -function updateArrayURLParameter(_url, param, _paramVals) { - const paramVals = new Set(_paramVals); // remove duplicate items - // convert "%5B%5D" back to "[]" - const url = new URL(_url.replace(/%5B%5D/g, "[]"), window.location.origin); - url.searchParams.delete(`${param}[]`); - paramVals.forEach(paramVal => { - url.searchParams.append(`${param}[]`, paramVal); - }); - return url.toString(); -} - -function getURLParameter(name, _url) { - const url = new URL(_url ?? window.location.href, window.location.origin); - return url.searchParams.get(name); -} - -function getArrayURLParameter(name, _url) { - const url = new URL(_url ?? window.location.href, window.location.origin); - return url.searchParams.getAll(`${name}[]`); -} - -function checkTimeZone(offset) { - if (offset !== new Date().getTimezoneOffset()) { - $("#time-zone-warning").removeClass("hidden"); - } -} - -function checkIframe() { - if (isInIframe()) { - $("#iframe-warning").removeClass("hidden"); - } -} - -// add CSRF token to each ajax-request -async function initCSRF() { - await ready; - $.ajaxSetup({ - "headers": { - "X-CSRF-Token": $("meta[name='csrf-token']").attr("content"), - }, - }); -} - -/** - * @param {Document | Element} root - */ -function initTooltips(root = document) { - // First remove dead tooltips - const tooltips = root.querySelectorAll(".tooltip"); - for (const tooltip of tooltips) { - tooltip.remove(); - } - - // Then reinitialize tooltips - const elements = root.querySelectorAll("[data-bs-toggle=\"tooltip\"]"); - for (const element of elements) { - const tooltip = window.bootstrap.Tooltip.getOrCreateInstance(element); - if (element.title) { - tooltip.setContent({ ".tooltip-inner": element.title }); - element.removeAttribute("title"); - } - } -} - -function tooltip(target, message, disappearAfter=1000) { - const $target = $(target); - const originalTitle = $target.attr("data-original-title"); - $target.attr("data-original-title", message).tooltip("show"); - $target.attr("title", message).tooltip("show"); - setTimeout(() => { - $target.attr("title", originalTitle).attr("data-original-title", originalTitle).tooltip(); - }, disappearAfter); -} - -function fetch(url, options = {}) { - const headers = options.headers || {}; - headers["x-csrf-token"] = headers["x-csrf-token"] || document.querySelector("meta[name=\"csrf-token\"]").content; - headers["x-requested-with"] = headers["x-requested-with"] || "XMLHttpRequest"; - options["headers"] = headers; - return window.fetch(url, options); -} - -/** - * Make an element invisible by applying "visibility: hidden". - * - * @param {HTMLElement} element The element to hide. - */ -function makeInvisible(element) { - element.style.visibility = "hidden"; -} - -/** - * Make an element visible by applying "visibility: visible". - * - * @param {HTMLElement} element The element to show. - */ -function makeVisible(element) { - element.style.visibility = "visible"; -} - -/** - * Set the title of the webpage. - * - * @param {string} title The new title. - */ -function setDocumentTitle(title) { - document.title = title; -} - -/** - * Initiates a datepicker using flatpicker - * @param {string} selector - The selector of div containing the input field and buttons - * @param {object} options - optional, Options object as should be provided to the flatpicker creation method - * @return {flatpickr} the created flatpicker - */ -function initDatePicker(selector, options = {}) { - function init() { - if (I18n.locale === "nl") { - options.locale = Dutch; - } - return flatpickr(selector, options); - } - - return init(); -} - -/** - * This promise will resolve when the dom content is fully loaded - * This could mean immediately if the dom is already loaded - */ -const ready = new Promise(resolve => { - if (document.readyState !== "loading") { - resolve(); - } else { - document.addEventListener("DOMContentLoaded", () => resolve()); - } -}); - -// source https://github.com/janl/mustache.js/blob/master/mustache.js#L73 -const entityMap = { - "&": "&", - "<": "<", - ">": ">", - "\"": """, - "'": "'", - "/": "/", - "`": "`", - "=": "=" -}; - -function htmlEncode(str) { - return String(str).replace(/[&<>"'`=/]/g, function (s) { - return entityMap[s]; - }); -} - -/** - * Returns the first parent of an element that has at least all of the given classes. - * Returns null if no such parent exists. - * @param {Element} element - Iterate over the parents of this element - * @param {string} classNames - The class names to search for, separated by white space - * @return {?Element} The parent containing the classes - */ -function getParentByClassName(element, classNames) { - let parent = element.parentElement; - while (parent) { - if (classNames.split(/\s+/).every(className => parent.classList.contains(className))) { - return parent; - } - parent = parent.parentElement; - } - return null; -} - -// insert `cached` function here after move to typescript -// the function is currently in `app/assets/javascripts/mark.ts` - -export { - createDelayer, - delay, - fetch, - updateURLParameter, - updateArrayURLParameter, - getURLParameter, - getArrayURLParameter, - checkTimeZone, - checkIframe, - initCSRF, - tooltip, - initTooltips, - makeInvisible, - makeVisible, - setDocumentTitle, - initDatePicker, - ready, - htmlEncode, - getParentByClassName, -}; diff --git a/app/assets/javascripts/util.ts b/app/assets/javascripts/utilities.ts similarity index 98% rename from app/assets/javascripts/util.ts rename to app/assets/javascripts/utilities.ts index 69a979b414..b8d300f953 100644 --- a/app/assets/javascripts/util.ts +++ b/app/assets/javascripts/utilities.ts @@ -27,10 +27,10 @@ import flatpickr from "flatpickr"; * @return {function(TimerHandler, number): void} */ function createDelayer(): (callback: () => void, ms?: number) => void { - const timer = 0; + let timer = 0; return (callback, ms) => { clearTimeout(timer); - setTimeout(callback, ms); + timer = window.setTimeout(callback, ms); }; } @@ -100,7 +100,7 @@ async function initCSRF(): Promise { /** * @param {Document | Element} root */ -function initTooltips(root = document): void { +function initTooltips(root: Document | Element = document): void { // First remove dead tooltips const tooltips = root.querySelectorAll(".tooltip"); for (const tooltip of tooltips) { diff --git a/app/assets/javascripts/visualisations/series_graph.ts b/app/assets/javascripts/visualisations/series_graph.ts index 0fc9df1f88..a1d3aea360 100644 --- a/app/assets/javascripts/visualisations/series_graph.ts +++ b/app/assets/javascripts/visualisations/series_graph.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line // @ts-nocheck import * as d3 from "d3"; -import { initDatePicker } from "util.js"; +import { initDatePicker } from "utilities"; export type RawData = { // eslint-disable-next-line camelcase diff --git a/app/javascript/packs/announcement.js b/app/javascript/packs/announcement.js index a927a1dcd3..3aa75bd503 100644 --- a/app/javascript/packs/announcement.js +++ b/app/javascript/packs/announcement.js @@ -1,4 +1,4 @@ -import { initDatePicker } from "util.js"; +import { initDatePicker } from "utilities.ts"; import "components/datalist_input"; dodona.initDatePicker = initDatePicker; diff --git a/app/javascript/packs/application_pack.js b/app/javascript/packs/application_pack.js index 22780d90b7..41f4c335dd 100644 --- a/app/javascript/packs/application_pack.js +++ b/app/javascript/packs/application_pack.js @@ -32,7 +32,7 @@ window.bootstrap = bootstrap; import { Drawer } from "drawer"; import { Toast } from "toast"; import { Notification } from "notification"; -import { checkTimeZone, checkIframe, initCSRF, initTooltips, ready } from "util.js"; +import { checkTimeZone, checkIframe, initCSRF, initTooltips, ready } from "utilities.ts"; import { initClipboard } from "copy"; import { FaviconManager } from "favicon"; import "components/saved_annotations/saved_annotation_list"; diff --git a/app/javascript/packs/course.js b/app/javascript/packs/course.js index b35d0c7c82..b13a1432b3 100644 --- a/app/javascript/packs/course.js +++ b/app/javascript/packs/course.js @@ -11,7 +11,7 @@ import { RefreshingQuestionTable, toggleQuestionNavDot } from "question_table.ts"; -import { setDocumentTitle } from "util.js"; +import { setDocumentTitle } from "utilities.ts"; window.dodona.initSeriesReorder = initSeriesReorder; window.dodona.initCourseForm = initCourseForm; diff --git a/app/javascript/packs/evaluation.js b/app/javascript/packs/evaluation.js index 226029cdd8..d14b4a2e38 100644 --- a/app/javascript/packs/evaluation.js +++ b/app/javascript/packs/evaluation.js @@ -1,6 +1,6 @@ import { initCheckboxes, initCheckbox, initEvaluationStepper } from "evaluation.ts"; import FeedbackActions from "feedback/actions"; -import { initDatePicker } from "util.js"; +import { initDatePicker } from "utilities.ts"; window.dodona.initDeadlinePicker = initDatePicker; window.dodona.initCheckbox = initCheckbox; diff --git a/app/javascript/packs/frame.js b/app/javascript/packs/frame.js index e68b94869d..4e4b00aad6 100644 --- a/app/javascript/packs/frame.js +++ b/app/javascript/packs/frame.js @@ -13,7 +13,7 @@ import { Alert, Button, Collapse, Dropdown, Modal, Popover, Tab, Tooltip } from const bootstrap = { Alert, Button, Collapse, Dropdown, Modal, Popover, Tab, Tooltip }; window.bootstrap = bootstrap; -import { initTooltips, ready } from "util.js"; +import { initTooltips, ready } from "utilities.ts"; import { initClipboard } from "copy"; // Use a global dodona object to prevent polluting the global na diff --git a/app/javascript/packs/series.js b/app/javascript/packs/series.js index 24e4456805..76b75efa9d 100644 --- a/app/javascript/packs/series.js +++ b/app/javascript/packs/series.js @@ -1,5 +1,5 @@ import { initSeriesEdit, initSeriesShow } from "series.ts"; -import { initDatePicker } from "util.js"; +import { initDatePicker } from "utilities.ts"; window.dodona.initDeadlinePicker = initDatePicker; window.dodona.initSeriesEdit = initSeriesEdit; diff --git a/test/javascript/components/search/search_actions.test.ts b/test/javascript/components/search/search_actions.test.ts index 137fe5951c..a357cde1a2 100644 --- a/test/javascript/components/search/search_actions.test.ts +++ b/test/javascript/components/search/search_actions.test.ts @@ -4,7 +4,7 @@ import { fixture, nextFrame } from "@open-wc/testing-helpers"; import userEvent from "@testing-library/user-event"; import { screen } from "@testing-library/dom"; import { html } from "lit"; -import * as util from "util.js"; +import * as util from "utilities"; import { searchQueryState } from "state/SearchQuery"; describe("SearchActions", () => { diff --git a/test/javascript/util.test.js b/test/javascript/utilities.test.js similarity index 98% rename from test/javascript/util.test.js rename to test/javascript/utilities.test.js index 3ed6c30eb5..5f73cbae4a 100644 --- a/test/javascript/util.test.js +++ b/test/javascript/utilities.test.js @@ -1,7 +1,7 @@ import { updateArrayURLParameter, updateURLParameter, getURLParameter, getArrayURLParameter, delay, createDelayer -} from "../../app/assets/javascripts/util"; +} from "utilities.ts"; jest.useFakeTimers(); From b966affa48c659adfee8f037345029a73303dc78 Mon Sep 17 00:00:00 2001 From: freyavs Date: Tue, 1 Aug 2023 22:18:54 +0200 Subject: [PATCH 3/7] fix optional parameter --- app/assets/javascripts/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/utilities.ts b/app/assets/javascripts/utilities.ts index b8d300f953..538937f5fe 100644 --- a/app/assets/javascripts/utilities.ts +++ b/app/assets/javascripts/utilities.ts @@ -64,7 +64,7 @@ function updateArrayURLParameter(_url: string, param: string, _paramVals: string return url.toString(); } -function getURLParameter(name: string, _url: string): string { +function getURLParameter(name: string, _url?: string): string { const url = new URL(_url ?? window.location.href, window.location.origin); return url.searchParams.get(name); } From 19ea86cc261fc9dc9bf2bd2eb7a4226952c3a0da Mon Sep 17 00:00:00 2001 From: freyavs Date: Tue, 1 Aug 2023 22:51:55 +0200 Subject: [PATCH 4/7] fix missed imports --- .../javascripts/components/annotations/annotation_tooltip.ts | 2 +- app/assets/javascripts/state/Theme.ts | 2 +- app/assets/javascripts/state/Users.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/components/annotations/annotation_tooltip.ts b/app/assets/javascripts/components/annotations/annotation_tooltip.ts index e97d3f0a5c..ec9023ec88 100644 --- a/app/assets/javascripts/components/annotations/annotation_tooltip.ts +++ b/app/assets/javascripts/components/annotations/annotation_tooltip.ts @@ -3,7 +3,7 @@ import { render, html, LitElement, TemplateResult, css } from "lit"; import tippy, { Instance as Tippy, createSingleton } from "tippy.js"; import { AnnotationData, annotationState, compareAnnotationOrders, isUserAnnotation } from "state/Annotations"; import { StateController } from "state/state_system/StateController"; -import { createDelayer } from "util.js"; +import { createDelayer } from "utilities"; const setInstancesDelayer = createDelayer(); /** diff --git a/app/assets/javascripts/state/Theme.ts b/app/assets/javascripts/state/Theme.ts index 8db605a1ce..fe32cda1c1 100644 --- a/app/assets/javascripts/state/Theme.ts +++ b/app/assets/javascripts/state/Theme.ts @@ -1,6 +1,6 @@ import { stateProperty } from "state/state_system/StateProperty"; import { State } from "state/state_system/State"; -import { getURLParameter, updateURLParameter } from "util.js"; +import { getURLParameter, updateURLParameter } from "utilities"; // The actual theme applied to the page export type Theme = "light" | "dark"; diff --git a/app/assets/javascripts/state/Users.ts b/app/assets/javascripts/state/Users.ts index 25d0a75bb5..a0341fb691 100644 --- a/app/assets/javascripts/state/Users.ts +++ b/app/assets/javascripts/state/Users.ts @@ -1,7 +1,7 @@ import { State } from "state/state_system/State"; import { stateProperty } from "state/state_system/StateProperty"; import { ThemeOption } from "state/Theme"; -import { fetch } from "util.js"; +import { fetch } from "utilities"; export type Permission = "annotation.create" From f0daa9cb1d6fe01b39951163577919c103820e5e Mon Sep 17 00:00:00 2001 From: freyavs Date: Wed, 2 Aug 2023 20:15:33 +0200 Subject: [PATCH 5/7] apply comments --- app/assets/javascripts/utilities.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/utilities.ts b/app/assets/javascripts/utilities.ts index 19c4234e36..4e5928d33f 100644 --- a/app/assets/javascripts/utilities.ts +++ b/app/assets/javascripts/utilities.ts @@ -26,7 +26,7 @@ import flatpickr from "flatpickr"; * * @return {function(TimerHandler, number): void} */ -function createDelayer(): (callback: () => void, ms?: number) => void { +function createDelayer(): (callback: () => void, ms: number) => void { let timer = 0; return (callback, ms) => { clearTimeout(timer); @@ -69,7 +69,7 @@ function getURLParameter(name: string, _url?: string): string { return url.searchParams.get(name); } -function getArrayURLParameter(name: string, _url: string): string[] { +function getArrayURLParameter(name: string, _url?: string): string[] { const url = new URL(_url ?? window.location.href, window.location.origin); return url.searchParams.getAll(`${name}[]`); } @@ -86,17 +86,6 @@ function checkIframe(): void { } } -// TODO: remove? -// add CSRF token to each ajax-request -async function initCSRF(): Promise { - await ready; - $.ajaxSetup({ - "headers": { - "X-CSRF-Token": $("meta[name='csrf-token']").attr("content"), - }, - }); -} - /** * @param {Document | Element} root */ From 7cf7c8259b7c296d0978b0b1fdb6ef4d78ea28c3 Mon Sep 17 00:00:00 2001 From: freyavs Date: Wed, 2 Aug 2023 20:18:55 +0200 Subject: [PATCH 6/7] remove unused export --- app/assets/javascripts/utilities.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/utilities.ts b/app/assets/javascripts/utilities.ts index 4e5928d33f..bf31a4dc02 100644 --- a/app/assets/javascripts/utilities.ts +++ b/app/assets/javascripts/utilities.ts @@ -225,7 +225,6 @@ export { getArrayURLParameter, checkTimeZone, checkIframe, - initCSRF, tooltip, initTooltips, makeInvisible, From 9b489748123c7de35c0b3280e3c73b4a03985229 Mon Sep 17 00:00:00 2001 From: freyavs Date: Thu, 3 Aug 2023 20:43:06 +0200 Subject: [PATCH 7/7] fix last delayer comments --- app/assets/javascripts/utilities.ts | 4 ++-- app/javascript/packs/application_pack.js | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/utilities.ts b/app/assets/javascripts/utilities.ts index bf31a4dc02..f3939e4adb 100644 --- a/app/assets/javascripts/utilities.ts +++ b/app/assets/javascripts/utilities.ts @@ -27,10 +27,10 @@ import flatpickr from "flatpickr"; * @return {function(TimerHandler, number): void} */ function createDelayer(): (callback: () => void, ms: number) => void { - let timer = 0; + let timer; return (callback, ms) => { clearTimeout(timer); - timer = window.setTimeout(callback, ms); + timer = setTimeout(callback, ms); }; } diff --git a/app/javascript/packs/application_pack.js b/app/javascript/packs/application_pack.js index 75498920ce..15863f8150 100644 --- a/app/javascript/packs/application_pack.js +++ b/app/javascript/packs/application_pack.js @@ -32,7 +32,7 @@ window.bootstrap = bootstrap; import { Drawer } from "drawer"; import { Toast } from "toast"; import { Notification } from "notification"; -import { checkTimeZone, checkIframe, initCSRF, initTooltips, ready } from "utilities.ts"; +import { checkTimeZone, checkIframe, initTooltips, ready } from "utilities.ts"; import { initClipboard } from "copy"; import { FaviconManager } from "favicon"; import { themeState } from "state/Theme"; @@ -50,10 +50,6 @@ if (!window.dodona.hideDrawer) { ready.then(() => new Drawer()); } - -// Adds the CSRF token to each ajax request -initCSRF(); - ready.then(initTooltips); // Use a global dodona object to prevent polluting the global na