util.ts 3.2 KB

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