util.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. export function loadTemplate(element: ParentNode, id: string): DocumentFragment {
  2. let template = element.querySelector(`#${id}-template`);
  3. if (template !== null && template instanceof HTMLTemplateElement) {
  4. const fragment = document.importNode(template.content, true);
  5. fragment.querySelectorAll('template').forEach(t => {
  6. let parent = t.parentNode!;
  7. const templateName = t.getAttribute('name');
  8. if (templateName === null) {
  9. return;
  10. }
  11. let template = loadTemplate(fragment, templateName);
  12. let firstElement = template.querySelector('*');
  13. if (firstElement !== null) {
  14. for (let i = 0; i < t.classList.length; ++i) {
  15. firstElement.classList.add(t.classList[i]);
  16. }
  17. }
  18. parent.insertBefore(template, t);
  19. parent.removeChild(t);
  20. });
  21. return fragment;
  22. } else {
  23. throw new Error(`#${id}-template is not a template`);
  24. }
  25. }
  26. export function clearChildren(node: Node): void {
  27. while (node.lastChild !== null) {
  28. node.removeChild(node.lastChild);
  29. }
  30. }
  31. export function getElement<E extends HTMLElement>(element: ParentNode, selector: string): E {
  32. const e = element.querySelector(selector);
  33. if (e === null) {
  34. throw new Error(`Could not find required element ${selector}`);
  35. }
  36. return e as E;
  37. }
  38. export function loadBackground(url: string): Promise<void> {
  39. if (url.includes('.')) {
  40. return new Promise((resolve, reject) => {
  41. let image = new Image();
  42. image.onload = (event) => resolve();
  43. image.src = url;
  44. });
  45. } else {
  46. return Promise.resolve();
  47. }
  48. }
  49. class ListenerManager {
  50. constructor(
  51. private target: EventTarget,
  52. private event: string,
  53. private handler: EventListener
  54. ) {}
  55. attach(): void {
  56. this.target.addEventListener(this.event, this.handler);
  57. }
  58. detach(): void {
  59. this.target.removeEventListener(this.event, this.handler);
  60. }
  61. }
  62. export class ListenersManager {
  63. private listeners: ListenerManager[] = [];
  64. add(target: EventTarget, event: string, handler: EventListener, attach: boolean = true): void {
  65. let listener = new ListenerManager(target, event, handler);
  66. this.listeners.push(listener);
  67. if (attach) {
  68. listener.attach();
  69. }
  70. }
  71. attach(): void {
  72. this.listeners.forEach(l => l.attach());
  73. }
  74. detach(): void {
  75. this.listeners.forEach(l => l.detach());
  76. }
  77. }
  78. export class FnContext {
  79. current: Symbol = Symbol();
  80. invalidate() {
  81. this.current = Symbol();
  82. }
  83. wrap<T extends Function>(fn: T): T {
  84. const id = this.current;
  85. const wrappedFn = (...args: any[]) => {
  86. if (this.current === id) {
  87. return fn(...args);
  88. }
  89. };
  90. return wrappedFn as any as T;
  91. }
  92. }
  93. export interface Deferred {
  94. promise: Promise<void>;
  95. resolve: () => void;
  96. }
  97. export function makeDeferred(): Deferred {
  98. let resolve: undefined | (() => void);
  99. const promise = new Promise<void>((r) => {
  100. resolve = r;
  101. });
  102. return {
  103. promise,
  104. resolve: resolve!,
  105. }
  106. }