Licitator 1.0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

8711 lines
349 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /**
  2. * @license
  3. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  4. * This code may only be used under the BSD style license found at
  5. * http://polymer.github.io/LICENSE.txt
  6. * The complete set of authors may be found at
  7. * http://polymer.github.io/AUTHORS.txt
  8. * The complete set of contributors may be found at
  9. * http://polymer.github.io/CONTRIBUTORS.txt
  10. * Code distributed by Google as part of the polymer project is also
  11. * subject to an additional IP rights grant found at
  12. * http://polymer.github.io/PATENTS.txt
  13. */
  14. /**
  15. * Brands a function as a directive factory function so that lit-html will call
  16. * the function during template rendering, rather than passing as a value.
  17. *
  18. * A _directive_ is a function that takes a Part as an argument. It has the
  19. * signature: `(part: Part) => void`.
  20. *
  21. * A directive _factory_ is a function that takes arguments for data and
  22. * configuration and returns a directive. Users of directive usually refer to
  23. * the directive factory as the directive. For example, "The repeat directive".
  24. *
  25. * Usually a template author will invoke a directive factory in their template
  26. * with relevant arguments, which will then return a directive function.
  27. *
  28. * Here's an example of using the `repeat()` directive factory that takes an
  29. * array and a function to render an item:
  30. *
  31. * ```js
  32. * html`<ul><${repeat(items, (item) => html`<li>${item}</li>`)}</ul>`
  33. * ```
  34. *
  35. * When `repeat` is invoked, it returns a directive function that closes over
  36. * `items` and the template function. When the outer template is rendered, the
  37. * return directive function is called with the Part for the expression.
  38. * `repeat` then performs it's custom logic to render multiple items.
  39. *
  40. * @param f The directive factory function. Must be a function that returns a
  41. * function of the signature `(part: Part) => void`. The returned function will
  42. * be called with the part object.
  43. *
  44. * @example
  45. *
  46. * import {directive, html} from 'lit-html';
  47. *
  48. * const immutable = directive((v) => (part) => {
  49. * if (part.value !== v) {
  50. * part.setValue(v)
  51. * }
  52. * });
  53. */
  54. const directive = (f) => ((...args) => {
  55. const d = f(...args);
  56. // tslint:disable-next-line:no-any
  57. d.isDirective = true;
  58. return d;
  59. });
  60. class Directive {
  61. constructor() {
  62. this.isDirective = true;
  63. this.isClass = true;
  64. }
  65. body(_part) {
  66. // body of the directive
  67. }
  68. }
  69. const isDirective = (o) => {
  70. return o !== undefined && o !== null &&
  71. // tslint:disable-next-line:no-any
  72. typeof o.isDirective === 'boolean';
  73. };
  74. /**
  75. * @license
  76. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  77. * This code may only be used under the BSD style license found at
  78. * http://polymer.github.io/LICENSE.txt
  79. * The complete set of authors may be found at
  80. * http://polymer.github.io/AUTHORS.txt
  81. * The complete set of contributors may be found at
  82. * http://polymer.github.io/CONTRIBUTORS.txt
  83. * Code distributed by Google as part of the polymer project is also
  84. * subject to an additional IP rights grant found at
  85. * http://polymer.github.io/PATENTS.txt
  86. */
  87. /**
  88. * True if the custom elements polyfill is in use.
  89. */
  90. const isCEPolyfill = typeof window !== 'undefined' ?
  91. window.customElements != null &&
  92. window.customElements
  93. .polyfillWrapFlushCallback !== undefined :
  94. false;
  95. /**
  96. * Reparents nodes, starting from `start` (inclusive) to `end` (exclusive),
  97. * into another container (could be the same container), before `before`. If
  98. * `before` is null, it appends the nodes to the container.
  99. */
  100. const reparentNodes = (container, start, end = null, before = null) => {
  101. while (start !== end) {
  102. const n = start.nextSibling;
  103. container.insertBefore(start, before);
  104. start = n;
  105. }
  106. };
  107. /**
  108. * Removes nodes, starting from `start` (inclusive) to `end` (exclusive), from
  109. * `container`.
  110. */
  111. const removeNodes = (container, start, end = null) => {
  112. while (start !== end) {
  113. const n = start.nextSibling;
  114. container.removeChild(start);
  115. start = n;
  116. }
  117. };
  118. /**
  119. * @license
  120. * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
  121. * This code may only be used under the BSD style license found at
  122. * http://polymer.github.io/LICENSE.txt
  123. * The complete set of authors may be found at
  124. * http://polymer.github.io/AUTHORS.txt
  125. * The complete set of contributors may be found at
  126. * http://polymer.github.io/CONTRIBUTORS.txt
  127. * Code distributed by Google as part of the polymer project is also
  128. * subject to an additional IP rights grant found at
  129. * http://polymer.github.io/PATENTS.txt
  130. */
  131. /**
  132. * A sentinel value that signals that a value was handled by a directive and
  133. * should not be written to the DOM.
  134. */
  135. const noChange = {};
  136. /**
  137. * A sentinel value that signals a NodePart to fully clear its content.
  138. */
  139. const nothing = {};
  140. /**
  141. * @license
  142. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  143. * This code may only be used under the BSD style license found at
  144. * http://polymer.github.io/LICENSE.txt
  145. * The complete set of authors may be found at
  146. * http://polymer.github.io/AUTHORS.txt
  147. * The complete set of contributors may be found at
  148. * http://polymer.github.io/CONTRIBUTORS.txt
  149. * Code distributed by Google as part of the polymer project is also
  150. * subject to an additional IP rights grant found at
  151. * http://polymer.github.io/PATENTS.txt
  152. */
  153. /**
  154. * An expression marker with embedded unique key to avoid collision with
  155. * possible text in templates.
  156. */
  157. const marker = `{{lit-${String(Math.random()).slice(2)}}}`;
  158. /**
  159. * An expression marker used text-positions, multi-binding attributes, and
  160. * attributes with markup-like text values.
  161. */
  162. const nodeMarker = `<!--${marker}-->`;
  163. const markerRegex = new RegExp(`${marker}|${nodeMarker}`);
  164. /**
  165. * Suffix appended to all bound attribute names.
  166. */
  167. const boundAttributeSuffix = '$lit$';
  168. /**
  169. * An updatable Template that tracks the location of dynamic parts.
  170. */
  171. class Template {
  172. constructor(result, element) {
  173. this.parts = [];
  174. this.element = element;
  175. const nodesToRemove = [];
  176. const stack = [];
  177. // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null
  178. const walker = document.createTreeWalker(element.content, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false);
  179. // Keeps track of the last index associated with a part. We try to delete
  180. // unnecessary nodes, but we never want to associate two different parts
  181. // to the same index. They must have a constant node between.
  182. let lastPartIndex = 0;
  183. let index = -1;
  184. let partIndex = 0;
  185. const { strings, values: { length } } = result;
  186. while (partIndex < length) {
  187. const node = walker.nextNode();
  188. if (node === null) {
  189. // We've exhausted the content inside a nested template element.
  190. // Because we still have parts (the outer for-loop), we know:
  191. // - There is a template in the stack
  192. // - The walker will find a nextNode outside the template
  193. walker.currentNode = stack.pop();
  194. continue;
  195. }
  196. index++;
  197. if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {
  198. if (node.hasAttributes()) {
  199. const attributes = node.attributes;
  200. const { length } = attributes;
  201. // Per
  202. // https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap,
  203. // attributes are not guaranteed to be returned in document order.
  204. // In particular, Edge/IE can return them out of order, so we cannot
  205. // assume a correspondence between part index and attribute index.
  206. let count = 0;
  207. for (let i = 0; i < length; i++) {
  208. if (endsWith(attributes[i].name, boundAttributeSuffix)) {
  209. count++;
  210. }
  211. }
  212. while (count-- > 0) {
  213. // Get the template literal section leading up to the first
  214. // expression in this attribute
  215. const stringForPart = strings[partIndex];
  216. // Find the attribute name
  217. const name = lastAttributeNameRegex.exec(stringForPart)[2];
  218. // Find the corresponding attribute
  219. // All bound attributes have had a suffix added in
  220. // TemplateResult#getHTML to opt out of special attribute
  221. // handling. To look up the attribute value we also need to add
  222. // the suffix.
  223. const attributeLookupName = name.toLowerCase() + boundAttributeSuffix;
  224. const attributeValue = node.getAttribute(attributeLookupName);
  225. node.removeAttribute(attributeLookupName);
  226. const statics = attributeValue.split(markerRegex);
  227. this.parts.push({
  228. type: 'attribute',
  229. index,
  230. name,
  231. strings: statics,
  232. sanitizer: undefined
  233. });
  234. partIndex += statics.length - 1;
  235. }
  236. }
  237. if (node.tagName === 'TEMPLATE') {
  238. stack.push(node);
  239. walker.currentNode = node.content;
  240. }
  241. }
  242. else if (node.nodeType === 3 /* Node.TEXT_NODE */) {
  243. const data = node.data;
  244. if (data.indexOf(marker) >= 0) {
  245. const parent = node.parentNode;
  246. const strings = data.split(markerRegex);
  247. const lastIndex = strings.length - 1;
  248. // Generate a new text node for each literal section
  249. // These nodes are also used as the markers for node parts
  250. for (let i = 0; i < lastIndex; i++) {
  251. let insert;
  252. let s = strings[i];
  253. if (s === '') {
  254. insert = createMarker();
  255. }
  256. else {
  257. const match = lastAttributeNameRegex.exec(s);
  258. if (match !== null && endsWith(match[2], boundAttributeSuffix)) {
  259. s = s.slice(0, match.index) + match[1] +
  260. match[2].slice(0, -boundAttributeSuffix.length) + match[3];
  261. }
  262. insert = document.createTextNode(s);
  263. }
  264. parent.insertBefore(insert, node);
  265. this.parts.push({ type: 'node', index: ++index });
  266. }
  267. // If there's no text, we must insert a comment to mark our place.
  268. // Else, we can trust it will stick around after cloning.
  269. if (strings[lastIndex] === '') {
  270. parent.insertBefore(createMarker(), node);
  271. nodesToRemove.push(node);
  272. }
  273. else {
  274. node.data = strings[lastIndex];
  275. }
  276. // We have a part for each match found
  277. partIndex += lastIndex;
  278. }
  279. }
  280. else if (node.nodeType === 8 /* Node.COMMENT_NODE */) {
  281. if (node.data === marker) {
  282. const parent = node.parentNode;
  283. // Add a new marker node to be the startNode of the Part if any of
  284. // the following are true:
  285. // * We don't have a previousSibling
  286. // * The previousSibling is already the start of a previous part
  287. if (node.previousSibling === null || index === lastPartIndex) {
  288. index++;
  289. parent.insertBefore(createMarker(), node);
  290. }
  291. lastPartIndex = index;
  292. this.parts.push({ type: 'node', index });
  293. // If we don't have a nextSibling, keep this node so we have an end.
  294. // Else, we can remove it to save future costs.
  295. if (node.nextSibling === null) {
  296. node.data = '';
  297. }
  298. else {
  299. nodesToRemove.push(node);
  300. index--;
  301. }
  302. partIndex++;
  303. }
  304. else {
  305. let i = -1;
  306. while ((i = node.data.indexOf(marker, i + 1)) !== -1) {
  307. // Comment node has a binding marker inside, make an inactive part
  308. // The binding won't work, but subsequent bindings will
  309. // TODO (justinfagnani): consider whether it's even worth it to
  310. // make bindings in comments work
  311. this.parts.push({ type: 'node', index: -1 });
  312. partIndex++;
  313. }
  314. }
  315. }
  316. }
  317. // Remove text binding nodes after the walk to not disturb the TreeWalker
  318. for (const n of nodesToRemove) {
  319. n.parentNode.removeChild(n);
  320. }
  321. }
  322. }
  323. const endsWith = (str, suffix) => {
  324. const index = str.length - suffix.length;
  325. return index >= 0 && str.slice(index) === suffix;
  326. };
  327. const isTemplatePartActive = (part) => part.index !== -1;
  328. /**
  329. * Used to clone existing node instead of each time creating new one which is
  330. * slower
  331. */
  332. const markerNode = document.createComment('');
  333. // Allows `document.createComment('')` to be renamed for a
  334. // small manual size-savings.
  335. const createMarker = () => markerNode.cloneNode();
  336. /**
  337. * This regex extracts the attribute name preceding an attribute-position
  338. * expression. It does this by matching the syntax allowed for attributes
  339. * against the string literal directly preceding the expression, assuming that
  340. * the expression is in an attribute-value position.
  341. *
  342. * See attributes in the HTML spec:
  343. * https://www.w3.org/TR/html5/syntax.html#elements-attributes
  344. *
  345. * " \x09\x0a\x0c\x0d" are HTML space characters:
  346. * https://www.w3.org/TR/html5/infrastructure.html#space-characters
  347. *
  348. * "\0-\x1F\x7F-\x9F" are Unicode control characters, which includes every
  349. * space character except " ".
  350. *
  351. * So an attribute is:
  352. * * The name: any character except a control character, space character, ('),
  353. * ("), ">", "=", or "/"
  354. * * Followed by zero or more space characters
  355. * * Followed by "="
  356. * * Followed by zero or more space characters
  357. * * Followed by:
  358. * * Any character except space, ('), ("), "<", ">", "=", (`), or
  359. * * (") then any non-("), or
  360. * * (') then any non-(')
  361. */
  362. const lastAttributeNameRegex =
  363. // eslint-disable-next-line no-control-regex
  364. /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
  365. /**
  366. * @license
  367. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  368. * This code may only be used under the BSD style license found at
  369. * http://polymer.github.io/LICENSE.txt
  370. * The complete set of authors may be found at
  371. * http://polymer.github.io/AUTHORS.txt
  372. * The complete set of contributors may be found at
  373. * http://polymer.github.io/CONTRIBUTORS.txt
  374. * Code distributed by Google as part of the polymer project is also
  375. * subject to an additional IP rights grant found at
  376. * http://polymer.github.io/PATENTS.txt
  377. */
  378. /**
  379. * An instance of a `Template` that can be attached to the DOM and updated
  380. * with new values.
  381. */
  382. class TemplateInstance {
  383. constructor(template, processor, options) {
  384. this.__parts = [];
  385. this.template = template;
  386. this.processor = processor;
  387. this.options = options;
  388. }
  389. update(values) {
  390. let i = 0;
  391. for (const part of this.__parts) {
  392. if (part !== undefined) {
  393. part.setValue(values[i]);
  394. }
  395. i++;
  396. }
  397. for (const part of this.__parts) {
  398. if (part !== undefined) {
  399. part.commit();
  400. }
  401. }
  402. }
  403. _clone() {
  404. // There are a number of steps in the lifecycle of a template instance's
  405. // DOM fragment:
  406. // 1. Clone - create the instance fragment
  407. // 2. Adopt - adopt into the main document
  408. // 3. Process - find part markers and create parts
  409. // 4. Upgrade - upgrade custom elements
  410. // 5. Update - set node, attribute, property, etc., values
  411. // 6. Connect - connect to the document. Optional and outside of this
  412. // method.
  413. //
  414. // We have a few constraints on the ordering of these steps:
  415. // * We need to upgrade before updating, so that property values will pass
  416. // through any property setters.
  417. // * We would like to process before upgrading so that we're sure that the
  418. // cloned fragment is inert and not disturbed by self-modifying DOM.
  419. // * We want custom elements to upgrade even in disconnected fragments.
  420. //
  421. // Given these constraints, with full custom elements support we would
  422. // prefer the order: Clone, Process, Adopt, Upgrade, Update, Connect
  423. //
  424. // But Safari does not implement CustomElementRegistry#upgrade, so we
  425. // can not implement that order and still have upgrade-before-update and
  426. // upgrade disconnected fragments. So we instead sacrifice the
  427. // process-before-upgrade constraint, since in Custom Elements v1 elements
  428. // must not modify their light DOM in the constructor. We still have issues
  429. // when co-existing with CEv0 elements like Polymer 1, and with polyfills
  430. // that don't strictly adhere to the no-modification rule because shadow
  431. // DOM, which may be created in the constructor, is emulated by being placed
  432. // in the light DOM.
  433. //
  434. // The resulting order is on native is: Clone, Adopt, Upgrade, Process,
  435. // Update, Connect. document.importNode() performs Clone, Adopt, and Upgrade
  436. // in one step.
  437. //
  438. // The Custom Elements v1 polyfill supports upgrade(), so the order when
  439. // polyfilled is the more ideal: Clone, Process, Adopt, Upgrade, Update,
  440. // Connect.
  441. const fragment = isCEPolyfill ?
  442. this.template.element.content.cloneNode(true) :
  443. document.importNode(this.template.element.content, true);
  444. const stack = [];
  445. const parts = this.template.parts;
  446. // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null
  447. const walker = document.createTreeWalker(fragment, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false);
  448. let partIndex = 0;
  449. let nodeIndex = 0;
  450. let part;
  451. let node = walker.nextNode();
  452. // Loop through all the nodes and parts of a template
  453. while (partIndex < parts.length) {
  454. part = parts[partIndex];
  455. if (!isTemplatePartActive(part)) {
  456. this.__parts.push(undefined);
  457. partIndex++;
  458. continue;
  459. }
  460. // Progress the tree walker until we find our next part's node.
  461. // Note that multiple parts may share the same node (attribute parts
  462. // on a single element), so this loop may not run at all.
  463. while (nodeIndex < part.index) {
  464. nodeIndex++;
  465. if (node.nodeName === 'TEMPLATE') {
  466. stack.push(node);
  467. walker.currentNode = node.content;
  468. }
  469. if ((node = walker.nextNode()) === null) {
  470. // We've exhausted the content inside a nested template element.
  471. // Because we still have parts (the outer for-loop), we know:
  472. // - There is a template in the stack
  473. // - The walker will find a nextNode outside the template
  474. walker.currentNode = stack.pop();
  475. node = walker.nextNode();
  476. }
  477. }
  478. // We've arrived at our part's node.
  479. if (part.type === 'node') {
  480. const textPart = this.processor.handleTextExpression(this.options, part);
  481. textPart.insertAfterNode(node.previousSibling);
  482. this.__parts.push(textPart);
  483. }
  484. else {
  485. this.__parts.push(...this.processor.handleAttributeExpressions(node, part.name, part.strings, this.options, part));
  486. }
  487. partIndex++;
  488. }
  489. if (isCEPolyfill) {
  490. document.adoptNode(fragment);
  491. customElements.upgrade(fragment);
  492. }
  493. return fragment;
  494. }
  495. }
  496. /**
  497. * @license
  498. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  499. * This code may only be used under the BSD style license found at
  500. * http://polymer.github.io/LICENSE.txt
  501. * The complete set of authors may be found at
  502. * http://polymer.github.io/AUTHORS.txt
  503. * The complete set of contributors may be found at
  504. * http://polymer.github.io/CONTRIBUTORS.txt
  505. * Code distributed by Google as part of the polymer project is also
  506. * subject to an additional IP rights grant found at
  507. * http://polymer.github.io/PATENTS.txt
  508. */
  509. let policy;
  510. /**
  511. * Turns the value to trusted HTML. If the application uses Trusted Types the
  512. * value is transformed into TrustedHTML, which can be assigned to execution
  513. * sink. If the application doesn't use Trusted Types, the return value is the
  514. * same as the argument.
  515. */
  516. function convertConstantTemplateStringToTrustedHTML(value) {
  517. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  518. const w = window;
  519. // TrustedTypes have been renamed to trustedTypes
  520. // (https://github.com/WICG/trusted-types/issues/177)
  521. const trustedTypes = (w.trustedTypes || w.TrustedTypes);
  522. if (trustedTypes && !policy) {
  523. policy = trustedTypes.createPolicy('lit-html', { createHTML: (s) => s });
  524. }
  525. return policy ? policy.createHTML(value) : value;
  526. }
  527. const commentMarker = ` ${marker} `;
  528. /**
  529. * Used to clone existing node instead of each time creating new one which is
  530. * slower
  531. */
  532. const emptyTemplateNode = document.createElement('template');
  533. /**
  534. * The return type of `html`, which holds a Template and the values from
  535. * interpolated expressions.
  536. */
  537. class TemplateResult {
  538. constructor(strings, values, type, processor) {
  539. this.strings = strings;
  540. this.values = values;
  541. this.type = type;
  542. this.processor = processor;
  543. }
  544. /**
  545. * Returns a string of HTML used to create a `<template>` element.
  546. */
  547. getHTML() {
  548. const l = this.strings.length - 1;
  549. let html = '';
  550. let isCommentBinding = false;
  551. for (let i = 0; i < l; i++) {
  552. const s = this.strings[i];
  553. // For each binding we want to determine the kind of marker to insert
  554. // into the template source before it's parsed by the browser's HTML
  555. // parser. The marker type is based on whether the expression is in an
  556. // attribute, text, or comment position.
  557. // * For node-position bindings we insert a comment with the marker
  558. // sentinel as its text content, like <!--{{lit-guid}}-->.
  559. // * For attribute bindings we insert just the marker sentinel for the
  560. // first binding, so that we support unquoted attribute bindings.
  561. // Subsequent bindings can use a comment marker because multi-binding
  562. // attributes must be quoted.
  563. // * For comment bindings we insert just the marker sentinel so we don't
  564. // close the comment.
  565. //
  566. // The following code scans the template source, but is *not* an HTML
  567. // parser. We don't need to track the tree structure of the HTML, only
  568. // whether a binding is inside a comment, and if not, if it appears to be
  569. // the first binding in an attribute.
  570. const commentOpen = s.lastIndexOf('<!--');
  571. // We're in comment position if we have a comment open with no following
  572. // comment close. Because <-- can appear in an attribute value there can
  573. // be false positives.
  574. isCommentBinding = (commentOpen > -1 || isCommentBinding) &&
  575. s.indexOf('-->', commentOpen + 1) === -1;
  576. // Check to see if we have an attribute-like sequence preceding the
  577. // expression. This can match "name=value" like structures in text,
  578. // comments, and attribute values, so there can be false-positives.
  579. const attributeMatch = lastAttributeNameRegex.exec(s);
  580. if (attributeMatch === null) {
  581. // We're only in this branch if we don't have a attribute-like
  582. // preceding sequence. For comments, this guards against unusual
  583. // attribute values like <div foo="<!--${'bar'}">. Cases like
  584. // <!-- foo=${'bar'}--> are handled correctly in the attribute branch
  585. // below.
  586. html += s + (isCommentBinding ? commentMarker : nodeMarker);
  587. }
  588. else {
  589. // For attributes we use just a marker sentinel, and also append a
  590. // $lit$ suffix to the name to opt-out of attribute-specific parsing
  591. // that IE and Edge do for style and certain SVG attributes.
  592. html += s.substr(0, attributeMatch.index) + attributeMatch[1] +
  593. attributeMatch[2] + boundAttributeSuffix + attributeMatch[3] +
  594. marker;
  595. }
  596. }
  597. html += this.strings[l];
  598. return html;
  599. }
  600. getTemplateElement() {
  601. const template = emptyTemplateNode.cloneNode();
  602. // this is secure because `this.strings` is a TemplateStringsArray.
  603. // TODO: validate this when
  604. // https://github.com/tc39/proposal-array-is-template-object is implemented.
  605. template.innerHTML =
  606. convertConstantTemplateStringToTrustedHTML(this.getHTML());
  607. return template;
  608. }
  609. }
  610. /**
  611. * A TemplateResult for SVG fragments.
  612. *
  613. * This class wraps HTML in an `<svg>` tag in order to parse its contents in the
  614. * SVG namespace, then modifies the template to remove the `<svg>` tag so that
  615. * clones only container the original fragment.
  616. */
  617. class SVGTemplateResult extends TemplateResult {
  618. getHTML() {
  619. return `<svg>${super.getHTML()}</svg>`;
  620. }
  621. getTemplateElement() {
  622. const template = super.getTemplateElement();
  623. const content = template.content;
  624. const svgElement = content.firstChild;
  625. content.removeChild(svgElement);
  626. reparentNodes(content, svgElement.firstChild);
  627. return template;
  628. }
  629. }
  630. /**
  631. * @license
  632. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  633. * This code may only be used under the BSD style license found at
  634. * http://polymer.github.io/LICENSE.txt
  635. * The complete set of authors may be found at
  636. * http://polymer.github.io/AUTHORS.txt
  637. * The complete set of contributors may be found at
  638. * http://polymer.github.io/CONTRIBUTORS.txt
  639. * Code distributed by Google as part of the polymer project is also
  640. * subject to an additional IP rights grant found at
  641. * http://polymer.github.io/PATENTS.txt
  642. */
  643. const isPrimitive = (value) => {
  644. return (value === null ||
  645. !(typeof value === 'object' || typeof value === 'function'));
  646. };
  647. const isIterable = (value) => {
  648. return Array.isArray(value) ||
  649. // tslint:disable-next-line: no-any
  650. !!(value && value[Symbol.iterator]);
  651. };
  652. const identityFunction = (value) => value;
  653. const noopSanitizer = (_node, _name, _type) => identityFunction;
  654. /**
  655. * A global callback used to get a sanitizer for a given field.
  656. */
  657. let sanitizerFactory = noopSanitizer;
  658. /** Sets the global sanitizer factory. */
  659. const setSanitizerFactory = (newSanitizer) => {
  660. if (sanitizerFactory !== noopSanitizer) {
  661. throw new Error(`Attempted to overwrite existing lit-html security policy.` +
  662. ` setSanitizeDOMValueFactory should be called at most once.`);
  663. }
  664. sanitizerFactory = newSanitizer;
  665. };
  666. /**
  667. * Used to clone text node instead of each time creating new one which is slower
  668. */
  669. const emptyTextNode = document.createTextNode('');
  670. /**
  671. * Writes attribute values to the DOM for a group of AttributeParts bound to a
  672. * single attribute. The value is only set once even if there are multiple parts
  673. * for an attribute.
  674. */
  675. class AttributeCommitter {
  676. constructor(element, name, strings,
  677. // Next breaking change, consider making this param required.
  678. templatePart, kind = 'attribute') {
  679. this.dirty = true;
  680. this.element = element;
  681. this.name = name;
  682. this.strings = strings;
  683. this.parts = [];
  684. let sanitizer = templatePart && templatePart.sanitizer;
  685. if (sanitizer === undefined) {
  686. sanitizer = sanitizerFactory(element, name, kind);
  687. if (templatePart !== undefined) {
  688. templatePart.sanitizer = sanitizer;
  689. }
  690. }
  691. this.sanitizer = sanitizer;
  692. for (let i = 0; i < strings.length - 1; i++) {
  693. this.parts[i] = this._createPart();
  694. }
  695. }
  696. /**
  697. * Creates a single part. Override this to create a differnt type of part.
  698. */
  699. _createPart() {
  700. return new AttributePart(this);
  701. }
  702. _getValue() {
  703. const strings = this.strings;
  704. const parts = this.parts;
  705. const l = strings.length - 1;
  706. // If we're assigning an attribute via syntax like:
  707. // attr="${foo}" or attr=${foo}
  708. // but not
  709. // attr="${foo} ${bar}" or attr="${foo} baz"
  710. // then we don't want to coerce the attribute value into one long
  711. // string. Instead we want to just return the value itself directly,
  712. // so that sanitizeDOMValue can get the actual value rather than
  713. // String(value)
  714. // The exception is if v is an array, in which case we do want to smash
  715. // it together into a string without calling String() on the array.
  716. //
  717. // This also allows trusted values (when using TrustedTypes) being
  718. // assigned to DOM sinks without being stringified in the process.
  719. if (l === 1 && strings[0] === '' && strings[1] === '' &&
  720. parts[0] !== undefined) {
  721. const v = parts[0].value;
  722. if (!isIterable(v)) {
  723. return v;
  724. }
  725. }
  726. let text = '';
  727. for (let i = 0; i < l; i++) {
  728. text += strings[i];
  729. const part = parts[i];
  730. if (part !== undefined) {
  731. const v = part.value;
  732. if (isPrimitive(v) || !isIterable(v)) {
  733. text += typeof v === 'string' ? v : String(v);
  734. }
  735. else {
  736. for (const t of v) {
  737. text += typeof t === 'string' ? t : String(t);
  738. }
  739. }
  740. }
  741. }
  742. text += strings[l];
  743. return text;
  744. }
  745. commit() {
  746. if (this.dirty) {
  747. this.dirty = false;
  748. let value = this._getValue();
  749. value = this.sanitizer(value);
  750. if (typeof value === 'symbol') {
  751. // Native Symbols throw if they're coerced to string.
  752. value = String(value);
  753. }
  754. this.element.setAttribute(this.name, value);
  755. }
  756. }
  757. }
  758. /**
  759. * A Part that controls all or part of an attribute value.
  760. */
  761. class AttributePart {
  762. constructor(committer) {
  763. this.value = undefined;
  764. this.committer = committer;
  765. }
  766. setValue(value) {
  767. if (value !== noChange && (!isPrimitive(value) || value !== this.value)) {
  768. this.value = value;
  769. // If the value is a not a directive, dirty the committer so that it'll
  770. // call setAttribute. If the value is a directive, it'll dirty the
  771. // committer if it calls setValue().
  772. if (!isDirective(value)) {
  773. this.committer.dirty = true;
  774. }
  775. }
  776. }
  777. commit() {
  778. while (isDirective(this.value)) {
  779. const directive = this.value;
  780. this.value = noChange;
  781. // tslint:disable-next-line: no-any
  782. if (directive.isClass) {
  783. // tslint:disable-next-line: no-any
  784. directive.body(this);
  785. }
  786. else {
  787. directive(this);
  788. }
  789. }
  790. if (this.value === noChange) {
  791. return;
  792. }
  793. this.committer.commit();
  794. }
  795. }
  796. /**
  797. * A Part that controls a location within a Node tree. Like a Range, NodePart
  798. * has start and end locations and can set and update the Nodes between those
  799. * locations.
  800. *
  801. * NodeParts support several value types: primitives, Nodes, TemplateResults,
  802. * as well as arrays and iterables of those types.
  803. */
  804. class NodePart {
  805. constructor(options, templatePart) {
  806. this.value = undefined;
  807. this.__pendingValue = undefined;
  808. /**
  809. * The sanitizer to use when writing text contents into this NodePart.
  810. *
  811. * We have to initialize this here rather than at the template literal level
  812. * because the security of text content depends on the context into which
  813. * it's written. e.g. the same text has different security requirements
  814. * when a child of a <script> vs a <style> vs a <div>.
  815. */
  816. this.textSanitizer = undefined;
  817. this.options = options;
  818. this.templatePart = templatePart;
  819. }
  820. /**
  821. * Appends this part into a container.
  822. *
  823. * This part must be empty, as its contents are not automatically moved.
  824. */
  825. appendInto(container) {
  826. this.startNode = container.appendChild(createMarker());
  827. this.endNode = container.appendChild(createMarker());
  828. }
  829. /**
  830. * Inserts this part after the `ref` node (between `ref` and `ref`'s next
  831. * sibling). Both `ref` and its next sibling must be static, unchanging nodes
  832. * such as those that appear in a literal section of a template.
  833. *
  834. * This part must be empty, as its contents are not automatically moved.
  835. */
  836. insertAfterNode(ref) {
  837. this.startNode = ref;
  838. this.endNode = ref.nextSibling;
  839. }
  840. /**
  841. * Appends this part into a parent part.
  842. *
  843. * This part must be empty, as its contents are not automatically moved.
  844. */
  845. appendIntoPart(part) {
  846. part.__insert(this.startNode = createMarker());
  847. part.__insert(this.endNode = createMarker());
  848. }
  849. /**
  850. * Inserts this part after the `ref` part.
  851. *
  852. * This part must be empty, as its contents are not automatically moved.
  853. */
  854. insertAfterPart(ref) {
  855. ref.__insert(this.startNode = createMarker());
  856. this.endNode = ref.endNode;
  857. ref.endNode = this.startNode;
  858. }
  859. setValue(value) {
  860. this.__pendingValue = value;
  861. }
  862. commit() {
  863. while (isDirective(this.__pendingValue)) {
  864. const directive = this.__pendingValue;
  865. this.__pendingValue = noChange;
  866. // tslint:disable-next-line: no-any
  867. if (directive.isClass) {
  868. // tslint:disable-next-line: no-any
  869. directive.body(this);
  870. }
  871. else {
  872. directive(this);
  873. }
  874. }
  875. const value = this.__pendingValue;
  876. if (value === noChange) {
  877. return;
  878. }
  879. if (isPrimitive(value)) {
  880. if (value !== this.value) {
  881. this.__commitText(value);
  882. }
  883. }
  884. else if (value instanceof TemplateResult) {
  885. this.__commitTemplateResult(value);
  886. }
  887. else if (value instanceof Node) {
  888. this.__commitNode(value);
  889. }
  890. else if (isIterable(value)) {
  891. this.__commitIterable(value);
  892. }
  893. else if (value === nothing) {
  894. this.value = nothing;
  895. this.clear();
  896. }
  897. else {
  898. // Fallback, will render the string representation
  899. this.__commitText(value);
  900. }
  901. }
  902. __insert(node) {
  903. this.endNode.parentNode.insertBefore(node, this.endNode);
  904. }
  905. __commitNode(value) {
  906. if (this.value === value) {
  907. return;
  908. }
  909. this.clear();
  910. this.__insert(value);
  911. this.value = value;
  912. }
  913. __commitText(value) {
  914. const node = this.startNode.nextSibling;
  915. value = value == null ? '' : value;
  916. if (node === this.endNode.previousSibling &&
  917. node.nodeType === 3 /* Node.TEXT_NODE */) {
  918. // If we only have a single text node between the markers, we can just
  919. // set its value, rather than replacing it.
  920. if (this.textSanitizer === undefined) {
  921. this.textSanitizer = sanitizerFactory(node, 'data', 'property');
  922. }
  923. const renderedValue = this.textSanitizer(value);
  924. node.data = typeof renderedValue === 'string' ?
  925. renderedValue :
  926. String(renderedValue);
  927. }
  928. else {
  929. // When setting text content, for security purposes it matters a lot what
  930. // the parent is. For example, <style> and <script> need to be handled
  931. // with care, while <span> does not. So first we need to put a text node
  932. // into the document, then we can sanitize its contentx.
  933. const textNode = emptyTextNode.cloneNode();
  934. this.__commitNode(textNode);
  935. if (this.textSanitizer === undefined) {
  936. this.textSanitizer = sanitizerFactory(textNode, 'data', 'property');
  937. }
  938. const renderedValue = this.textSanitizer(value);
  939. textNode.data = typeof renderedValue === 'string' ? renderedValue :
  940. String(renderedValue);
  941. }
  942. this.value = value;
  943. }
  944. __commitTemplateResult(value) {
  945. const template = this.options.templateFactory(value);
  946. if (this.value instanceof TemplateInstance &&
  947. this.value.template === template) {
  948. this.value.update(value.values);
  949. }
  950. else {
  951. // `value` is a template result that was constructed without knowledge of
  952. // the parent we're about to write it into. sanitizeDOMValue hasn't been
  953. // made aware of this relationship, and for scripts and style specifically
  954. // this is known to be unsafe. So in the case where the user is in
  955. // "secure mode" (i.e. when there's a sanitizeDOMValue set), we just want
  956. // to forbid this because it's not a use case we want to support.
  957. // We only apply this policy when sanitizerFactory has been set to
  958. // prevent this from being a breaking change to the library.
  959. const parent = this.endNode.parentNode;
  960. if (sanitizerFactory !== noopSanitizer && parent.nodeName === 'STYLE' ||
  961. parent.nodeName === 'SCRIPT') {
  962. this.__commitText('/* lit-html will not write ' +
  963. 'TemplateResults to scripts and styles */');
  964. return;
  965. }
  966. // Make sure we propagate the template processor from the TemplateResult
  967. // so that we use its syntax extension, etc. The template factory comes
  968. // from the render function options so that it can control template
  969. // caching and preprocessing.
  970. const instance = new TemplateInstance(template, value.processor, this.options);
  971. const fragment = instance._clone();
  972. instance.update(value.values);
  973. this.__commitNode(fragment);
  974. this.value = instance;
  975. }
  976. }
  977. __commitIterable(value) {
  978. // For an Iterable, we create a new InstancePart per item, then set its
  979. // value to the item. This is a little bit of overhead for every item in
  980. // an Iterable, but it lets us recurse easily and efficiently update Arrays
  981. // of TemplateResults that will be commonly returned from expressions like:
  982. // array.map((i) => html`${i}`), by reusing existing TemplateInstances.
  983. // If _value is an array, then the previous render was of an
  984. // iterable and _value will contain the NodeParts from the previous
  985. // render. If _value is not an array, clear this part and make a new
  986. // array for NodeParts.
  987. if (!Array.isArray(this.value)) {
  988. this.value = [];
  989. this.clear();
  990. }
  991. // Lets us keep track of how many items we stamped so we can clear leftover
  992. // items from a previous render
  993. const itemParts = this.value;
  994. let partIndex = 0;
  995. let itemPart;
  996. for (const item of value) {
  997. // Try to reuse an existing part
  998. itemPart = itemParts[partIndex];
  999. // If no existing part, create a new one
  1000. if (itemPart === undefined) {
  1001. itemPart = new NodePart(this.options, this.templatePart);
  1002. itemParts.push(itemPart);
  1003. if (partIndex === 0) {
  1004. itemPart.appendIntoPart(this);
  1005. }
  1006. else {
  1007. itemPart.insertAfterPart(itemParts[partIndex - 1]);
  1008. }
  1009. }
  1010. itemPart.setValue(item);
  1011. itemPart.commit();
  1012. partIndex++;
  1013. }
  1014. if (partIndex < itemParts.length) {
  1015. // Truncate the parts array so _value reflects the current state
  1016. itemParts.length = partIndex;
  1017. this.clear(itemPart && itemPart.endNode);
  1018. }
  1019. }
  1020. clear(startNode = this.startNode) {
  1021. removeNodes(this.startNode.parentNode, startNode.nextSibling, this.endNode);
  1022. }
  1023. }
  1024. /**
  1025. * Implements a boolean attribute, roughly as defined in the HTML
  1026. * specification.
  1027. *
  1028. * If the value is truthy, then the attribute is present with a value of
  1029. * ''. If the value is falsey, the attribute is removed.
  1030. */
  1031. class BooleanAttributePart {
  1032. constructor(element, name, strings) {
  1033. this.value = undefined;
  1034. this.__pendingValue = undefined;
  1035. if (strings.length !== 2 || strings[0] !== '' || strings[1] !== '') {
  1036. throw new Error('Boolean attributes can only contain a single expression');
  1037. }
  1038. this.element = element;
  1039. this.name = name;
  1040. this.strings = strings;
  1041. }
  1042. setValue(value) {
  1043. this.__pendingValue = value;
  1044. }
  1045. commit() {
  1046. while (isDirective(this.__pendingValue)) {
  1047. const directive = this.__pendingValue;
  1048. this.__pendingValue = noChange;
  1049. // tslint:disable-next-line: no-any
  1050. if (directive.isClass) {
  1051. // tslint:disable-next-line: no-any
  1052. directive.body(this);
  1053. }
  1054. else {
  1055. directive(this);
  1056. }
  1057. }
  1058. if (this.__pendingValue === noChange) {
  1059. return;
  1060. }
  1061. const value = !!this.__pendingValue;
  1062. if (this.value !== value) {
  1063. if (value) {
  1064. this.element.setAttribute(this.name, '');
  1065. }
  1066. else {
  1067. this.element.removeAttribute(this.name);
  1068. }
  1069. this.value = value;
  1070. }
  1071. this.__pendingValue = noChange;
  1072. }
  1073. }
  1074. /**
  1075. * Sets attribute values for PropertyParts, so that the value is only set once
  1076. * even if there are multiple parts for a property.
  1077. *
  1078. * If an expression controls the whole property value, then the value is simply
  1079. * assigned to the property under control. If there are string literals or
  1080. * multiple expressions, then the strings are expressions are interpolated into
  1081. * a string first.
  1082. */
  1083. class PropertyCommitter extends AttributeCommitter {
  1084. constructor(element, name, strings,
  1085. // Next breaking change, consider making this param required.
  1086. templatePart) {
  1087. super(element, name, strings, templatePart, 'property');
  1088. this.single =
  1089. (strings.length === 2 && strings[0] === '' && strings[1] === '');
  1090. }
  1091. _createPart() {
  1092. return new PropertyPart(this);
  1093. }
  1094. _getValue() {
  1095. if (this.single) {
  1096. return this.parts[0].value;
  1097. }
  1098. return super._getValue();
  1099. }
  1100. commit() {
  1101. if (this.dirty) {
  1102. this.dirty = false;
  1103. let value = this._getValue();
  1104. value = this.sanitizer(value);
  1105. // tslint:disable-next-line: no-any
  1106. this.element[this.name] = value;
  1107. }
  1108. }
  1109. }
  1110. class PropertyPart extends AttributePart {
  1111. }
  1112. // Detect event listener options support. If the `capture` property is read
  1113. // from the options object, then options are supported. If not, then the third
  1114. // argument to add/removeEventListener is interpreted as the boolean capture
  1115. // value so we should only pass the `capture` property.
  1116. let eventOptionsSupported = false;
  1117. // Wrap into an IIFE because MS Edge <= v41 does not support having try/catch
  1118. // blocks right into the body of a module
  1119. (() => {
  1120. try {
  1121. const options = {
  1122. get capture() {
  1123. eventOptionsSupported = true;
  1124. return false;
  1125. }
  1126. };
  1127. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  1128. window.addEventListener('test', options, options);
  1129. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  1130. window.removeEventListener('test', options, options);
  1131. }
  1132. catch (_e) {
  1133. // noop
  1134. }
  1135. })();
  1136. class EventPart {
  1137. constructor(element, eventName, eventContext) {
  1138. this.value = undefined;
  1139. this.__pendingValue = undefined;
  1140. this.element = element;
  1141. this.eventName = eventName;
  1142. this.eventContext = eventContext;
  1143. this.__boundHandleEvent = (e) => this.handleEvent(e);
  1144. }
  1145. setValue(value) {
  1146. this.__pendingValue = value;
  1147. }
  1148. commit() {
  1149. while (isDirective(this.__pendingValue)) {
  1150. const directive = this.__pendingValue;
  1151. this.__pendingValue = noChange;
  1152. // tslint:disable-next-line: no-any
  1153. if (directive.isClass) {
  1154. // tslint:disable-next-line: no-any
  1155. directive.body(this);
  1156. }
  1157. else {
  1158. directive(this);
  1159. }
  1160. }
  1161. if (this.__pendingValue === noChange) {
  1162. return;
  1163. }
  1164. const newListener = this.__pendingValue;
  1165. const oldListener = this.value;
  1166. const shouldRemoveListener = newListener == null ||
  1167. oldListener != null &&
  1168. (newListener.capture !== oldListener.capture ||
  1169. newListener.once !== oldListener.once ||
  1170. newListener.passive !== oldListener.passive);
  1171. const shouldAddListener = newListener != null && (oldListener == null || shouldRemoveListener);
  1172. if (shouldRemoveListener) {
  1173. this.element.removeEventListener(this.eventName, this.__boundHandleEvent, this.__options);
  1174. }
  1175. if (shouldAddListener) {
  1176. this.__options = getOptions(newListener);
  1177. this.element.addEventListener(this.eventName, this.__boundHandleEvent, this.__options);
  1178. }
  1179. this.value = newListener;
  1180. this.__pendingValue = noChange;
  1181. }
  1182. handleEvent(event) {
  1183. if (typeof this.value === 'function') {
  1184. this.value.call(this.eventContext || this.element, event);
  1185. }
  1186. else {
  1187. this.value.handleEvent(event);
  1188. }
  1189. }
  1190. }
  1191. // We copy options because of the inconsistent behavior of browsers when reading
  1192. // the third argument of add/removeEventListener. IE11 doesn't support options
  1193. // at all. Chrome 41 only reads `capture` if the argument is an object.
  1194. const getOptions = (o) => o &&
  1195. (eventOptionsSupported ?
  1196. { capture: o.capture, passive: o.passive, once: o.once } :
  1197. o.capture);
  1198. /**
  1199. * @license
  1200. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1201. * This code may only be used under the BSD style license found at
  1202. * http://polymer.github.io/LICENSE.txt
  1203. * The complete set of authors may be found at
  1204. * http://polymer.github.io/AUTHORS.txt
  1205. * The complete set of contributors may be found at
  1206. * http://polymer.github.io/CONTRIBUTORS.txt
  1207. * Code distributed by Google as part of the polymer project is also
  1208. * subject to an additional IP rights grant found at
  1209. * http://polymer.github.io/PATENTS.txt
  1210. */
  1211. /**
  1212. * Creates Parts when a template is instantiated.
  1213. */
  1214. class DefaultTemplateProcessor {
  1215. /**
  1216. * Create parts for an attribute-position binding, given the event, attribute
  1217. * name, and string literals.
  1218. *
  1219. * @param element The element containing the binding
  1220. * @param name The attribute name
  1221. * @param strings The string literals. There are always at least two strings,
  1222. * event for fully-controlled bindings with a single expression.
  1223. */
  1224. handleAttributeExpressions(element, name, strings, options, templatePart) {
  1225. const prefix = name[0];
  1226. if (prefix === '.') {
  1227. const committer = new PropertyCommitter(element, name.slice(1), strings, templatePart);
  1228. return committer.parts;
  1229. }
  1230. if (prefix === '@') {
  1231. return [new EventPart(element, name.slice(1), options.eventContext)];
  1232. }
  1233. if (prefix === '?') {
  1234. return [new BooleanAttributePart(element, name.slice(1), strings)];
  1235. }
  1236. const committer = new AttributeCommitter(element, name, strings, templatePart);
  1237. return committer.parts;
  1238. }
  1239. /**
  1240. * Create parts for a text-position binding.
  1241. * @param templateFactory
  1242. */
  1243. handleTextExpression(options, nodeTemplatePart) {
  1244. return new NodePart(options, nodeTemplatePart);
  1245. }
  1246. }
  1247. const defaultTemplateProcessor = new DefaultTemplateProcessor();
  1248. /**
  1249. * @license
  1250. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1251. * This code may only be used under the BSD style license found at
  1252. * http://polymer.github.io/LICENSE.txt
  1253. * The complete set of authors may be found at
  1254. * http://polymer.github.io/AUTHORS.txt
  1255. * The complete set of contributors may be found at
  1256. * http://polymer.github.io/CONTRIBUTORS.txt
  1257. * Code distributed by Google as part of the polymer project is also
  1258. * subject to an additional IP rights grant found at
  1259. * http://polymer.github.io/PATENTS.txt
  1260. */
  1261. /**
  1262. * The default TemplateFactory which caches Templates keyed on
  1263. * result.type and result.strings.
  1264. */
  1265. function templateFactory(result) {
  1266. let templateCache = templateCaches.get(result.type);
  1267. if (templateCache === undefined) {
  1268. templateCache = {
  1269. stringsArray: new WeakMap(),
  1270. keyString: new Map()
  1271. };
  1272. templateCaches.set(result.type, templateCache);
  1273. }
  1274. let template = templateCache.stringsArray.get(result.strings);
  1275. if (template !== undefined) {
  1276. return template;
  1277. }
  1278. // If the TemplateStringsArray is new, generate a key from the strings
  1279. // This key is shared between all templates with identical content
  1280. const key = result.strings.join(marker);
  1281. // Check if we already have a Template for this key
  1282. template = templateCache.keyString.get(key);
  1283. if (template === undefined) {
  1284. // If we have not seen this key before, create a new Template
  1285. template = new Template(result, result.getTemplateElement());
  1286. // Cache the Template for this key
  1287. templateCache.keyString.set(key, template);
  1288. }
  1289. // Cache all future queries for this TemplateStringsArray
  1290. templateCache.stringsArray.set(result.strings, template);
  1291. return template;
  1292. }
  1293. const templateCaches = new Map();
  1294. /**
  1295. * @license
  1296. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1297. * This code may only be used under the BSD style license found at
  1298. * http://polymer.github.io/LICENSE.txt
  1299. * The complete set of authors may be found at
  1300. * http://polymer.github.io/AUTHORS.txt
  1301. * The complete set of contributors may be found at
  1302. * http://polymer.github.io/CONTRIBUTORS.txt
  1303. * Code distributed by Google as part of the polymer project is also
  1304. * subject to an additional IP rights grant found at
  1305. * http://polymer.github.io/PATENTS.txt
  1306. */
  1307. const parts = new WeakMap();
  1308. /**
  1309. * Renders a template result or other value to a container.
  1310. *
  1311. * To update a container with new values, reevaluate the template literal and
  1312. * call `render` with the new result.
  1313. *
  1314. * @param result Any value renderable by NodePart - typically a TemplateResult
  1315. * created by evaluating a template tag like `html` or `svg`.
  1316. * @param container A DOM parent to render to. The entire contents are either
  1317. * replaced, or efficiently updated if the same result type was previous
  1318. * rendered there.
  1319. * @param options RenderOptions for the entire render tree rendered to this
  1320. * container. Render options must *not* change between renders to the same
  1321. * container, as those changes will not effect previously rendered DOM.
  1322. */
  1323. const render = (result, container, options) => {
  1324. let part = parts.get(container);
  1325. if (part === undefined) {
  1326. removeNodes(container, container.firstChild);
  1327. parts.set(container, part = new NodePart(Object.assign({ templateFactory }, options), undefined));
  1328. part.appendInto(container);
  1329. }
  1330. part.setValue(result);
  1331. part.commit();
  1332. };
  1333. /**
  1334. * @license
  1335. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1336. * This code may only be used under the BSD style license found at
  1337. * http://polymer.github.io/LICENSE.txt
  1338. * The complete set of authors may be found at
  1339. * http://polymer.github.io/AUTHORS.txt
  1340. * The complete set of contributors may be found at
  1341. * http://polymer.github.io/CONTRIBUTORS.txt
  1342. * Code distributed by Google as part of the polymer project is also
  1343. * subject to an additional IP rights grant found at
  1344. * http://polymer.github.io/PATENTS.txt
  1345. */
  1346. // IMPORTANT: do not change the property name or the assignment expression.
  1347. // This line will be used in regexes to search for lit-html usage.
  1348. // TODO(justinfagnani): inject version number at build time
  1349. const isBrowser = typeof window !== 'undefined';
  1350. if (isBrowser) {
  1351. // If we run in the browser set version
  1352. (window['litHtmlVersions'] || (window['litHtmlVersions'] = [])).push('1.1.7');
  1353. }
  1354. /**
  1355. * Interprets a template literal as an HTML template that can efficiently
  1356. * render to and update a container.
  1357. */
  1358. const html = (strings, ...values) => new TemplateResult(strings, values, 'html', defaultTemplateProcessor);
  1359. /**
  1360. * Interprets a template literal as an SVG template that can efficiently
  1361. * render to and update a container.
  1362. */
  1363. const svg = (strings, ...values) => new SVGTemplateResult(strings, values, 'svg', defaultTemplateProcessor);
  1364. var lithtml = /*#__PURE__*/Object.freeze({
  1365. __proto__: null,
  1366. html: html,
  1367. svg: svg,
  1368. DefaultTemplateProcessor: DefaultTemplateProcessor,
  1369. defaultTemplateProcessor: defaultTemplateProcessor,
  1370. directive: directive,
  1371. Directive: Directive,
  1372. isDirective: isDirective,
  1373. removeNodes: removeNodes,
  1374. reparentNodes: reparentNodes,
  1375. noChange: noChange,
  1376. nothing: nothing,
  1377. AttributeCommitter: AttributeCommitter,
  1378. AttributePart: AttributePart,
  1379. BooleanAttributePart: BooleanAttributePart,
  1380. EventPart: EventPart,
  1381. isIterable: isIterable,
  1382. isPrimitive: isPrimitive,
  1383. NodePart: NodePart,
  1384. PropertyCommitter: PropertyCommitter,
  1385. PropertyPart: PropertyPart,
  1386. get sanitizerFactory () { return sanitizerFactory; },
  1387. setSanitizerFactory: setSanitizerFactory,
  1388. parts: parts,
  1389. render: render,
  1390. templateCaches: templateCaches,
  1391. templateFactory: templateFactory,
  1392. TemplateInstance: TemplateInstance,
  1393. SVGTemplateResult: SVGTemplateResult,
  1394. TemplateResult: TemplateResult,
  1395. createMarker: createMarker,
  1396. isTemplatePartActive: isTemplatePartActive,
  1397. Template: Template
  1398. });
  1399. /**
  1400. * @license
  1401. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1402. * This code may only be used under the BSD style license found at
  1403. * http://polymer.github.io/LICENSE.txt
  1404. * The complete set of authors may be found at
  1405. * http://polymer.github.io/AUTHORS.txt
  1406. * The complete set of contributors may be found at
  1407. * http://polymer.github.io/CONTRIBUTORS.txt
  1408. * Code distributed by Google as part of the polymer project is also
  1409. * subject to an additional IP rights grant found at
  1410. * http://polymer.github.io/PATENTS.txt
  1411. */
  1412. var __asyncValues = function (o) {
  1413. if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
  1414. var m = o[Symbol.asyncIterator], i;
  1415. return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
  1416. function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
  1417. function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
  1418. };
  1419. /**
  1420. * A directive that renders the items of an async iterable[1], appending new
  1421. * values after previous values, similar to the built-in support for iterables.
  1422. *
  1423. * Async iterables are objects with a [Symbol.asyncIterator] method, which
  1424. * returns an iterator who's `next()` method returns a Promise. When a new
  1425. * value is available, the Promise resolves and the value is appended to the
  1426. * Part controlled by the directive. If another value other than this
  1427. * directive has been set on the Part, the iterable will no longer be listened
  1428. * to and new values won't be written to the Part.
  1429. *
  1430. * [1]: https://github.com/tc39/proposal-async-iteration
  1431. *
  1432. * @param value An async iterable
  1433. * @param mapper An optional function that maps from (value, index) to another
  1434. * value. Useful for generating templates for each item in the iterable.
  1435. */
  1436. const asyncAppend = directive((value, mapper) => async (part) => {
  1437. var e_1, _a;
  1438. if (!(part instanceof NodePart)) {
  1439. throw new Error('asyncAppend can only be used in text bindings');
  1440. }
  1441. // If we've already set up this particular iterable, we don't need
  1442. // to do anything.
  1443. if (value === part.value) {
  1444. return;
  1445. }
  1446. part.value = value;
  1447. // We keep track of item Parts across iterations, so that we can
  1448. // share marker nodes between consecutive Parts.
  1449. let itemPart;
  1450. let i = 0;
  1451. try {
  1452. for (var value_1 = __asyncValues(value), value_1_1; value_1_1 = await value_1.next(), !value_1_1.done;) {
  1453. let v = value_1_1.value;
  1454. // Check to make sure that value is the still the current value of
  1455. // the part, and if not bail because a new value owns this part
  1456. if (part.value !== value) {
  1457. break;
  1458. }
  1459. // When we get the first value, clear the part. This lets the
  1460. // previous value display until we can replace it.
  1461. if (i === 0) {
  1462. part.clear();
  1463. }
  1464. // As a convenience, because functional-programming-style
  1465. // transforms of iterables and async iterables requires a library,
  1466. // we accept a mapper function. This is especially convenient for
  1467. // rendering a template for each item.
  1468. if (mapper !== undefined) {
  1469. // This is safe because T must otherwise be treated as unknown by
  1470. // the rest of the system.
  1471. v = mapper(v, i);
  1472. }
  1473. // Like with sync iterables, each item induces a Part, so we need
  1474. // to keep track of start and end nodes for the Part.
  1475. // Note: Because these Parts are not updatable like with a sync
  1476. // iterable (if we render a new value, we always clear), it may
  1477. // be possible to optimize away the Parts and just re-use the
  1478. // Part.setValue() logic.
  1479. let itemStartNode = part.startNode;
  1480. // Check to see if we have a previous item and Part
  1481. if (itemPart !== undefined) {
  1482. // Create a new node to separate the previous and next Parts
  1483. itemStartNode = createMarker();
  1484. // itemPart is currently the Part for the previous item. Set
  1485. // it's endNode to the node we'll use for the next Part's
  1486. // startNode.
  1487. itemPart.endNode = itemStartNode;
  1488. part.endNode.parentNode.insertBefore(itemStartNode, part.endNode);
  1489. }
  1490. itemPart = new NodePart(part.options, part.templatePart);
  1491. itemPart.insertAfterNode(itemStartNode);
  1492. itemPart.setValue(v);
  1493. itemPart.commit();
  1494. i++;
  1495. }
  1496. }
  1497. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  1498. finally {
  1499. try {
  1500. if (value_1_1 && !value_1_1.done && (_a = value_1.return)) await _a.call(value_1);
  1501. }
  1502. finally { if (e_1) throw e_1.error; }
  1503. }
  1504. });
  1505. /**
  1506. * @license
  1507. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1508. * This code may only be used under the BSD style license found at
  1509. * http://polymer.github.io/LICENSE.txt
  1510. * The complete set of authors may be found at
  1511. * http://polymer.github.io/AUTHORS.txt
  1512. * The complete set of contributors may be found at
  1513. * http://polymer.github.io/CONTRIBUTORS.txt
  1514. * Code distributed by Google as part of the polymer project is also
  1515. * subject to an additional IP rights grant found at
  1516. * http://polymer.github.io/PATENTS.txt
  1517. */
  1518. var __asyncValues$1 = function (o) {
  1519. if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
  1520. var m = o[Symbol.asyncIterator], i;
  1521. return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
  1522. function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
  1523. function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
  1524. };
  1525. /**
  1526. * A directive that renders the items of an async iterable[1], replacing
  1527. * previous values with new values, so that only one value is ever rendered
  1528. * at a time.
  1529. *
  1530. * Async iterables are objects with a [Symbol.asyncIterator] method, which
  1531. * returns an iterator who's `next()` method returns a Promise. When a new
  1532. * value is available, the Promise resolves and the value is rendered to the
  1533. * Part controlled by the directive. If another value other than this
  1534. * directive has been set on the Part, the iterable will no longer be listened
  1535. * to and new values won't be written to the Part.
  1536. *
  1537. * [1]: https://github.com/tc39/proposal-async-iteration
  1538. *
  1539. * @param value An async iterable
  1540. * @param mapper An optional function that maps from (value, index) to another
  1541. * value. Useful for generating templates for each item in the iterable.
  1542. */
  1543. const asyncReplace = directive((value, mapper) => async (part) => {
  1544. var e_1, _a;
  1545. if (!(part instanceof NodePart)) {
  1546. throw new Error('asyncReplace can only be used in text bindings');
  1547. }
  1548. // If we've already set up this particular iterable, we don't need
  1549. // to do anything.
  1550. if (value === part.value) {
  1551. return;
  1552. }
  1553. // We nest a new part to keep track of previous item values separately
  1554. // of the iterable as a value itself.
  1555. const itemPart = new NodePart(part.options, part.templatePart);
  1556. part.value = value;
  1557. let i = 0;
  1558. try {
  1559. for (var value_1 = __asyncValues$1(value), value_1_1; value_1_1 = await value_1.next(), !value_1_1.done;) {
  1560. let v = value_1_1.value;
  1561. // Check to make sure that value is the still the current value of
  1562. // the part, and if not bail because a new value owns this part
  1563. if (part.value !== value) {
  1564. break;
  1565. }
  1566. // When we get the first value, clear the part. This let's the
  1567. // previous value display until we can replace it.
  1568. if (i === 0) {
  1569. part.clear();
  1570. itemPart.appendIntoPart(part);
  1571. }
  1572. // As a convenience, because functional-programming-style
  1573. // transforms of iterables and async iterables requires a library,
  1574. // we accept a mapper function. This is especially convenient for
  1575. // rendering a template for each item.
  1576. if (mapper !== undefined) {
  1577. // This is safe because T must otherwise be treated as unknown by
  1578. // the rest of the system.
  1579. v = mapper(v, i);
  1580. }
  1581. itemPart.setValue(v);
  1582. itemPart.commit();
  1583. i++;
  1584. }
  1585. }
  1586. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  1587. finally {
  1588. try {
  1589. if (value_1_1 && !value_1_1.done && (_a = value_1.return)) await _a.call(value_1);
  1590. }
  1591. finally { if (e_1) throw e_1.error; }
  1592. }
  1593. });
  1594. /**
  1595. * @license
  1596. * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
  1597. * This code may only be used under the BSD style license found at
  1598. * http://polymer.github.io/LICENSE.txt
  1599. * The complete set of authors may be found at
  1600. * http://polymer.github.io/AUTHORS.txt
  1601. * The complete set of contributors may be found at
  1602. * http://polymer.github.io/CONTRIBUTORS.txt
  1603. * Code distributed by Google as part of the polymer project is also
  1604. * subject to an additional IP rights grant found at
  1605. * http://polymer.github.io/PATENTS.txt
  1606. */
  1607. const templateCaches$1 = new WeakMap();
  1608. /**
  1609. * Enables fast switching between multiple templates by caching the DOM nodes
  1610. * and TemplateInstances produced by the templates.
  1611. *
  1612. * Example:
  1613. *
  1614. * ```
  1615. * let checked = false;
  1616. *
  1617. * html`
  1618. * ${cache(checked ? html`input is checked` : html`input is not checked`)}
  1619. * `
  1620. * ```
  1621. */
  1622. const cache = directive((value) => (part) => {
  1623. if (!(part instanceof NodePart)) {
  1624. throw new Error('cache can only be used in text bindings');
  1625. }
  1626. let templateCache = templateCaches$1.get(part);
  1627. if (templateCache === undefined) {
  1628. templateCache = new WeakMap();
  1629. templateCaches$1.set(part, templateCache);
  1630. }
  1631. const previousValue = part.value;
  1632. // First, can we update the current TemplateInstance, or do we need to move
  1633. // the current nodes into the cache?
  1634. if (previousValue instanceof TemplateInstance) {
  1635. if (value instanceof TemplateResult &&
  1636. previousValue.template === part.options.templateFactory(value)) {
  1637. // Same Template, just trigger an update of the TemplateInstance
  1638. part.setValue(value);
  1639. return;
  1640. }
  1641. else {
  1642. // Not the same Template, move the nodes from the DOM into the cache.
  1643. let cachedTemplate = templateCache.get(previousValue.template);
  1644. if (cachedTemplate === undefined) {
  1645. cachedTemplate = {
  1646. instance: previousValue,
  1647. nodes: document.createDocumentFragment(),
  1648. };
  1649. templateCache.set(previousValue.template, cachedTemplate);
  1650. }
  1651. reparentNodes(cachedTemplate.nodes, part.startNode.nextSibling, part.endNode);
  1652. }
  1653. }
  1654. // Next, can we reuse nodes from the cache?
  1655. if (value instanceof TemplateResult) {
  1656. const template = part.options.templateFactory(value);
  1657. const cachedTemplate = templateCache.get(template);
  1658. if (cachedTemplate !== undefined) {
  1659. // Move nodes out of cache
  1660. part.setValue(cachedTemplate.nodes);
  1661. part.commit();
  1662. // Set the Part value to the TemplateInstance so it'll update it.
  1663. part.value = cachedTemplate.instance;
  1664. }
  1665. }
  1666. part.setValue(value);
  1667. });
  1668. /**
  1669. * @license
  1670. * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
  1671. * This code may only be used under the BSD style license found at
  1672. * http://polymer.github.io/LICENSE.txt
  1673. * The complete set of authors may be found at
  1674. * http://polymer.github.io/AUTHORS.txt
  1675. * The complete set of contributors may be found at
  1676. * http://polymer.github.io/CONTRIBUTORS.txt
  1677. * Code distributed by Google as part of the polymer project is also
  1678. * subject to an additional IP rights grant found at
  1679. * http://polymer.github.io/PATENTS.txt
  1680. */
  1681. /**
  1682. * Stores the ClassInfo object applied to a given AttributePart.
  1683. * Used to unset existing values when a new ClassInfo object is applied.
  1684. */
  1685. const previousClassesCache = new WeakMap();
  1686. /**
  1687. * A directive that applies CSS classes. This must be used in the `class`
  1688. * attribute and must be the only part used in the attribute. It takes each
  1689. * property in the `classInfo` argument and adds the property name to the
  1690. * element's `classList` if the property value is truthy; if the property value
  1691. * is falsey, the property name is removed from the element's `classList`. For
  1692. * example
  1693. * `{foo: bar}` applies the class `foo` if the value of `bar` is truthy.
  1694. * @param classInfo {ClassInfo}
  1695. */
  1696. const classMap = directive((classInfo) => (part) => {
  1697. if (!(part instanceof AttributePart) || (part instanceof PropertyPart) ||
  1698. part.committer.name !== 'class' || part.committer.parts.length > 1) {
  1699. throw new Error('The `classMap` directive must be used in the `class` attribute ' +
  1700. 'and must be the only part in the attribute.');
  1701. }
  1702. const { committer } = part;
  1703. const { element } = committer;
  1704. let previousClasses = previousClassesCache.get(part);
  1705. if (previousClasses === undefined) {
  1706. // Write static classes once
  1707. element.className = committer.strings.join(' ');
  1708. previousClassesCache.set(part, previousClasses = new Set());
  1709. }
  1710. const { classList } = element;
  1711. // Remove old classes that no longer apply
  1712. // We use forEach() instead of for-of so that re don't require down-level
  1713. // iteration.
  1714. previousClasses.forEach((name) => {
  1715. if (!(name in classInfo)) {
  1716. classList.remove(name);
  1717. previousClasses.delete(name);
  1718. }
  1719. });
  1720. // Add or remove classes based on their classMap value
  1721. for (const name in classInfo) {
  1722. const value = classInfo[name];
  1723. // We explicitly want a loose truthy check of `value` because it seems more
  1724. // convenient that '' and 0 are skipped.
  1725. if (value != previousClasses.has(name)) {
  1726. if (value) {
  1727. classList.add(name);
  1728. previousClasses.add(name);
  1729. }
  1730. else {
  1731. classList.remove(name);
  1732. previousClasses.delete(name);
  1733. }
  1734. }
  1735. }
  1736. });
  1737. /**
  1738. * @license
  1739. * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
  1740. * This code may only be used under the BSD style license found at
  1741. * http://polymer.github.io/LICENSE.txt
  1742. * The complete set of authors may be found at
  1743. * http://polymer.github.io/AUTHORS.txt
  1744. * The complete set of contributors may be found at
  1745. * http://polymer.github.io/CONTRIBUTORS.txt
  1746. * Code distributed by Google as part of the polymer project is also
  1747. * subject to an additional IP rights grant found at
  1748. * http://polymer.github.io/PATENTS.txt
  1749. */
  1750. const previousValues = new WeakMap();
  1751. /**
  1752. * Prevents re-render of a template function until a single value or an array of
  1753. * values changes.
  1754. *
  1755. * Example:
  1756. *
  1757. * ```js
  1758. * html`
  1759. * <div>
  1760. * ${guard([user.id, company.id], () => html`...`)}
  1761. * </div>
  1762. * ```
  1763. *
  1764. * In this case, the template only renders if either `user.id` or `company.id`
  1765. * changes.
  1766. *
  1767. * guard() is useful with immutable data patterns, by preventing expensive work
  1768. * until data updates.
  1769. *
  1770. * Example:
  1771. *
  1772. * ```js
  1773. * html`
  1774. * <div>
  1775. * ${guard([immutableItems], () => immutableItems.map(i => html`${i}`))}
  1776. * </div>
  1777. * ```
  1778. *
  1779. * In this case, items are mapped over only when the array reference changes.
  1780. *
  1781. * @param value the value to check before re-rendering
  1782. * @param f the template function
  1783. */
  1784. const guard = directive((value, f) => (part) => {
  1785. const previousValue = previousValues.get(part);
  1786. if (Array.isArray(value)) {
  1787. // Dirty-check arrays by item
  1788. if (Array.isArray(previousValue) &&
  1789. previousValue.length === value.length &&
  1790. value.every((v, i) => v === previousValue[i])) {
  1791. return;
  1792. }
  1793. }
  1794. else if (previousValue === value &&
  1795. (value !== undefined || previousValues.has(part))) {
  1796. // Dirty-check non-arrays by identity
  1797. return;
  1798. }
  1799. part.setValue(f());
  1800. // Copy the value if it's an array so that if it's mutated we don't forget
  1801. // what the previous values were.
  1802. previousValues.set(part, Array.isArray(value) ? Array.from(value) : value);
  1803. });
  1804. /**
  1805. * @license
  1806. * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
  1807. * This code may only be used under the BSD style license found at
  1808. * http://polymer.github.io/LICENSE.txt
  1809. * The complete set of authors may be found at
  1810. * http://polymer.github.io/AUTHORS.txt
  1811. * The complete set of contributors may be found at
  1812. * http://polymer.github.io/CONTRIBUTORS.txt
  1813. * Code distributed by Google as part of the polymer project is also
  1814. * subject to an additional IP rights grant found at
  1815. * http://polymer.github.io/PATENTS.txt
  1816. */
  1817. /**
  1818. * For AttributeParts, sets the attribute if the value is defined and removes
  1819. * the attribute if the value is undefined.
  1820. *
  1821. * For other part types, this directive is a no-op.
  1822. */
  1823. const ifDefined = directive((value) => (part) => {
  1824. if (value === undefined && part instanceof AttributePart) {
  1825. if (value !== part.value) {
  1826. const name = part.committer.name;
  1827. part.committer.element.removeAttribute(name);
  1828. }
  1829. }
  1830. else {
  1831. part.setValue(value);
  1832. }
  1833. });
  1834. /**
  1835. * @license
  1836. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  1837. * This code may only be used under the BSD style license found at
  1838. * http://polymer.github.io/LICENSE.txt
  1839. * The complete set of authors may be found at
  1840. * http://polymer.github.io/AUTHORS.txt
  1841. * The complete set of contributors may be found at
  1842. * http://polymer.github.io/CONTRIBUTORS.txt
  1843. * Code distributed by Google as part of the polymer project is also
  1844. * subject to an additional IP rights grant found at
  1845. * http://polymer.github.io/PATENTS.txt
  1846. */
  1847. // Helper functions for manipulating parts
  1848. // TODO(kschaaf): Refactor into Part API?
  1849. const createAndInsertPart = (containerPart, beforePart) => {
  1850. const container = containerPart.startNode.parentNode;
  1851. const beforeNode = beforePart == null ? containerPart.endNode : beforePart.startNode;
  1852. const startNode = container.insertBefore(createMarker(), beforeNode);
  1853. container.insertBefore(createMarker(), beforeNode);
  1854. const newPart = new NodePart(containerPart.options, undefined);
  1855. newPart.insertAfterNode(startNode);
  1856. return newPart;
  1857. };
  1858. const updatePart = (part, value) => {
  1859. part.setValue(value);
  1860. part.commit();
  1861. return part;
  1862. };
  1863. const insertPartBefore = (containerPart, part, ref) => {
  1864. const container = containerPart.startNode.parentNode;
  1865. const beforeNode = ref ? ref.startNode : containerPart.endNode;
  1866. const endNode = part.endNode.nextSibling;
  1867. if (endNode !== beforeNode) {
  1868. reparentNodes(container, part.startNode, endNode, beforeNode);
  1869. }
  1870. };
  1871. const removePart = (part) => {
  1872. removeNodes(part.startNode.parentNode, part.startNode, part.endNode.nextSibling);
  1873. };
  1874. // Helper for generating a map of array item to its index over a subset
  1875. // of an array (used to lazily generate `newKeyToIndexMap` and
  1876. // `oldKeyToIndexMap`)
  1877. const generateMap = (list, start, end) => {
  1878. const map = new Map();
  1879. for (let i = start; i <= end; i++) {
  1880. map.set(list[i], i);
  1881. }
  1882. return map;
  1883. };
  1884. // Stores previous ordered list of parts and map of key to index
  1885. const partListCache = new WeakMap();
  1886. const keyListCache = new WeakMap();
  1887. /**
  1888. * A directive that repeats a series of values (usually `TemplateResults`)
  1889. * generated from an iterable, and updates those items efficiently when the
  1890. * iterable changes based on user-provided `keys` associated with each item.
  1891. *
  1892. * Note that if a `keyFn` is provided, strict key-to-DOM mapping is maintained,
  1893. * meaning previous DOM for a given key is moved into the new position if
  1894. * needed, and DOM will never be reused with values for different keys (new DOM
  1895. * will always be created for new keys). This is generally the most efficient
  1896. * way to use `repeat` since it performs minimum unnecessary work for insertions
  1897. * and removals.
  1898. *
  1899. * IMPORTANT: If providing a `keyFn`, keys *must* be unique for all items in a
  1900. * given call to `repeat`. The behavior when two or more items have the same key
  1901. * is undefined.
  1902. *
  1903. * If no `keyFn` is provided, this directive will perform similar to mapping
  1904. * items to values, and DOM will be reused against potentially different items.
  1905. */
  1906. const repeat = directive((items, keyFnOrTemplate, template) => {
  1907. let keyFn;
  1908. if (template === undefined) {
  1909. template = keyFnOrTemplate;
  1910. }
  1911. else if (keyFnOrTemplate !== undefined) {
  1912. keyFn = keyFnOrTemplate;
  1913. }
  1914. return (containerPart) => {
  1915. if (!(containerPart instanceof NodePart)) {
  1916. throw new Error('repeat can only be used in text bindings');
  1917. }
  1918. // Old part & key lists are retrieved from the last update
  1919. // (associated with the part for this instance of the directive)
  1920. const oldParts = partListCache.get(containerPart) || [];
  1921. const oldKeys = keyListCache.get(containerPart) || [];
  1922. // New part list will be built up as we go (either reused from
  1923. // old parts or created for new keys in this update). This is
  1924. // saved in the above cache at the end of the update.
  1925. const newParts = [];
  1926. // New value list is eagerly generated from items along with a
  1927. // parallel array indicating its key.
  1928. const newValues = [];
  1929. const newKeys = [];
  1930. let index = 0;
  1931. for (const item of items) {
  1932. newKeys[index] = keyFn ? keyFn(item, index) : index;
  1933. newValues[index] = template(item, index);
  1934. index++;
  1935. }
  1936. // Maps from key to index for current and previous update; these
  1937. // are generated lazily only when needed as a performance
  1938. // optimization, since they are only required for multiple
  1939. // non-contiguous changes in the list, which are less common.
  1940. let newKeyToIndexMap;
  1941. let oldKeyToIndexMap;
  1942. // Head and tail pointers to old parts and new values
  1943. let oldHead = 0;
  1944. let oldTail = oldParts.length - 1;
  1945. let newHead = 0;
  1946. let newTail = newValues.length - 1;
  1947. // Overview of O(n) reconciliation algorithm (general approach
  1948. // based on ideas found in ivi, vue, snabbdom, etc.):
  1949. //
  1950. // * We start with the list of old parts and new values (and
  1951. // arrays of their respective keys), head/tail pointers into
  1952. // each, and we build up the new list of parts by updating
  1953. // (and when needed, moving) old parts or creating new ones.
  1954. // The initial scenario might look like this (for brevity of
  1955. // the diagrams, the numbers in the array reflect keys
  1956. // associated with the old parts or new values, although keys
  1957. // and parts/values are actually stored in parallel arrays
  1958. // indexed using the same head/tail pointers):
  1959. //
  1960. // oldHead v v oldTail
  1961. // oldKeys: [0, 1, 2, 3, 4, 5, 6]
  1962. // newParts: [ , , , , , , ]
  1963. // newKeys: [0, 2, 1, 4, 3, 7, 6] <- reflects the user's new
  1964. // item order
  1965. // newHead ^ ^ newTail
  1966. //
  1967. // * Iterate old & new lists from both sides, updating,
  1968. // swapping, or removing parts at the head/tail locations
  1969. // until neither head nor tail can move.
  1970. //
  1971. // * Example below: keys at head pointers match, so update old
  1972. // part 0 in-place (no need to move it) and record part 0 in
  1973. // the `newParts` list. The last thing we do is advance the
  1974. // `oldHead` and `newHead` pointers (will be reflected in the
  1975. // next diagram).
  1976. //
  1977. // oldHead v v oldTail
  1978. // oldKeys: [0, 1, 2, 3, 4, 5, 6]
  1979. // newParts: [0, , , , , , ] <- heads matched: update 0
  1980. // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldHead
  1981. // & newHead
  1982. // newHead ^ ^ newTail
  1983. //
  1984. // * Example below: head pointers don't match, but tail
  1985. // pointers do, so update part 6 in place (no need to move
  1986. // it), and record part 6 in the `newParts` list. Last,
  1987. // advance the `oldTail` and `oldHead` pointers.
  1988. //
  1989. // oldHead v v oldTail
  1990. // oldKeys: [0, 1, 2, 3, 4, 5, 6]
  1991. // newParts: [0, , , , , , 6] <- tails matched: update 6
  1992. // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldTail
  1993. // & newTail
  1994. // newHead ^ ^ newTail
  1995. //
  1996. // * If neither head nor tail match; next check if one of the
  1997. // old head/tail items was removed. We first need to generate
  1998. // the reverse map of new keys to index (`newKeyToIndexMap`),
  1999. // which is done once lazily as a performance optimization,
  2000. // since we only hit this case if multiple non-contiguous
  2001. // changes were made. Note that for contiguous removal
  2002. // anywhere in the list, the head and tails would advance
  2003. // from either end and pass each other before we get to this
  2004. // case and removals would be handled in the final while loop
  2005. // without needing to generate the map.
  2006. //
  2007. // * Example below: The key at `oldTail` was removed (no longer
  2008. // in the `newKeyToIndexMap`), so remove that part from the
  2009. // DOM and advance just the `oldTail` pointer.
  2010. //
  2011. // oldHead v v oldTail
  2012. // oldKeys: [0, 1, 2, 3, 4, 5, 6]
  2013. // newParts: [0, , , , , , 6] <- 5 not in new map: remove
  2014. // newKeys: [0, 2, 1, 4, 3, 7, 6] 5 and advance oldTail
  2015. // newHead ^ ^ newTail
  2016. //
  2017. // * Once head and tail cannot move, any mismatches are due to
  2018. // either new or moved items; if a new key is in the previous
  2019. // "old key to old index" map, move the old part to the new
  2020. // location, otherwise create and insert a new part. Note
  2021. // that when moving an old part we null its position in the
  2022. // oldParts array if it lies between the head and tail so we
  2023. // know to skip it when the pointers get there.
  2024. //
  2025. // * Example below: neither head nor tail match, and neither
  2026. // were removed; so find the `newHead` key in the
  2027. // `oldKeyToIndexMap`, and move that old part's DOM into the
  2028. // next head position (before `oldParts[oldHead]`). Last,
  2029. // null the part in the `oldPart` array since it was
  2030. // somewhere in the remaining oldParts still to be scanned
  2031. // (between the head and tail pointers) so that we know to
  2032. // skip that old part on future iterations.
  2033. //
  2034. // oldHead v v oldTail
  2035. // oldKeys: [0, 1, -, 3, 4, 5, 6]
  2036. // newParts: [0, 2, , , , , 6] <- stuck: update & move 2
  2037. // newKeys: [0, 2, 1, 4, 3, 7, 6] into place and advance
  2038. // newHead
  2039. // newHead ^ ^ newTail
  2040. //
  2041. // * Note that for moves/insertions like the one above, a part
  2042. // inserted at the head pointer is inserted before the
  2043. // current `oldParts[oldHead]`, and a part inserted at the
  2044. // tail pointer is inserted before `newParts[newTail+1]`. The
  2045. // seeming asymmetry lies in the fact that new parts are
  2046. // moved into place outside in, so to the right of the head
  2047. // pointer are old parts, and to the right of the tail
  2048. // pointer are new parts.
  2049. //
  2050. // * We always restart back from the top of the algorithm,
  2051. // allowing matching and simple updates in place to
  2052. // continue...
  2053. //
  2054. // * Example below: the head pointers once again match, so
  2055. // simply update part 1 and record it in the `newParts`
  2056. // array. Last, advance both head pointers.
  2057. //
  2058. // oldHead v v oldTail
  2059. // oldKeys: [0, 1, -, 3, 4, 5, 6]
  2060. // newParts: [0, 2, 1, , , , 6] <- heads matched: update 1
  2061. // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldHead
  2062. // & newHead
  2063. // newHead ^ ^ newTail
  2064. //
  2065. // * As mentioned above, items that were moved as a result of
  2066. // being stuck (the final else clause in the code below) are
  2067. // marked with null, so we always advance old pointers over
  2068. // these so we're comparing the next actual old value on
  2069. // either end.
  2070. //
  2071. // * Example below: `oldHead` is null (already placed in
  2072. // newParts), so advance `oldHead`.
  2073. //
  2074. // oldHead v v oldTail
  2075. // oldKeys: [0, 1, -, 3, 4, 5, 6] <- old head already used:
  2076. // newParts: [0, 2, 1, , , , 6] advance oldHead
  2077. // newKeys: [0, 2, 1, 4, 3, 7, 6]
  2078. // newHead ^ ^ newTail
  2079. //
  2080. // * Note it's not critical to mark old parts as null when they
  2081. // are moved from head to tail or tail to head, since they
  2082. // will be outside the pointer range and never visited again.
  2083. //
  2084. // * Example below: Here the old tail key matches the new head
  2085. // key, so the part at the `oldTail` position and move its
  2086. // DOM to the new head position (before `oldParts[oldHead]`).
  2087. // Last, advance `oldTail` and `newHead` pointers.
  2088. //
  2089. // oldHead v v oldTail
  2090. // oldKeys: [0, 1, -, 3, 4, 5, 6]
  2091. // newParts: [0, 2, 1, 4, , , 6] <- old tail matches new
  2092. // newKeys: [0, 2, 1, 4, 3, 7, 6] head: update & move 4,
  2093. // advance oldTail & newHead
  2094. // newHead ^ ^ newTail
  2095. //
  2096. // * Example below: Old and new head keys match, so update the
  2097. // old head part in place, and advance the `oldHead` and
  2098. // `newHead` pointers.
  2099. //
  2100. // oldHead v oldTail
  2101. // oldKeys: [0, 1, -, 3, 4, 5, 6]
  2102. // newParts: [0, 2, 1, 4, 3, ,6] <- heads match: update 3
  2103. // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance oldHead &
  2104. // newHead
  2105. // newHead ^ ^ newTail
  2106. //
  2107. // * Once the new or old pointers move past each other then all
  2108. // we have left is additions (if old list exhausted) or
  2109. // removals (if new list exhausted). Those are handled in the
  2110. // final while loops at the end.
  2111. //
  2112. // * Example below: `oldHead` exceeded `oldTail`, so we're done
  2113. // with the main loop. Create the remaining part and insert
  2114. // it at the new head position, and the update is complete.
  2115. //
  2116. // (oldHead > oldTail)
  2117. // oldKeys: [0, 1, -, 3, 4, 5, 6]
  2118. // newParts: [0, 2, 1, 4, 3, 7 ,6] <- create and insert 7
  2119. // newKeys: [0, 2, 1, 4, 3, 7, 6]
  2120. // newHead ^ newTail
  2121. //
  2122. // * Note that the order of the if/else clauses is not
  2123. // important to the algorithm, as long as the null checks
  2124. // come first (to ensure we're always working on valid old
  2125. // parts) and that the final else clause comes last (since
  2126. // that's where the expensive moves occur). The order of
  2127. // remaining clauses is is just a simple guess at which cases
  2128. // will be most common.
  2129. //
  2130. // * TODO(kschaaf) Note, we could calculate the longest
  2131. // increasing subsequence (LIS) of old items in new position,
  2132. // and only move those not in the LIS set. However that costs
  2133. // O(nlogn) time and adds a bit more code, and only helps
  2134. // make rare types of mutations require fewer moves. The
  2135. // above handles removes, adds, reversal, swaps, and single
  2136. // moves of contiguous items in linear time, in the minimum
  2137. // number of moves. As the number of multiple moves where LIS
  2138. // might help approaches a random shuffle, the LIS
  2139. // optimization becomes less helpful, so it seems not worth
  2140. // the code at this point. Could reconsider if a compelling
  2141. // case arises.
  2142. while (oldHead <= oldTail && newHead <= newTail) {
  2143. if (oldParts[oldHead] === null) {
  2144. // `null` means old part at head has already been used
  2145. // below; skip
  2146. oldHead++;
  2147. }
  2148. else if (oldParts[oldTail] === null) {
  2149. // `null` means old part at tail has already been used
  2150. // below; skip
  2151. oldTail--;
  2152. }
  2153. else if (oldKeys[oldHead] === newKeys[newHead]) {
  2154. // Old head matches new head; update in place
  2155. newParts[newHead] =
  2156. updatePart(oldParts[oldHead], newValues[newHead]);
  2157. oldHead++;
  2158. newHead++;
  2159. }
  2160. else if (oldKeys[oldTail] === newKeys[newTail]) {
  2161. // Old tail matches new tail; update in place
  2162. newParts[newTail] =
  2163. updatePart(oldParts[oldTail], newValues[newTail]);
  2164. oldTail--;
  2165. newTail--;
  2166. }
  2167. else if (oldKeys[oldHead] === newKeys[newTail]) {
  2168. // Old head matches new tail; update and move to new tail
  2169. newParts[newTail] =
  2170. updatePart(oldParts[oldHead], newValues[newTail]);
  2171. insertPartBefore(containerPart, oldParts[oldHead], newParts[newTail + 1]);
  2172. oldHead++;
  2173. newTail--;
  2174. }
  2175. else if (oldKeys[oldTail] === newKeys[newHead]) {
  2176. // Old tail matches new head; update and move to new head
  2177. newParts[newHead] =
  2178. updatePart(oldParts[oldTail], newValues[newHead]);
  2179. insertPartBefore(containerPart, oldParts[oldTail], oldParts[oldHead]);
  2180. oldTail--;
  2181. newHead++;
  2182. }
  2183. else {
  2184. if (newKeyToIndexMap === undefined) {
  2185. // Lazily generate key-to-index maps, used for removals &
  2186. // moves below
  2187. newKeyToIndexMap = generateMap(newKeys, newHead, newTail);
  2188. oldKeyToIndexMap = generateMap(oldKeys, oldHead, oldTail);
  2189. }
  2190. if (!newKeyToIndexMap.has(oldKeys[oldHead])) {
  2191. // Old head is no longer in new list; remove
  2192. removePart(oldParts[oldHead]);
  2193. oldHead++;
  2194. }
  2195. else if (!newKeyToIndexMap.has(oldKeys[oldTail])) {
  2196. // Old tail is no longer in new list; remove
  2197. removePart(oldParts[oldTail]);
  2198. oldTail--;
  2199. }
  2200. else {
  2201. // Any mismatches at this point are due to additions or
  2202. // moves; see if we have an old part we can reuse and move
  2203. // into place
  2204. const oldIndex = oldKeyToIndexMap.get(newKeys[newHead]);
  2205. const oldPart = oldIndex !== undefined ? oldParts[oldIndex] : null;
  2206. if (oldPart === null) {
  2207. // No old part for this value; create a new one and
  2208. // insert it
  2209. const newPart = createAndInsertPart(containerPart, oldParts[oldHead]);
  2210. updatePart(newPart, newValues[newHead]);
  2211. newParts[newHead] = newPart;
  2212. }
  2213. else {
  2214. // Reuse old part
  2215. newParts[newHead] =
  2216. updatePart(oldPart, newValues[newHead]);
  2217. insertPartBefore(containerPart, oldPart, oldParts[oldHead]);
  2218. // This marks the old part as having been used, so that
  2219. // it will be skipped in the first two checks above
  2220. oldParts[oldIndex] = null;
  2221. }
  2222. newHead++;
  2223. }
  2224. }
  2225. }
  2226. // Add parts for any remaining new values
  2227. while (newHead <= newTail) {
  2228. // For all remaining additions, we insert before last new
  2229. // tail, since old pointers are no longer valid
  2230. const newPart = createAndInsertPart(containerPart, newParts[newTail + 1]);
  2231. updatePart(newPart, newValues[newHead]);
  2232. newParts[newHead++] = newPart;
  2233. }
  2234. // Remove any remaining unused old parts
  2235. while (oldHead <= oldTail) {
  2236. const oldPart = oldParts[oldHead++];
  2237. if (oldPart !== null) {
  2238. removePart(oldPart);
  2239. }
  2240. }
  2241. // Save order of new parts for next round
  2242. partListCache.set(containerPart, newParts);
  2243. keyListCache.set(containerPart, newKeys);
  2244. };
  2245. });
  2246. /**
  2247. * @license
  2248. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  2249. * This code may only be used under the BSD style license found at
  2250. * http://polymer.github.io/LICENSE.txt
  2251. * The complete set of authors may be found at
  2252. * http://polymer.github.io/AUTHORS.txt
  2253. * The complete set of contributors may be found at
  2254. * http://polymer.github.io/CONTRIBUTORS.txt
  2255. * Code distributed by Google as part of the polymer project is also
  2256. * subject to an additional IP rights grant found at
  2257. * http://polymer.github.io/PATENTS.txt
  2258. */
  2259. // For each part, remember the value that was last rendered to the part by the
  2260. // unsafeHTML directive, and the DocumentFragment that was last set as a value.
  2261. // The DocumentFragment is used as a unique key to check if the last value
  2262. // rendered to the part was with unsafeHTML. If not, we'll always re-render the
  2263. // value passed to unsafeHTML.
  2264. const previousValues$1 = new WeakMap();
  2265. /**
  2266. * Used to clone existing node instead of each time creating new one which is
  2267. * slower
  2268. */
  2269. const emptyTemplateNode$1 = document.createElement('template');
  2270. /**
  2271. * Renders the result as HTML, rather than text.
  2272. *
  2273. * Note, this is unsafe to use with any user-provided input that hasn't been
  2274. * sanitized or escaped, as it may lead to cross-site-scripting
  2275. * vulnerabilities.
  2276. */
  2277. const unsafeHTML = directive((value) => (part) => {
  2278. if (!(part instanceof NodePart)) {
  2279. throw new Error('unsafeHTML can only be used in text bindings');
  2280. }
  2281. const previousValue = previousValues$1.get(part);
  2282. if (previousValue !== undefined && isPrimitive(value) &&
  2283. value === previousValue.value && part.value === previousValue.fragment) {
  2284. return;
  2285. }
  2286. const template = emptyTemplateNode$1.cloneNode();
  2287. template.innerHTML = value; // innerHTML casts to string internally
  2288. const fragment = document.importNode(template.content, true);
  2289. part.setValue(fragment);
  2290. previousValues$1.set(part, { value, fragment });
  2291. });
  2292. /**
  2293. * @license
  2294. * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  2295. * This code may only be used under the BSD style license found at
  2296. * http://polymer.github.io/LICENSE.txt
  2297. * The complete set of authors may be found at
  2298. * http://polymer.github.io/AUTHORS.txt
  2299. * The complete set of contributors may be found at
  2300. * http://polymer.github.io/CONTRIBUTORS.txt
  2301. * Code distributed by Google as part of the polymer project is also
  2302. * subject to an additional IP rights grant found at
  2303. * http://polymer.github.io/PATENTS.txt
  2304. */
  2305. const _state = new WeakMap();
  2306. // Effectively infinity, but a SMI.
  2307. const _infinity = 0x7fffffff;
  2308. /**
  2309. * Renders one of a series of values, including Promises, to a Part.
  2310. *
  2311. * Values are rendered in priority order, with the first argument having the
  2312. * highest priority and the last argument having the lowest priority. If a
  2313. * value is a Promise, low-priority values will be rendered until it resolves.
  2314. *
  2315. * The priority of values can be used to create placeholder content for async
  2316. * data. For example, a Promise with pending content can be the first,
  2317. * highest-priority, argument, and a non_promise loading indicator template can
  2318. * be used as the second, lower-priority, argument. The loading indicator will
  2319. * render immediately, and the primary content will render when the Promise
  2320. * resolves.
  2321. *
  2322. * Example:
  2323. *
  2324. * const content = fetch('./content.txt').then(r => r.text());
  2325. * html`${until(content, html`<span>Loading...</span>`)}`
  2326. */
  2327. const until = directive((...args) => (part) => {
  2328. let state = _state.get(part);
  2329. if (state === undefined) {
  2330. state = {
  2331. lastRenderedIndex: _infinity,
  2332. values: [],
  2333. };
  2334. _state.set(part, state);
  2335. }
  2336. const previousValues = state.values;
  2337. let previousLength = previousValues.length;
  2338. state.values = args;
  2339. for (let i = 0; i < args.length; i++) {
  2340. // If we've rendered a higher-priority value already, stop.
  2341. if (i > state.lastRenderedIndex) {
  2342. break;
  2343. }
  2344. const value = args[i];
  2345. // Render non-Promise values immediately
  2346. if (isPrimitive(value) ||
  2347. typeof value.then !== 'function') {
  2348. part.setValue(value);
  2349. state.lastRenderedIndex = i;
  2350. // Since a lower-priority value will never overwrite a higher-priority
  2351. // synchronous value, we can stop processing now.
  2352. break;
  2353. }
  2354. // If this is a Promise we've already handled, skip it.
  2355. if (i < previousLength && value === previousValues[i]) {
  2356. continue;
  2357. }
  2358. // We have a Promise that we haven't seen before, so priorities may have
  2359. // changed. Forget what we rendered before.
  2360. state.lastRenderedIndex = _infinity;
  2361. previousLength = 0;
  2362. Promise.resolve(value).then((resolvedValue) => {
  2363. const index = state.values.indexOf(value);
  2364. // If state.values doesn't contain the value, we've re-rendered without
  2365. // the value, so don't render it. Then, only render if the value is
  2366. // higher-priority than what's already been rendered.
  2367. if (index > -1 && index < state.lastRenderedIndex) {
  2368. state.lastRenderedIndex = index;
  2369. part.setValue(resolvedValue);
  2370. part.commit();
  2371. }
  2372. });
  2373. }
  2374. });
  2375. const detached = new WeakMap();
  2376. class Detach extends Directive {
  2377. constructor(ifFn) {
  2378. super();
  2379. this.ifFn = ifFn;
  2380. }
  2381. body(part) {
  2382. const detach = this.ifFn();
  2383. const element = part.committer.element;
  2384. if (detach) {
  2385. if (!detached.has(part)) {
  2386. const nextSibling = element.nextSibling;
  2387. detached.set(part, { element, nextSibling });
  2388. }
  2389. element.remove();
  2390. }
  2391. else {
  2392. const data = detached.get(part);
  2393. if (typeof data !== 'undefined' && data !== null) {
  2394. data.nextSibling.parentNode.insertBefore(data.element, data.nextSibling);
  2395. detached.delete(part);
  2396. }
  2397. }
  2398. }
  2399. }
  2400. const toRemove = [], toUpdate = [];
  2401. class StyleMap extends Directive {
  2402. constructor(styleInfo, detach = false) {
  2403. super();
  2404. this.previous = {};
  2405. this.style = styleInfo;
  2406. this.detach = detach;
  2407. }
  2408. setStyle(styleInfo) {
  2409. this.style = styleInfo;
  2410. }
  2411. setDetach(detach) {
  2412. this.detach = detach;
  2413. }
  2414. body(part) {
  2415. toRemove.length = 0;
  2416. toUpdate.length = 0;
  2417. // @ts-ignore
  2418. const element = part.committer.element;
  2419. const style = element.style;
  2420. let previous = this.previous;
  2421. for (const name in previous) {
  2422. if (this.style[name] === undefined) {
  2423. toRemove.push(name);
  2424. }
  2425. }
  2426. for (const name in this.style) {
  2427. const value = this.style[name];
  2428. const prev = previous[name];
  2429. if (prev !== undefined && prev === value) {
  2430. continue;
  2431. }
  2432. toUpdate.push(name);
  2433. }
  2434. if (toRemove.length || toUpdate.length) {
  2435. let parent, nextSibling;
  2436. if (this.detach) {
  2437. parent = element.parentNode;
  2438. if (parent) {
  2439. nextSibling = element.nextSibling;
  2440. element.remove();
  2441. }
  2442. }
  2443. for (const name of toRemove) {
  2444. style.removeProperty(name);
  2445. }
  2446. for (const name of toUpdate) {
  2447. const value = this.style[name];
  2448. if (!name.includes('-')) {
  2449. style[name] = value;
  2450. }
  2451. else {
  2452. style.setProperty(name, value);
  2453. }
  2454. }
  2455. if (this.detach && parent) {
  2456. parent.insertBefore(element, nextSibling);
  2457. }
  2458. this.previous = Object.assign({}, this.style);
  2459. }
  2460. }
  2461. }
  2462. class Action {
  2463. constructor() {
  2464. this.isAction = true;
  2465. }
  2466. }
  2467. Action.prototype.isAction = true;
  2468. const defaultOptions = {
  2469. element: document.createTextNode(''),
  2470. axis: 'xy',
  2471. threshold: 10,
  2472. onDown(data) { },
  2473. onMove(data) { },
  2474. onUp(data) { },
  2475. onWheel(data) { }
  2476. };
  2477. const pointerEventsExists = typeof PointerEvent !== 'undefined';
  2478. let id = 0;
  2479. class PointerAction extends Action {
  2480. constructor(element, data) {
  2481. super();
  2482. this.moving = '';
  2483. this.initialX = 0;
  2484. this.initialY = 0;
  2485. this.lastY = 0;
  2486. this.lastX = 0;
  2487. this.onPointerDown = this.onPointerDown.bind(this);
  2488. this.onPointerMove = this.onPointerMove.bind(this);
  2489. this.onPointerUp = this.onPointerUp.bind(this);
  2490. this.onWheel = this.onWheel.bind(this);
  2491. this.element = element;
  2492. this.id = ++id;
  2493. this.options = Object.assign(Object.assign({}, defaultOptions), data.pointerOptions);
  2494. if (pointerEventsExists) {
  2495. element.addEventListener('pointerdown', this.onPointerDown);
  2496. document.addEventListener('pointermove', this.onPointerMove);
  2497. document.addEventListener('pointerup', this.onPointerUp);
  2498. }
  2499. else {
  2500. element.addEventListener('touchstart', this.onPointerDown);
  2501. document.addEventListener('touchmove', this.onPointerMove, { passive: false });
  2502. document.addEventListener('touchend', this.onPointerUp);
  2503. document.addEventListener('touchcancel', this.onPointerUp);
  2504. element.addEventListener('mousedown', this.onPointerDown);
  2505. document.addEventListener('mousemove', this.onPointerMove, { passive: false });
  2506. document.addEventListener('mouseup', this.onPointerUp);
  2507. }
  2508. }
  2509. normalizeMouseWheelEvent(event) {
  2510. // @ts-ignore
  2511. let x = event.deltaX || 0;
  2512. // @ts-ignore
  2513. let y = event.deltaY || 0;
  2514. // @ts-ignore
  2515. let z = event.deltaZ || 0;
  2516. // @ts-ignore
  2517. const mode = event.deltaMode;
  2518. // @ts-ignore
  2519. const lineHeight = parseInt(getComputedStyle(event.target).getPropertyValue('line-height'));
  2520. let scale = 1;
  2521. switch (mode) {
  2522. case 1:
  2523. scale = lineHeight;
  2524. break;
  2525. case 2:
  2526. // @ts-ignore
  2527. scale = window.height;
  2528. break;
  2529. }
  2530. x *= scale;
  2531. y *= scale;
  2532. z *= scale;
  2533. return { x, y, z, event };
  2534. }
  2535. onWheel(event) {
  2536. const normalized = this.normalizeMouseWheelEvent(event);
  2537. this.options.onWheel(normalized);
  2538. }
  2539. normalizePointerEvent(event) {
  2540. let result = { x: 0, y: 0, pageX: 0, pageY: 0, clientX: 0, clientY: 0, screenX: 0, screenY: 0, event };
  2541. switch (event.type) {
  2542. case 'wheel':
  2543. const wheel = this.normalizeMouseWheelEvent(event);
  2544. result.x = wheel.x;
  2545. result.y = wheel.y;
  2546. result.pageX = result.x;
  2547. result.pageY = result.y;
  2548. result.screenX = result.x;
  2549. result.screenY = result.y;
  2550. result.clientX = result.x;
  2551. result.clientY = result.y;
  2552. break;
  2553. case 'touchstart':
  2554. case 'touchmove':
  2555. case 'touchend':
  2556. case 'touchcancel':
  2557. result.x = event.changedTouches[0].screenX;
  2558. result.y = event.changedTouches[0].screenY;
  2559. result.pageX = event.changedTouches[0].pageX;
  2560. result.pageY = event.changedTouches[0].pageY;
  2561. result.screenX = event.changedTouches[0].screenX;
  2562. result.screenY = event.changedTouches[0].screenY;
  2563. result.clientX = event.changedTouches[0].clientX;
  2564. result.clientY = event.changedTouches[0].clientY;
  2565. break;
  2566. default:
  2567. result.x = event.x;
  2568. result.y = event.y;
  2569. result.pageX = event.pageX;
  2570. result.pageY = event.pageY;
  2571. result.screenX = event.screenX;
  2572. result.screenY = event.screenY;
  2573. result.clientX = event.clientX;
  2574. result.clientY = event.clientY;
  2575. break;
  2576. }
  2577. return result;
  2578. }
  2579. onPointerDown(event) {
  2580. if (event.type === 'mousedown' && event.button !== 0)
  2581. return;
  2582. this.moving = 'xy';
  2583. const normalized = this.normalizePointerEvent(event);
  2584. this.lastX = normalized.x;
  2585. this.lastY = normalized.y;
  2586. this.initialX = normalized.x;
  2587. this.initialY = normalized.y;
  2588. this.options.onDown(normalized);
  2589. }
  2590. handleX(normalized) {
  2591. let movementX = normalized.x - this.lastX;
  2592. this.lastY = normalized.y;
  2593. this.lastX = normalized.x;
  2594. return movementX;
  2595. }
  2596. handleY(normalized) {
  2597. let movementY = normalized.y - this.lastY;
  2598. this.lastY = normalized.y;
  2599. this.lastX = normalized.x;
  2600. return movementY;
  2601. }
  2602. onPointerMove(event) {
  2603. if (this.moving === '' || (event.type === 'mousemove' && event.button !== 0))
  2604. return;
  2605. const normalized = this.normalizePointerEvent(event);
  2606. if (this.options.axis === 'x|y') {
  2607. let movementX = 0, movementY = 0;
  2608. if (this.moving === 'x' ||
  2609. (this.moving === 'xy' && Math.abs(normalized.x - this.initialX) > this.options.threshold)) {
  2610. this.moving = 'x';
  2611. movementX = this.handleX(normalized);
  2612. }
  2613. if (this.moving === 'y' ||
  2614. (this.moving === 'xy' && Math.abs(normalized.y - this.initialY) > this.options.threshold)) {
  2615. this.moving = 'y';
  2616. movementY = this.handleY(normalized);
  2617. }
  2618. this.options.onMove({
  2619. movementX,
  2620. movementY,
  2621. x: normalized.x,
  2622. y: normalized.y,
  2623. initialX: this.initialX,
  2624. initialY: this.initialY,
  2625. lastX: this.lastX,
  2626. lastY: this.lastY,
  2627. event
  2628. });
  2629. }
  2630. else if (this.options.axis === 'xy') {
  2631. let movementX = 0, movementY = 0;
  2632. if (Math.abs(normalized.x - this.initialX) > this.options.threshold) {
  2633. movementX = this.handleX(normalized);
  2634. }
  2635. if (Math.abs(normalized.y - this.initialY) > this.options.threshold) {
  2636. movementY = this.handleY(normalized);
  2637. }
  2638. this.options.onMove({
  2639. movementX,
  2640. movementY,
  2641. x: normalized.x,
  2642. y: normalized.y,
  2643. initialX: this.initialX,
  2644. initialY: this.initialY,
  2645. lastX: this.lastX,
  2646. lastY: this.lastY,
  2647. event
  2648. });
  2649. }
  2650. else if (this.options.axis === 'x') {
  2651. if (this.moving === 'x' ||
  2652. (this.moving === 'xy' && Math.abs(normalized.x - this.initialX) > this.options.threshold)) {
  2653. this.moving = 'x';
  2654. this.options.onMove({
  2655. movementX: this.handleX(normalized),
  2656. movementY: 0,
  2657. initialX: this.initialX,
  2658. initialY: this.initialY,
  2659. lastX: this.lastX,
  2660. lastY: this.lastY,
  2661. event
  2662. });
  2663. }
  2664. }
  2665. else if (this.options.axis === 'y') {
  2666. let movementY = 0;
  2667. if (this.moving === 'y' ||
  2668. (this.moving === 'xy' && Math.abs(normalized.y - this.initialY) > this.options.threshold)) {
  2669. this.moving = 'y';
  2670. movementY = this.handleY(normalized);
  2671. }
  2672. this.options.onMove({
  2673. movementX: 0,
  2674. movementY,
  2675. x: normalized.x,
  2676. y: normalized.y,
  2677. initialX: this.initialX,
  2678. initialY: this.initialY,
  2679. lastX: this.lastX,
  2680. lastY: this.lastY,
  2681. event
  2682. });
  2683. }
  2684. }
  2685. onPointerUp(event) {
  2686. this.moving = '';
  2687. const normalized = this.normalizePointerEvent(event);
  2688. this.options.onUp({
  2689. movementX: 0,
  2690. movementY: 0,
  2691. x: normalized.x,
  2692. y: normalized.y,
  2693. initialX: this.initialX,
  2694. initialY: this.initialY,
  2695. lastX: this.lastX,
  2696. lastY: this.lastY,
  2697. event
  2698. });
  2699. this.lastY = 0;
  2700. this.lastX = 0;
  2701. }
  2702. destroy(element) {
  2703. if (pointerEventsExists) {
  2704. element.removeEventListener('pointerdown', this.onPointerDown);
  2705. document.removeEventListener('pointermove', this.onPointerMove);
  2706. document.removeEventListener('pointerup', this.onPointerUp);
  2707. }
  2708. else {
  2709. element.removeEventListener('mousedown', this.onPointerDown);
  2710. document.removeEventListener('mousemove', this.onPointerMove);
  2711. document.removeEventListener('mouseup', this.onPointerUp);
  2712. element.removeEventListener('touchstart', this.onPointerDown);
  2713. document.removeEventListener('touchmove', this.onPointerMove);
  2714. document.removeEventListener('touchend', this.onPointerUp);
  2715. document.removeEventListener('touchcancel', this.onPointerUp);
  2716. }
  2717. }
  2718. }
  2719. function getPublicComponentMethods(components, actionsByInstance, clone) {
  2720. return class PublicComponentMethods {
  2721. constructor(instance, vidoInstance, props = {}) {
  2722. this.instance = instance;
  2723. this.name = vidoInstance.name;
  2724. this.vidoInstance = vidoInstance;
  2725. this.props = props;
  2726. this.destroy = this.destroy.bind(this);
  2727. this.update = this.update.bind(this);
  2728. this.change = this.change.bind(this);
  2729. this.html = this.html.bind(this);
  2730. }
  2731. /**
  2732. * Destroy component
  2733. */
  2734. destroy() {
  2735. if (this.vidoInstance.debug) {
  2736. console.groupCollapsed(`destroying component ${this.instance}`);
  2737. console.log(clone({ components: components.keys(), actionsByInstance }));
  2738. console.trace();
  2739. console.groupEnd();
  2740. }
  2741. return this.vidoInstance.destroyComponent(this.instance, this.vidoInstance);
  2742. }
  2743. /**
  2744. * Update template - trigger rendering process
  2745. */
  2746. update() {
  2747. if (this.vidoInstance.debug) {
  2748. console.groupCollapsed(`updating component ${this.instance}`);
  2749. console.log(clone({ components: components.keys(), actionsByInstance }));
  2750. console.trace();
  2751. console.groupEnd();
  2752. }
  2753. return this.vidoInstance.updateTemplate(this.vidoInstance);
  2754. }
  2755. /**
  2756. * Change component input properties
  2757. * @param {any} newProps
  2758. */
  2759. change(newProps, options) {
  2760. if (this.vidoInstance.debug) {
  2761. console.groupCollapsed(`changing component ${this.instance}`);
  2762. console.log(clone({ props: this.props, newProps: newProps, components: components.keys(), actionsByInstance }));
  2763. console.trace();
  2764. console.groupEnd();
  2765. }
  2766. components.get(this.instance).change(newProps, options);
  2767. }
  2768. /**
  2769. * Get component lit-html template
  2770. * @param {} templateProps
  2771. */
  2772. html(templateProps = {}) {
  2773. const component = components.get(this.instance);
  2774. if (component) {
  2775. return component.update(templateProps, this.vidoInstance);
  2776. }
  2777. return undefined;
  2778. }
  2779. _getComponents() {
  2780. return components;
  2781. }
  2782. _getActions() {
  2783. return actionsByInstance;
  2784. }
  2785. };
  2786. }
  2787. function getActionsCollector(actionsByInstance) {
  2788. return class ActionsCollector extends Directive {
  2789. constructor(instance) {
  2790. super();
  2791. this.instance = instance;
  2792. }
  2793. set(actions, props) {
  2794. this.actions = actions;
  2795. this.props = props;
  2796. // props must be mutable! (do not do this -> {...props})
  2797. // because we will modify action props with onChange and can reuse existin instance
  2798. return this;
  2799. }
  2800. body(part) {
  2801. const element = part.committer.element;
  2802. for (const create of this.actions) {
  2803. if (typeof create !== 'undefined') {
  2804. let exists;
  2805. if (actionsByInstance.has(this.instance)) {
  2806. for (const action of actionsByInstance.get(this.instance)) {
  2807. if (action.componentAction.create === create && action.element === element) {
  2808. exists = action;
  2809. break;
  2810. }
  2811. }
  2812. }
  2813. if (!exists) {
  2814. // @ts-ignore
  2815. if (typeof element.vido !== 'undefined')
  2816. delete element.vido;
  2817. const componentAction = {
  2818. create,
  2819. update() { },
  2820. destroy() { }
  2821. };
  2822. const action = { instance: this.instance, componentAction, element, props: this.props };
  2823. let byInstance = [];
  2824. if (actionsByInstance.has(this.instance)) {
  2825. byInstance = actionsByInstance.get(this.instance);
  2826. }
  2827. byInstance.push(action);
  2828. actionsByInstance.set(this.instance, byInstance);
  2829. }
  2830. else {
  2831. exists.props = this.props;
  2832. }
  2833. }
  2834. }
  2835. }
  2836. };
  2837. }
  2838. function getInternalComponentMethods(components, actionsByInstance, clone) {
  2839. return class InternalComponentMethods {
  2840. constructor(instance, vidoInstance, renderFunction, content) {
  2841. this.instance = instance;
  2842. this.vidoInstance = vidoInstance;
  2843. this.renderFunction = renderFunction;
  2844. this.content = content;
  2845. }
  2846. destroy() {
  2847. var _a;
  2848. if (this.vidoInstance.debug) {
  2849. console.groupCollapsed(`component destroy method fired ${this.instance}`);
  2850. console.log(clone({
  2851. props: this.vidoInstance.props,
  2852. components: components.keys(),
  2853. destroyable: this.vidoInstance.destroyable,
  2854. actionsByInstance
  2855. }));
  2856. console.trace();
  2857. console.groupEnd();
  2858. }
  2859. if (typeof ((_a = this.content) === null || _a === void 0 ? void 0 : _a.destroy) === 'function') {
  2860. this.content.destroy();
  2861. }
  2862. for (const d of this.vidoInstance.destroyable) {
  2863. d();
  2864. }
  2865. this.vidoInstance.onChangeFunctions = [];
  2866. this.vidoInstance.destroyable = [];
  2867. this.vidoInstance.update();
  2868. }
  2869. update(props = {}) {
  2870. if (this.vidoInstance.debug) {
  2871. console.groupCollapsed(`component update method fired ${this.instance}`);
  2872. console.log(clone({ components: components.keys(), actionsByInstance }));
  2873. console.trace();
  2874. console.groupEnd();
  2875. }
  2876. return this.renderFunction(props);
  2877. }
  2878. change(changedProps, options = { leave: false }) {
  2879. const props = changedProps;
  2880. if (this.vidoInstance.debug) {
  2881. console.groupCollapsed(`component change method fired ${this.instance}`);
  2882. console.log(clone({
  2883. props,
  2884. components: components.keys(),
  2885. onChangeFunctions: this.vidoInstance.onChangeFunctions,
  2886. changedProps,
  2887. actionsByInstance
  2888. }));
  2889. console.trace();
  2890. console.groupEnd();
  2891. }
  2892. for (const fn of this.vidoInstance.onChangeFunctions) {
  2893. fn(changedProps, options);
  2894. }
  2895. }
  2896. };
  2897. }
  2898. /**
  2899. * Schedule - a throttle function that uses requestAnimationFrame to limit the rate at which a function is called.
  2900. *
  2901. * @param {function} fn
  2902. * @returns {function}
  2903. */
  2904. function schedule(fn) {
  2905. let frameId = 0;
  2906. function wrapperFn(argument) {
  2907. if (frameId) {
  2908. return;
  2909. }
  2910. function executeFrame() {
  2911. frameId = 0;
  2912. fn.apply(undefined, [argument]);
  2913. }
  2914. frameId = requestAnimationFrame(executeFrame);
  2915. }
  2916. return wrapperFn;
  2917. }
  2918. /**
  2919. * Is object - helper function to determine if specified variable is an object
  2920. *
  2921. * @param {any} item
  2922. * @returns {boolean}
  2923. */
  2924. function isObject(item) {
  2925. return item && typeof item === 'object' && !Array.isArray(item);
  2926. }
  2927. /**
  2928. * Merge deep - helper function which will merge objects recursively - creating brand new one - like clone
  2929. *
  2930. * @param {object} target
  2931. * @params {object} sources
  2932. * @returns {object}
  2933. */
  2934. function mergeDeep(target, ...sources) {
  2935. const source = sources.shift();
  2936. if (isObject(target) && isObject(source)) {
  2937. for (const key in source) {
  2938. if (isObject(source[key])) {
  2939. if (typeof target[key] === 'undefined') {
  2940. target[key] = {};
  2941. }
  2942. target[key] = mergeDeep(target[key], source[key]);
  2943. }
  2944. else if (Array.isArray(source[key])) {
  2945. target[key] = [];
  2946. for (let item of source[key]) {
  2947. if (isObject(item)) {
  2948. target[key].push(mergeDeep({}, item));
  2949. continue;
  2950. }
  2951. target[key].push(item);
  2952. }
  2953. }
  2954. else {
  2955. target[key] = source[key];
  2956. }
  2957. }
  2958. }
  2959. if (!sources.length) {
  2960. return target;
  2961. }
  2962. return mergeDeep(target, ...sources);
  2963. }
  2964. /**
  2965. * Clone helper function
  2966. *
  2967. * @param source
  2968. * @returns {object} cloned source
  2969. */
  2970. function clone(source) {
  2971. if (typeof source.actions !== 'undefined') {
  2972. const actns = source.actions.map((action) => {
  2973. const result = Object.assign({}, action);
  2974. const props = Object.assign({}, result.props);
  2975. delete props.state;
  2976. delete props.api;
  2977. delete result.element;
  2978. result.props = props;
  2979. return result;
  2980. });
  2981. source.actions = actns;
  2982. }
  2983. return mergeDeep({}, source);
  2984. }
  2985. /* dev imports
  2986. import { render, html, directive, svg, Part } from '../lit-html';
  2987. import { asyncAppend } from '../lit-html/directives/async-append';
  2988. import { asyncReplace } from '../lit-html/directives/async-replace';
  2989. import { cache } from '../lit-html/directives/cache';
  2990. import { classMap } from '../lit-html/directives/class-map';
  2991. import { guard } from '../lit-html/directives/guard';
  2992. import { ifDefined } from '../lit-html/directives/if-defined';
  2993. import { repeat } from '../lit-html/directives/repeat';
  2994. import { unsafeHTML } from '../lit-html/directives/unsafe-html';
  2995. import { until } from '../lit-html/directives/until';
  2996. import { Directive } from '../lit-html/lib/directive';
  2997. */
  2998. /**
  2999. * Vido library
  3000. *
  3001. * @param {any} state - state management for the view (can be anything)
  3002. * @param {any} api - some api's or other globally available services
  3003. * @returns {object} vido instance
  3004. */
  3005. function Vido(state, api) {
  3006. let componentId = 0;
  3007. const components = new Map();
  3008. let actionsByInstance = new Map();
  3009. let app, element;
  3010. let shouldUpdateCount = 0;
  3011. const resolved = Promise.resolve();
  3012. const additionalMethods = {};
  3013. const ActionsCollector = getActionsCollector(actionsByInstance);
  3014. class InstanceActionsCollector {
  3015. constructor(instance) {
  3016. this.instance = instance;
  3017. }
  3018. create(actions, props) {
  3019. const actionsInstance = new ActionsCollector(this.instance);
  3020. actionsInstance.set(actions, props);
  3021. return actionsInstance;
  3022. }
  3023. }
  3024. const PublicComponentMethods = getPublicComponentMethods(components, actionsByInstance, clone);
  3025. const InternalComponentMethods = getInternalComponentMethods(components, actionsByInstance, clone);
  3026. class vido {
  3027. constructor() {
  3028. this.destroyable = [];
  3029. this.onChangeFunctions = [];
  3030. this.debug = false;
  3031. this.state = state;
  3032. this.api = api;
  3033. this.lastProps = {};
  3034. this.html = html;
  3035. this.svg = svg;
  3036. this.directive = directive;
  3037. this.asyncAppend = asyncAppend;
  3038. this.asyncReplace = asyncReplace;
  3039. this.cache = cache;
  3040. this.classMap = classMap;
  3041. this.guard = guard;
  3042. this.ifDefined = ifDefined;
  3043. this.repeat = repeat;
  3044. this.unsafeHTML = unsafeHTML;
  3045. this.until = until;
  3046. this.schedule = schedule;
  3047. this.actionsByInstance = (componentActions, props) => { };
  3048. this.StyleMap = StyleMap;
  3049. this.Detach = Detach;
  3050. this.PointerAction = PointerAction;
  3051. this.Action = Action;
  3052. this._components = components;
  3053. this._actions = actionsByInstance;
  3054. this.reuseComponents = this.reuseComponents.bind(this);
  3055. this.onDestroy = this.onDestroy.bind(this);
  3056. this.onChange = this.onChange.bind(this);
  3057. this.update = this.update.bind(this);
  3058. for (const name in additionalMethods) {
  3059. this[name] = additionalMethods[name];
  3060. }
  3061. }
  3062. addMethod(name, body) {
  3063. additionalMethods[name] = body;
  3064. }
  3065. onDestroy(fn) {
  3066. this.destroyable.push(fn);
  3067. }
  3068. onChange(fn) {
  3069. this.onChangeFunctions.push(fn);
  3070. }
  3071. update(callback) {
  3072. return this.updateTemplate(callback);
  3073. }
  3074. /**
  3075. * Reuse existing components when your data was changed
  3076. *
  3077. * @param {array} currentComponents - array of components
  3078. * @param {array} dataArray - any data as array for each component
  3079. * @param {function} getProps - you can pass params to component from array item ( example: item=>({id:item.id}) )
  3080. * @param {function} component - what kind of components do you want to create?
  3081. * @param {boolean} leaveTail - leave last elements and do not destroy corresponding components
  3082. * @returns {array} of components (with updated/destroyed/created ones)
  3083. */
  3084. reuseComponents(currentComponents, dataArray, getProps, component, leaveTail = true) {
  3085. const modified = [];
  3086. const currentLen = currentComponents.length;
  3087. const dataLen = dataArray.length;
  3088. let leave = false;
  3089. if (leaveTail && (dataArray === undefined || dataArray.length === 0)) {
  3090. leave = true;
  3091. }
  3092. let leaveStartingAt = 0;
  3093. if (currentLen < dataLen) {
  3094. let diff = dataLen - currentLen;
  3095. while (diff) {
  3096. const item = dataArray[dataLen - diff];
  3097. const newComponent = this.createComponent(component, getProps(item));
  3098. currentComponents.push(newComponent);
  3099. modified.push(newComponent.instance);
  3100. diff--;
  3101. }
  3102. }
  3103. else if (currentLen > dataLen) {
  3104. let diff = currentLen - dataLen;
  3105. if (leaveTail) {
  3106. leave = true;
  3107. leaveStartingAt = currentLen - diff;
  3108. }
  3109. while (diff) {
  3110. const index = currentLen - diff;
  3111. if (!leaveTail) {
  3112. modified.push(currentComponents[index].instance);
  3113. currentComponents[index].destroy();
  3114. }
  3115. diff--;
  3116. }
  3117. if (!leaveTail) {
  3118. currentComponents.length = dataLen;
  3119. }
  3120. }
  3121. let index = 0;
  3122. for (const component of currentComponents) {
  3123. const item = dataArray[index];
  3124. if (!modified.includes(component.instance)) {
  3125. component.change(getProps(item), { leave: leave && index >= leaveStartingAt });
  3126. }
  3127. index++;
  3128. }
  3129. }
  3130. createComponent(component, props = {}, content = null) {
  3131. const instance = component.name + ':' + componentId++;
  3132. let vidoInstance;
  3133. vidoInstance = new vido();
  3134. vidoInstance.instance = instance;
  3135. vidoInstance.name = component.name;
  3136. vidoInstance.Actions = new InstanceActionsCollector(instance);
  3137. const publicMethods = new PublicComponentMethods(instance, vidoInstance, props);
  3138. const internalMethods = new InternalComponentMethods(instance, vidoInstance, component(vidoInstance, props, content), content);
  3139. components.set(instance, internalMethods);
  3140. components.get(instance).change(props);
  3141. if (vidoInstance.debug) {
  3142. console.groupCollapsed(`component created ${instance}`);
  3143. console.log(clone({ props, components: components.keys(), actionsByInstance }));
  3144. console.trace();
  3145. console.groupEnd();
  3146. }
  3147. return publicMethods;
  3148. }
  3149. destroyComponent(instance, vidoInstance) {
  3150. if (vidoInstance.debug) {
  3151. console.groupCollapsed(`destroying component ${instance}...`);
  3152. console.log(clone({ components: components.keys(), actionsByInstance }));
  3153. console.trace();
  3154. console.groupEnd();
  3155. }
  3156. if (actionsByInstance.has(instance)) {
  3157. for (const action of actionsByInstance.get(instance)) {
  3158. if (typeof action.componentAction.destroy === 'function') {
  3159. action.componentAction.destroy(action.element, action.props);
  3160. }
  3161. }
  3162. }
  3163. actionsByInstance.delete(instance);
  3164. const component = components.get(instance);
  3165. component.update();
  3166. component.destroy();
  3167. components.delete(instance);
  3168. if (vidoInstance.debug) {
  3169. console.groupCollapsed(`component destroyed ${instance}`);
  3170. console.log(clone({ components: components.keys(), actionsByInstance }));
  3171. console.trace();
  3172. console.groupEnd();
  3173. }
  3174. }
  3175. executeActions() {
  3176. var _a, _b, _c;
  3177. for (const actions of actionsByInstance.values()) {
  3178. for (const action of actions) {
  3179. if (action.element.vido === undefined) {
  3180. const componentAction = action.componentAction;
  3181. const create = componentAction.create;
  3182. if (typeof create !== 'undefined') {
  3183. let result;
  3184. if (((_a = create.prototype) === null || _a === void 0 ? void 0 : _a.isAction) !== true &&
  3185. create.isAction === undefined &&
  3186. ((_b = create.prototype) === null || _b === void 0 ? void 0 : _b.update) === undefined &&
  3187. ((_c = create.prototype) === null || _c === void 0 ? void 0 : _c.destroy) === undefined) {
  3188. result = create(action.element, action.props);
  3189. }
  3190. else {
  3191. result = new create(action.element, action.props);
  3192. }
  3193. if (result !== undefined) {
  3194. if (typeof result === 'function') {
  3195. componentAction.destroy = result;
  3196. }
  3197. else {
  3198. if (typeof result.update === 'function') {
  3199. componentAction.update = result.update.bind(result);
  3200. }
  3201. if (typeof result.destroy === 'function') {
  3202. componentAction.destroy = result.destroy.bind(result);
  3203. }
  3204. }
  3205. }
  3206. }
  3207. }
  3208. else {
  3209. action.element.vido = action.props;
  3210. if (typeof action.componentAction.update === 'function') {
  3211. action.componentAction.update(action.element, action.props);
  3212. }
  3213. }
  3214. }
  3215. for (const action of actions) {
  3216. action.element.vido = action.props;
  3217. }
  3218. }
  3219. }
  3220. updateTemplate(callback) {
  3221. return new Promise((resolve) => {
  3222. const currentShouldUpdateCount = ++shouldUpdateCount;
  3223. const self = this;
  3224. function flush() {
  3225. if (currentShouldUpdateCount === shouldUpdateCount) {
  3226. shouldUpdateCount = 0;
  3227. self.render();
  3228. if (typeof callback === 'function')
  3229. callback();
  3230. resolve();
  3231. }
  3232. }
  3233. resolved.then(flush);
  3234. });
  3235. }
  3236. createApp(config) {
  3237. element = config.element;
  3238. const App = this.createComponent(config.component, config.props);
  3239. app = App.instance;
  3240. this.render();
  3241. return App;
  3242. }
  3243. render() {
  3244. const appComponent = components.get(app);
  3245. if (appComponent) {
  3246. render(appComponent.update(), element);
  3247. this.executeActions();
  3248. }
  3249. else if (element) {
  3250. element.remove();
  3251. }
  3252. }
  3253. }
  3254. return new vido();
  3255. }
  3256. Vido.prototype.lithtml = lithtml;
  3257. Vido.prototype.Action = Action;
  3258. Vido.prototype.Directive = Directive;
  3259. Vido.prototype.schedule = schedule;
  3260. Vido.prototype.Detach = Detach;
  3261. Vido.prototype.StyleMap = StyleMap;
  3262. Vido.prototype.PointerAction = PointerAction;
  3263. Vido.prototype.asyncAppend = asyncAppend;
  3264. Vido.prototype.asyncReplace = asyncReplace;
  3265. Vido.prototype.cache = cache;
  3266. Vido.prototype.classMap = classMap;
  3267. Vido.prototype.guard = guard;
  3268. Vido.prototype.ifDefined = ifDefined;
  3269. Vido.prototype.repeat = repeat;
  3270. Vido.prototype.unsafeHTML = unsafeHTML;
  3271. Vido.prototype.unti = until;
  3272. /**
  3273. * A collection of shims that provide minimal functionality of the ES6 collections.
  3274. *
  3275. * These implementations are not meant to be used outside of the ResizeObserver
  3276. * modules as they cover only a limited range of use cases.
  3277. */
  3278. /* eslint-disable require-jsdoc, valid-jsdoc */
  3279. var MapShim = (function () {
  3280. if (typeof Map !== 'undefined') {
  3281. return Map;
  3282. }
  3283. /**
  3284. * Returns index in provided array that matches the specified key.
  3285. *
  3286. * @param {Array<Array>} arr
  3287. * @param {*} key
  3288. * @returns {number}
  3289. */
  3290. function getIndex(arr, key) {
  3291. var result = -1;
  3292. arr.some(function (entry, index) {
  3293. if (entry[0] === key) {
  3294. result = index;
  3295. return true;
  3296. }
  3297. return false;
  3298. });
  3299. return result;
  3300. }
  3301. return /** @class */ (function () {
  3302. function class_1() {
  3303. this.__entries__ = [];
  3304. }
  3305. Object.defineProperty(class_1.prototype, "size", {
  3306. /**
  3307. * @returns {boolean}
  3308. */
  3309. get: function () {
  3310. return this.__entries__.length;
  3311. },
  3312. enumerable: true,
  3313. configurable: true
  3314. });
  3315. /**
  3316. * @param {*} key
  3317. * @returns {*}
  3318. */
  3319. class_1.prototype.get = function (key) {
  3320. var index = getIndex(this.__entries__, key);
  3321. var entry = this.__entries__[index];
  3322. return entry && entry[1];
  3323. };
  3324. /**
  3325. * @param {*} key
  3326. * @param {*} value
  3327. * @returns {void}
  3328. */
  3329. class_1.prototype.set = function (key, value) {
  3330. var index = getIndex(this.__entries__, key);
  3331. if (~index) {
  3332. this.__entries__[index][1] = value;
  3333. }
  3334. else {
  3335. this.__entries__.push([key, value]);
  3336. }
  3337. };
  3338. /**
  3339. * @param {*} key
  3340. * @returns {void}
  3341. */
  3342. class_1.prototype.delete = function (key) {
  3343. var entries = this.__entries__;
  3344. var index = getIndex(entries, key);
  3345. if (~index) {
  3346. entries.splice(index, 1);
  3347. }
  3348. };
  3349. /**
  3350. * @param {*} key
  3351. * @returns {void}
  3352. */
  3353. class_1.prototype.has = function (key) {
  3354. return !!~getIndex(this.__entries__, key);
  3355. };
  3356. /**
  3357. * @returns {void}
  3358. */
  3359. class_1.prototype.clear = function () {
  3360. this.__entries__.splice(0);
  3361. };
  3362. /**
  3363. * @param {Function} callback
  3364. * @param {*} [ctx=null]
  3365. * @returns {void}
  3366. */
  3367. class_1.prototype.forEach = function (callback, ctx) {
  3368. if (ctx === void 0) { ctx = null; }
  3369. for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) {
  3370. var entry = _a[_i];
  3371. callback.call(ctx, entry[1], entry[0]);
  3372. }
  3373. };
  3374. return class_1;
  3375. }());
  3376. })();
  3377. /**
  3378. * Detects whether window and document objects are available in current environment.
  3379. */
  3380. var isBrowser$1 = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document;
  3381. // Returns global object of a current environment.
  3382. var global$1 = (function () {
  3383. if (typeof global !== 'undefined' && global.Math === Math) {
  3384. return global;
  3385. }
  3386. if (typeof self !== 'undefined' && self.Math === Math) {
  3387. return self;
  3388. }
  3389. if (typeof window !== 'undefined' && window.Math === Math) {
  3390. return window;
  3391. }
  3392. // eslint-disable-next-line no-new-func
  3393. return Function('return this')();
  3394. })();
  3395. /**
  3396. * A shim for the requestAnimationFrame which falls back to the setTimeout if
  3397. * first one is not supported.
  3398. *
  3399. * @returns {number} Requests' identifier.
  3400. */
  3401. var requestAnimationFrame$1 = (function () {
  3402. if (typeof requestAnimationFrame === 'function') {
  3403. // It's required to use a bounded function because IE sometimes throws
  3404. // an "Invalid calling object" error if rAF is invoked without the global
  3405. // object on the left hand side.
  3406. return requestAnimationFrame.bind(global$1);
  3407. }
  3408. return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); };
  3409. })();
  3410. // Defines minimum timeout before adding a trailing call.
  3411. var trailingTimeout = 2;
  3412. /**
  3413. * Creates a wrapper function which ensures that provided callback will be
  3414. * invoked only once during the specified delay period.
  3415. *
  3416. * @param {Function} callback - Function to be invoked after the delay period.
  3417. * @param {number} delay - Delay after which to invoke callback.
  3418. * @returns {Function}
  3419. */
  3420. function throttle (callback, delay) {
  3421. var leadingCall = false, trailingCall = false, lastCallTime = 0;
  3422. /**
  3423. * Invokes the original callback function and schedules new invocation if
  3424. * the "proxy" was called during current request.
  3425. *
  3426. * @returns {void}
  3427. */
  3428. function resolvePending() {
  3429. if (leadingCall) {
  3430. leadingCall = false;
  3431. callback();
  3432. }
  3433. if (trailingCall) {
  3434. proxy();
  3435. }
  3436. }
  3437. /**
  3438. * Callback invoked after the specified delay. It will further postpone
  3439. * invocation of the original function delegating it to the
  3440. * requestAnimationFrame.
  3441. *
  3442. * @returns {void}
  3443. */
  3444. function timeoutCallback() {
  3445. requestAnimationFrame$1(resolvePending);
  3446. }
  3447. /**
  3448. * Schedules invocation of the original function.
  3449. *
  3450. * @returns {void}
  3451. */
  3452. function proxy() {
  3453. var timeStamp = Date.now();
  3454. if (leadingCall) {
  3455. // Reject immediately following calls.
  3456. if (timeStamp - lastCallTime < trailingTimeout) {
  3457. return;
  3458. }
  3459. // Schedule new call to be in invoked when the pending one is resolved.
  3460. // This is important for "transitions" which never actually start
  3461. // immediately so there is a chance that we might miss one if change
  3462. // happens amids the pending invocation.
  3463. trailingCall = true;
  3464. }
  3465. else {
  3466. leadingCall = true;
  3467. trailingCall = false;
  3468. setTimeout(timeoutCallback, delay);
  3469. }
  3470. lastCallTime = timeStamp;
  3471. }
  3472. return proxy;
  3473. }
  3474. // Minimum delay before invoking the update of observers.
  3475. var REFRESH_DELAY = 20;
  3476. // A list of substrings of CSS properties used to find transition events that
  3477. // might affect dimensions of observed elements.
  3478. var transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight'];
  3479. // Check if MutationObserver is available.
  3480. var mutationObserverSupported = typeof MutationObserver !== 'undefined';
  3481. /**
  3482. * Singleton controller class which handles updates of ResizeObserver instances.
  3483. */
  3484. var ResizeObserverController = /** @class */ (function () {
  3485. /**
  3486. * Creates a new instance of ResizeObserverController.
  3487. *
  3488. * @private
  3489. */
  3490. function ResizeObserverController() {
  3491. /**
  3492. * Indicates whether DOM listeners have been added.
  3493. *
  3494. * @private {boolean}
  3495. */
  3496. this.connected_ = false;
  3497. /**
  3498. * Tells that controller has subscribed for Mutation Events.
  3499. *
  3500. * @private {boolean}
  3501. */
  3502. this.mutationEventsAdded_ = false;
  3503. /**
  3504. * Keeps reference to the instance of MutationObserver.
  3505. *
  3506. * @private {MutationObserver}
  3507. */
  3508. this.mutationsObserver_ = null;
  3509. /**
  3510. * A list of connected observers.
  3511. *
  3512. * @private {Array<ResizeObserverSPI>}
  3513. */
  3514. this.observers_ = [];
  3515. this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);
  3516. this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);
  3517. }
  3518. /**
  3519. * Adds observer to observers list.
  3520. *
  3521. * @param {ResizeObserverSPI} observer - Observer to be added.
  3522. * @returns {void}
  3523. */
  3524. ResizeObserverController.prototype.addObserver = function (observer) {
  3525. if (!~this.observers_.indexOf(observer)) {
  3526. this.observers_.push(observer);
  3527. }
  3528. // Add listeners if they haven't been added yet.
  3529. if (!this.connected_) {
  3530. this.connect_();
  3531. }
  3532. };
  3533. /**
  3534. * Removes observer from observers list.
  3535. *
  3536. * @param {ResizeObserverSPI} observer - Observer to be removed.
  3537. * @returns {void}
  3538. */
  3539. ResizeObserverController.prototype.removeObserver = function (observer) {
  3540. var observers = this.observers_;
  3541. var index = observers.indexOf(observer);
  3542. // Remove observer if it's present in registry.
  3543. if (~index) {
  3544. observers.splice(index, 1);
  3545. }
  3546. // Remove listeners if controller has no connected observers.
  3547. if (!observers.length && this.connected_) {
  3548. this.disconnect_();
  3549. }
  3550. };
  3551. /**
  3552. * Invokes the update of observers. It will continue running updates insofar
  3553. * it detects changes.
  3554. *
  3555. * @returns {void}
  3556. */
  3557. ResizeObserverController.prototype.refresh = function () {
  3558. var changesDetected = this.updateObservers_();
  3559. // Continue running updates if changes have been detected as there might
  3560. // be future ones caused by CSS transitions.
  3561. if (changesDetected) {
  3562. this.refresh();
  3563. }
  3564. };
  3565. /**
  3566. * Updates every observer from observers list and notifies them of queued
  3567. * entries.
  3568. *
  3569. * @private
  3570. * @returns {boolean} Returns "true" if any observer has detected changes in
  3571. * dimensions of it's elements.
  3572. */
  3573. ResizeObserverController.prototype.updateObservers_ = function () {
  3574. // Collect observers that have active observations.
  3575. var activeObservers = this.observers_.filter(function (observer) {
  3576. return observer.gatherActive(), observer.hasActive();
  3577. });
  3578. // Deliver notifications in a separate cycle in order to avoid any
  3579. // collisions between observers, e.g. when multiple instances of
  3580. // ResizeObserver are tracking the same element and the callback of one
  3581. // of them changes content dimensions of the observed target. Sometimes
  3582. // this may result in notifications being blocked for the rest of observers.
  3583. activeObservers.forEach(function (observer) { return observer.broadcastActive(); });
  3584. return activeObservers.length > 0;
  3585. };
  3586. /**
  3587. * Initializes DOM listeners.
  3588. *
  3589. * @private
  3590. * @returns {void}
  3591. */
  3592. ResizeObserverController.prototype.connect_ = function () {
  3593. // Do nothing if running in a non-browser environment or if listeners
  3594. // have been already added.
  3595. if (!isBrowser$1 || this.connected_) {
  3596. return;
  3597. }
  3598. // Subscription to the "Transitionend" event is used as a workaround for
  3599. // delayed transitions. This way it's possible to capture at least the
  3600. // final state of an element.
  3601. document.addEventListener('transitionend', this.onTransitionEnd_);
  3602. window.addEventListener('resize', this.refresh);
  3603. if (mutationObserverSupported) {
  3604. this.mutationsObserver_ = new MutationObserver(this.refresh);
  3605. this.mutationsObserver_.observe(document, {
  3606. attributes: true,
  3607. childList: true,
  3608. characterData: true,
  3609. subtree: true
  3610. });
  3611. }
  3612. else {
  3613. document.addEventListener('DOMSubtreeModified', this.refresh);
  3614. this.mutationEventsAdded_ = true;
  3615. }
  3616. this.connected_ = true;
  3617. };
  3618. /**
  3619. * Removes DOM listeners.
  3620. *
  3621. * @private
  3622. * @returns {void}
  3623. */
  3624. ResizeObserverController.prototype.disconnect_ = function () {
  3625. // Do nothing if running in a non-browser environment or if listeners
  3626. // have been already removed.
  3627. if (!isBrowser$1 || !this.connected_) {
  3628. return;
  3629. }
  3630. document.removeEventListener('transitionend', this.onTransitionEnd_);
  3631. window.removeEventListener('resize', this.refresh);
  3632. if (this.mutationsObserver_) {
  3633. this.mutationsObserver_.disconnect();
  3634. }
  3635. if (this.mutationEventsAdded_) {
  3636. document.removeEventListener('DOMSubtreeModified', this.refresh);
  3637. }
  3638. this.mutationsObserver_ = null;
  3639. this.mutationEventsAdded_ = false;
  3640. this.connected_ = false;
  3641. };
  3642. /**
  3643. * "Transitionend" event handler.
  3644. *
  3645. * @private
  3646. * @param {TransitionEvent} event
  3647. * @returns {void}
  3648. */
  3649. ResizeObserverController.prototype.onTransitionEnd_ = function (_a) {
  3650. var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b;
  3651. // Detect whether transition may affect dimensions of an element.
  3652. var isReflowProperty = transitionKeys.some(function (key) {
  3653. return !!~propertyName.indexOf(key);
  3654. });
  3655. if (isReflowProperty) {
  3656. this.refresh();
  3657. }
  3658. };
  3659. /**
  3660. * Returns instance of the ResizeObserverController.
  3661. *
  3662. * @returns {ResizeObserverController}
  3663. */
  3664. ResizeObserverController.getInstance = function () {
  3665. if (!this.instance_) {
  3666. this.instance_ = new ResizeObserverController();
  3667. }
  3668. return this.instance_;
  3669. };
  3670. /**
  3671. * Holds reference to the controller's instance.
  3672. *
  3673. * @private {ResizeObserverController}
  3674. */
  3675. ResizeObserverController.instance_ = null;
  3676. return ResizeObserverController;
  3677. }());
  3678. /**
  3679. * Defines non-writable/enumerable properties of the provided target object.
  3680. *
  3681. * @param {Object} target - Object for which to define properties.
  3682. * @param {Object} props - Properties to be defined.
  3683. * @returns {Object} Target object.
  3684. */
  3685. var defineConfigurable = (function (target, props) {
  3686. for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) {
  3687. var key = _a[_i];
  3688. Object.defineProperty(target, key, {
  3689. value: props[key],
  3690. enumerable: false,
  3691. writable: false,
  3692. configurable: true
  3693. });
  3694. }
  3695. return target;
  3696. });
  3697. /**
  3698. * Returns the global object associated with provided element.
  3699. *
  3700. * @param {Object} target
  3701. * @returns {Object}
  3702. */
  3703. var getWindowOf = (function (target) {
  3704. // Assume that the element is an instance of Node, which means that it
  3705. // has the "ownerDocument" property from which we can retrieve a
  3706. // corresponding global object.
  3707. var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView;
  3708. // Return the local global object if it's not possible extract one from
  3709. // provided element.
  3710. return ownerGlobal || global$1;
  3711. });
  3712. // Placeholder of an empty content rectangle.
  3713. var emptyRect = createRectInit(0, 0, 0, 0);
  3714. /**
  3715. * Converts provided string to a number.
  3716. *
  3717. * @param {number|string} value
  3718. * @returns {number}
  3719. */
  3720. function toFloat(value) {
  3721. return parseFloat(value) || 0;
  3722. }
  3723. /**
  3724. * Extracts borders size from provided styles.
  3725. *
  3726. * @param {CSSStyleDeclaration} styles
  3727. * @param {...string} positions - Borders positions (top, right, ...)
  3728. * @returns {number}
  3729. */
  3730. function getBordersSize(styles) {
  3731. var positions = [];
  3732. for (var _i = 1; _i < arguments.length; _i++) {
  3733. positions[_i - 1] = arguments[_i];
  3734. }
  3735. return positions.reduce(function (size, position) {
  3736. var value = styles['border-' + position + '-width'];
  3737. return size + toFloat(value);
  3738. }, 0);
  3739. }
  3740. /**
  3741. * Extracts paddings sizes from provided styles.
  3742. *
  3743. * @param {CSSStyleDeclaration} styles
  3744. * @returns {Object} Paddings box.
  3745. */
  3746. function getPaddings(styles) {
  3747. var positions = ['top', 'right', 'bottom', 'left'];
  3748. var paddings = {};
  3749. for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) {
  3750. var position = positions_1[_i];
  3751. var value = styles['padding-' + position];
  3752. paddings[position] = toFloat(value);
  3753. }
  3754. return paddings;
  3755. }
  3756. /**
  3757. * Calculates content rectangle of provided SVG element.
  3758. *
  3759. * @param {SVGGraphicsElement} target - Element content rectangle of which needs
  3760. * to be calculated.
  3761. * @returns {DOMRectInit}
  3762. */
  3763. function getSVGContentRect(target) {
  3764. var bbox = target.getBBox();
  3765. return createRectInit(0, 0, bbox.width, bbox.height);
  3766. }
  3767. /**
  3768. * Calculates content rectangle of provided HTMLElement.
  3769. *
  3770. * @param {HTMLElement} target - Element for which to calculate the content rectangle.
  3771. * @returns {DOMRectInit}
  3772. */
  3773. function getHTMLElementContentRect(target) {
  3774. // Client width & height properties can't be
  3775. // used exclusively as they provide rounded values.
  3776. var clientWidth = target.clientWidth, clientHeight = target.clientHeight;
  3777. // By this condition we can catch all non-replaced inline, hidden and
  3778. // detached elements. Though elements with width & height properties less
  3779. // than 0.5 will be discarded as well.
  3780. //
  3781. // Without it we would need to implement separate methods for each of
  3782. // those cases and it's not possible to perform a precise and performance
  3783. // effective test for hidden elements. E.g. even jQuery's ':visible' filter
  3784. // gives wrong results for elements with width & height less than 0.5.
  3785. if (!clientWidth && !clientHeight) {
  3786. return emptyRect;
  3787. }
  3788. var styles = getWindowOf(target).getComputedStyle(target);
  3789. var paddings = getPaddings(styles);
  3790. var horizPad = paddings.left + paddings.right;
  3791. var vertPad = paddings.top + paddings.bottom;
  3792. // Computed styles of width & height are being used because they are the
  3793. // only dimensions available to JS that contain non-rounded values. It could
  3794. // be possible to utilize the getBoundingClientRect if only it's data wasn't
  3795. // affected by CSS transformations let alone paddings, borders and scroll bars.
  3796. var width = toFloat(styles.width), height = toFloat(styles.height);
  3797. // Width & height include paddings and borders when the 'border-box' box
  3798. // model is applied (except for IE).
  3799. if (styles.boxSizing === 'border-box') {
  3800. // Following conditions are required to handle Internet Explorer which
  3801. // doesn't include paddings and borders to computed CSS dimensions.
  3802. //
  3803. // We can say that if CSS dimensions + paddings are equal to the "client"
  3804. // properties then it's either IE, and thus we don't need to subtract
  3805. // anything, or an element merely doesn't have paddings/borders styles.
  3806. if (Math.round(width + horizPad) !== clientWidth) {
  3807. width -= getBordersSize(styles, 'left', 'right') + horizPad;
  3808. }
  3809. if (Math.round(height + vertPad) !== clientHeight) {
  3810. height -= getBordersSize(styles, 'top', 'bottom') + vertPad;
  3811. }
  3812. }
  3813. // Following steps can't be applied to the document's root element as its
  3814. // client[Width/Height] properties represent viewport area of the window.
  3815. // Besides, it's as well not necessary as the <html> itself neither has
  3816. // rendered scroll bars nor it can be clipped.
  3817. if (!isDocumentElement(target)) {
  3818. // In some browsers (only in Firefox, actually) CSS width & height
  3819. // include scroll bars size which can be removed at this step as scroll
  3820. // bars are the only difference between rounded dimensions + paddings
  3821. // and "client" properties, though that is not always true in Chrome.
  3822. var vertScrollbar = Math.round(width + horizPad) - clientWidth;
  3823. var horizScrollbar = Math.round(height + vertPad) - clientHeight;
  3824. // Chrome has a rather weird rounding of "client" properties.
  3825. // E.g. for an element with content width of 314.2px it sometimes gives
  3826. // the client width of 315px and for the width of 314.7px it may give
  3827. // 314px. And it doesn't happen all the time. So just ignore this delta
  3828. // as a non-relevant.
  3829. if (Math.abs(vertScrollbar) !== 1) {
  3830. width -= vertScrollbar;
  3831. }
  3832. if (Math.abs(horizScrollbar) !== 1) {
  3833. height -= horizScrollbar;
  3834. }
  3835. }
  3836. return createRectInit(paddings.left, paddings.top, width, height);
  3837. }
  3838. /**
  3839. * Checks whether provided element is an instance of the SVGGraphicsElement.
  3840. *
  3841. * @param {Element} target - Element to be checked.
  3842. * @returns {boolean}
  3843. */
  3844. var isSVGGraphicsElement = (function () {
  3845. // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement
  3846. // interface.
  3847. if (typeof SVGGraphicsElement !== 'undefined') {
  3848. return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; };
  3849. }
  3850. // If it's so, then check that element is at least an instance of the
  3851. // SVGElement and that it has the "getBBox" method.
  3852. // eslint-disable-next-line no-extra-parens
  3853. return function (target) { return (target instanceof getWindowOf(target).SVGElement &&
  3854. typeof target.getBBox === 'function'); };
  3855. })();
  3856. /**
  3857. * Checks whether provided element is a document element (<html>).
  3858. *
  3859. * @param {Element} target - Element to be checked.
  3860. * @returns {boolean}
  3861. */
  3862. function isDocumentElement(target) {
  3863. return target === getWindowOf(target).document.documentElement;
  3864. }
  3865. /**
  3866. * Calculates an appropriate content rectangle for provided html or svg element.
  3867. *
  3868. * @param {Element} target - Element content rectangle of which needs to be calculated.
  3869. * @returns {DOMRectInit}
  3870. */
  3871. function getContentRect(target) {
  3872. if (!isBrowser$1) {
  3873. return emptyRect;
  3874. }
  3875. if (isSVGGraphicsElement(target)) {
  3876. return getSVGContentRect(target);
  3877. }
  3878. return getHTMLElementContentRect(target);
  3879. }
  3880. /**
  3881. * Creates rectangle with an interface of the DOMRectReadOnly.
  3882. * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly
  3883. *
  3884. * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions.
  3885. * @returns {DOMRectReadOnly}
  3886. */
  3887. function createReadOnlyRect(_a) {
  3888. var x = _a.x, y = _a.y, width = _a.width, height = _a.height;
  3889. // If DOMRectReadOnly is available use it as a prototype for the rectangle.
  3890. var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object;
  3891. var rect = Object.create(Constr.prototype);
  3892. // Rectangle's properties are not writable and non-enumerable.
  3893. defineConfigurable(rect, {
  3894. x: x, y: y, width: width, height: height,
  3895. top: y,
  3896. right: x + width,
  3897. bottom: height + y,
  3898. left: x
  3899. });
  3900. return rect;
  3901. }
  3902. /**
  3903. * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates.
  3904. * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit
  3905. *
  3906. * @param {number} x - X coordinate.
  3907. * @param {number} y - Y coordinate.
  3908. * @param {number} width - Rectangle's width.
  3909. * @param {number} height - Rectangle's height.
  3910. * @returns {DOMRectInit}
  3911. */
  3912. function createRectInit(x, y, width, height) {
  3913. return { x: x, y: y, width: width, height: height };
  3914. }
  3915. /**
  3916. * Class that is responsible for computations of the content rectangle of
  3917. * provided DOM element and for keeping track of it's changes.
  3918. */
  3919. var ResizeObservation = /** @class */ (function () {
  3920. /**
  3921. * Creates an instance of ResizeObservation.
  3922. *
  3923. * @param {Element} target - Element to be observed.
  3924. */
  3925. function ResizeObservation(target) {
  3926. /**
  3927. * Broadcasted width of content rectangle.
  3928. *
  3929. * @type {number}
  3930. */
  3931. this.broadcastWidth = 0;
  3932. /**
  3933. * Broadcasted height of content rectangle.
  3934. *
  3935. * @type {number}
  3936. */
  3937. this.broadcastHeight = 0;
  3938. /**
  3939. * Reference to the last observed content rectangle.
  3940. *
  3941. * @private {DOMRectInit}
  3942. */
  3943. this.contentRect_ = createRectInit(0, 0, 0, 0);
  3944. this.target = target;
  3945. }
  3946. /**
  3947. * Updates content rectangle and tells whether it's width or height properties
  3948. * have changed since the last broadcast.
  3949. *
  3950. * @returns {boolean}
  3951. */
  3952. ResizeObservation.prototype.isActive = function () {
  3953. var rect = getContentRect(this.target);
  3954. this.contentRect_ = rect;
  3955. return (rect.width !== this.broadcastWidth ||
  3956. rect.height !== this.broadcastHeight);
  3957. };
  3958. /**
  3959. * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data
  3960. * from the corresponding properties of the last observed content rectangle.
  3961. *
  3962. * @returns {DOMRectInit} Last observed content rectangle.
  3963. */
  3964. ResizeObservation.prototype.broadcastRect = function () {
  3965. var rect = this.contentRect_;
  3966. this.broadcastWidth = rect.width;
  3967. this.broadcastHeight = rect.height;
  3968. return rect;
  3969. };
  3970. return ResizeObservation;
  3971. }());
  3972. var ResizeObserverEntry = /** @class */ (function () {
  3973. /**
  3974. * Creates an instance of ResizeObserverEntry.
  3975. *
  3976. * @param {Element} target - Element that is being observed.
  3977. * @param {DOMRectInit} rectInit - Data of the element's content rectangle.
  3978. */
  3979. function ResizeObserverEntry(target, rectInit) {
  3980. var contentRect = createReadOnlyRect(rectInit);
  3981. // According to the specification following properties are not writable
  3982. // and are also not enumerable in the native implementation.
  3983. //
  3984. // Property accessors are not being used as they'd require to define a
  3985. // private WeakMap storage which may cause memory leaks in browsers that
  3986. // don't support this type of collections.
  3987. defineConfigurable(this, { target: target, contentRect: contentRect });
  3988. }
  3989. return ResizeObserverEntry;
  3990. }());
  3991. var ResizeObserverSPI = /** @class */ (function () {
  3992. /**
  3993. * Creates a new instance of ResizeObserver.
  3994. *
  3995. * @param {ResizeObserverCallback} callback - Callback function that is invoked
  3996. * when one of the observed elements changes it's content dimensions.
  3997. * @param {ResizeObserverController} controller - Controller instance which
  3998. * is responsible for the updates of observer.
  3999. * @param {ResizeObserver} callbackCtx - Reference to the public
  4000. * ResizeObserver instance which will be passed to callback function.
  4001. */
  4002. function ResizeObserverSPI(callback, controller, callbackCtx) {
  4003. /**
  4004. * Collection of resize observations that have detected changes in dimensions
  4005. * of elements.
  4006. *
  4007. * @private {Array<ResizeObservation>}
  4008. */
  4009. this.activeObservations_ = [];
  4010. /**
  4011. * Registry of the ResizeObservation instances.
  4012. *
  4013. * @private {Map<Element, ResizeObservation>}
  4014. */
  4015. this.observations_ = new MapShim();
  4016. if (typeof callback !== 'function') {
  4017. throw new TypeError('The callback provided as parameter 1 is not a function.');
  4018. }
  4019. this.callback_ = callback;
  4020. this.controller_ = controller;
  4021. this.callbackCtx_ = callbackCtx;
  4022. }
  4023. /**
  4024. * Starts observing provided element.
  4025. *
  4026. * @param {Element} target - Element to be observed.
  4027. * @returns {void}
  4028. */
  4029. ResizeObserverSPI.prototype.observe = function (target) {
  4030. if (!arguments.length) {
  4031. throw new TypeError('1 argument required, but only 0 present.');
  4032. }
  4033. // Do nothing if current environment doesn't have the Element interface.
  4034. if (typeof Element === 'undefined' || !(Element instanceof Object)) {
  4035. return;
  4036. }
  4037. if (!(target instanceof getWindowOf(target).Element)) {
  4038. throw new TypeError('parameter 1 is not of type "Element".');
  4039. }
  4040. var observations = this.observations_;
  4041. // Do nothing if element is already being observed.
  4042. if (observations.has(target)) {
  4043. return;
  4044. }
  4045. observations.set(target, new ResizeObservation(target));
  4046. this.controller_.addObserver(this);
  4047. // Force the update of observations.
  4048. this.controller_.refresh();
  4049. };
  4050. /**
  4051. * Stops observing provided element.
  4052. *
  4053. * @param {Element} target - Element to stop observing.
  4054. * @returns {void}
  4055. */
  4056. ResizeObserverSPI.prototype.unobserve = function (target) {
  4057. if (!arguments.length) {
  4058. throw new TypeError('1 argument required, but only 0 present.');
  4059. }
  4060. // Do nothing if current environment doesn't have the Element interface.
  4061. if (typeof Element === 'undefined' || !(Element instanceof Object)) {
  4062. return;
  4063. }
  4064. if (!(target instanceof getWindowOf(target).Element)) {
  4065. throw new TypeError('parameter 1 is not of type "Element".');
  4066. }
  4067. var observations = this.observations_;
  4068. // Do nothing if element is not being observed.
  4069. if (!observations.has(target)) {
  4070. return;
  4071. }
  4072. observations.delete(target);
  4073. if (!observations.size) {
  4074. this.controller_.removeObserver(this);
  4075. }
  4076. };
  4077. /**
  4078. * Stops observing all elements.
  4079. *
  4080. * @returns {void}
  4081. */
  4082. ResizeObserverSPI.prototype.disconnect = function () {
  4083. this.clearActive();
  4084. this.observations_.clear();
  4085. this.controller_.removeObserver(this);
  4086. };
  4087. /**
  4088. * Collects observation instances the associated element of which has changed
  4089. * it's content rectangle.
  4090. *
  4091. * @returns {void}
  4092. */
  4093. ResizeObserverSPI.prototype.gatherActive = function () {
  4094. var _this = this;
  4095. this.clearActive();
  4096. this.observations_.forEach(function (observation) {
  4097. if (observation.isActive()) {
  4098. _this.activeObservations_.push(observation);
  4099. }
  4100. });
  4101. };
  4102. /**
  4103. * Invokes initial callback function with a list of ResizeObserverEntry
  4104. * instances collected from active resize observations.
  4105. *
  4106. * @returns {void}
  4107. */
  4108. ResizeObserverSPI.prototype.broadcastActive = function () {
  4109. // Do nothing if observer doesn't have active observations.
  4110. if (!this.hasActive()) {
  4111. return;
  4112. }
  4113. var ctx = this.callbackCtx_;
  4114. // Create ResizeObserverEntry instance for every active observation.
  4115. var entries = this.activeObservations_.map(function (observation) {
  4116. return new ResizeObserverEntry(observation.target, observation.broadcastRect());
  4117. });
  4118. this.callback_.call(ctx, entries, ctx);
  4119. this.clearActive();
  4120. };
  4121. /**
  4122. * Clears the collection of active observations.
  4123. *
  4124. * @returns {void}
  4125. */
  4126. ResizeObserverSPI.prototype.clearActive = function () {
  4127. this.activeObservations_.splice(0);
  4128. };
  4129. /**
  4130. * Tells whether observer has active observations.
  4131. *
  4132. * @returns {boolean}
  4133. */
  4134. ResizeObserverSPI.prototype.hasActive = function () {
  4135. return this.activeObservations_.length > 0;
  4136. };
  4137. return ResizeObserverSPI;
  4138. }());
  4139. // Registry of internal observers. If WeakMap is not available use current shim
  4140. // for the Map collection as it has all required methods and because WeakMap
  4141. // can't be fully polyfilled anyway.
  4142. var observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim();
  4143. /**
  4144. * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation
  4145. * exposing only those methods and properties that are defined in the spec.
  4146. */
  4147. var ResizeObserver = /** @class */ (function () {
  4148. /**
  4149. * Creates a new instance of ResizeObserver.
  4150. *
  4151. * @param {ResizeObserverCallback} callback - Callback that is invoked when
  4152. * dimensions of the observed elements change.
  4153. */
  4154. function ResizeObserver(callback) {
  4155. if (!(this instanceof ResizeObserver)) {
  4156. throw new TypeError('Cannot call a class as a function.');
  4157. }
  4158. if (!arguments.length) {
  4159. throw new TypeError('1 argument required, but only 0 present.');
  4160. }
  4161. var controller = ResizeObserverController.getInstance();
  4162. var observer = new ResizeObserverSPI(callback, controller, this);
  4163. observers.set(this, observer);
  4164. }
  4165. return ResizeObserver;
  4166. }());
  4167. // Expose public methods of ResizeObserver.
  4168. [
  4169. 'observe',
  4170. 'unobserve',
  4171. 'disconnect'
  4172. ].forEach(function (method) {
  4173. ResizeObserver.prototype[method] = function () {
  4174. var _a;
  4175. return (_a = observers.get(this))[method].apply(_a, arguments);
  4176. };
  4177. });
  4178. var index = (function () {
  4179. // Export existing implementation if available.
  4180. if (typeof global$1.ResizeObserver !== 'undefined') {
  4181. return global$1.ResizeObserver;
  4182. }
  4183. return ResizeObserver;
  4184. })();
  4185. /**
  4186. * Main component
  4187. *
  4188. * @copyright Rafal Pospiech <https://neuronet.io>
  4189. * @author Rafal Pospiech <neuronet.io@gmail.com>
  4190. * @package gantt-schedule-timeline-calendar
  4191. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  4192. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  4193. */
  4194. function Main(vido, props = {}) {
  4195. const { api, state, onDestroy, Actions, update, createComponent, html, StyleMap, schedule } = vido;
  4196. const componentName = api.name;
  4197. // Initialize plugins
  4198. onDestroy(state.subscribe('config.plugins', plugins => {
  4199. if (typeof plugins !== 'undefined' && Array.isArray(plugins)) {
  4200. for (const initializePlugin of plugins) {
  4201. const destroyPlugin = initializePlugin(vido);
  4202. if (typeof destroyPlugin === 'function') {
  4203. onDestroy(destroyPlugin);
  4204. }
  4205. else if (destroyPlugin && destroyPlugin.hasOwnProperty('destroy')) {
  4206. destroyPlugin.destroy();
  4207. }
  4208. }
  4209. }
  4210. }));
  4211. const componentSubs = [];
  4212. let ListComponent;
  4213. componentSubs.push(state.subscribe('config.components.List', value => (ListComponent = value)));
  4214. let ChartComponent;
  4215. componentSubs.push(state.subscribe('config.components.Chart', value => (ChartComponent = value)));
  4216. const List = createComponent(ListComponent);
  4217. onDestroy(List.destroy);
  4218. const Chart = createComponent(ChartComponent);
  4219. onDestroy(Chart.destroy);
  4220. onDestroy(() => {
  4221. componentSubs.forEach(unsub => unsub());
  4222. });
  4223. let wrapper;
  4224. onDestroy(state.subscribe('config.wrappers.Main', value => (wrapper = value)));
  4225. const componentActions = api.getActions('main');
  4226. let className, classNameVerticalScroll;
  4227. const styleMap = new StyleMap({}), verticalScrollStyleMap = new StyleMap({}), verticalScrollAreaStyleMap = new StyleMap({});
  4228. let verticalScrollBarElement;
  4229. let rowsHeight = 0;
  4230. let resizerActive = false;
  4231. /**
  4232. * Update class names
  4233. * @param {object} classNames
  4234. */
  4235. const updateClassNames = classNames => {
  4236. const config = state.get('config');
  4237. className = api.getClass(componentName, { config });
  4238. if (resizerActive) {
  4239. className += ` ${componentName}__list-column-header-resizer--active`;
  4240. }
  4241. classNameVerticalScroll = api.getClass('vertical-scroll', { config });
  4242. update();
  4243. };
  4244. onDestroy(state.subscribe('config.classNames', updateClassNames));
  4245. /**
  4246. * Height change
  4247. */
  4248. function heightChange() {
  4249. const config = state.get('config');
  4250. const scrollBarHeight = state.get('_internal.scrollBarHeight');
  4251. const height = config.height - config.headerHeight - scrollBarHeight;
  4252. state.update('_internal.height', height);
  4253. styleMap.style['--height'] = config.height + 'px';
  4254. verticalScrollStyleMap.style.height = height + 'px';
  4255. verticalScrollStyleMap.style.width = scrollBarHeight + 'px';
  4256. verticalScrollStyleMap.style['margin-top'] = config.headerHeight + 'px';
  4257. update();
  4258. }
  4259. onDestroy(state.subscribeAll(['config.height', 'config.headerHeight', '_internal.scrollBarHeight'], heightChange));
  4260. /**
  4261. * Resizer active change
  4262. * @param {boolean} active
  4263. */
  4264. function resizerActiveChange(active) {
  4265. resizerActive = active;
  4266. className = api.getClass(api.name);
  4267. if (resizerActive) {
  4268. className += ` ${api.name}__list-column-header-resizer--active`;
  4269. }
  4270. update();
  4271. }
  4272. onDestroy(state.subscribe('_internal.list.columns.resizer.active', resizerActiveChange));
  4273. /**
  4274. * Generate tree
  4275. * @param {object} bulk
  4276. * @param {object} eventInfo
  4277. */
  4278. function generateTree(bulk, eventInfo) {
  4279. if (state.get('_internal.flatTreeMap').length && eventInfo.type === 'subscribe') {
  4280. return;
  4281. }
  4282. const configRows = state.get('config.list.rows');
  4283. const rows = [];
  4284. for (const rowId in configRows) {
  4285. rows.push(configRows[rowId]);
  4286. }
  4287. api.fillEmptyRowValues(rows);
  4288. const configItems = state.get('config.chart.items');
  4289. const items = [];
  4290. for (const itemId in configItems) {
  4291. items.push(configItems[itemId]);
  4292. }
  4293. api.prepareItems(items);
  4294. const treeMap = api.makeTreeMap(rows, items);
  4295. state.update('_internal.treeMap', treeMap);
  4296. const flatTreeMapById = api.getFlatTreeMapById(treeMap);
  4297. state.update('_internal.flatTreeMapById', flatTreeMapById);
  4298. const flatTreeMap = api.flattenTreeMap(treeMap);
  4299. state.update('_internal.flatTreeMap', flatTreeMap);
  4300. update();
  4301. }
  4302. onDestroy(state.subscribeAll(['config.list.rows;', 'config.chart.items;'], generateTree));
  4303. onDestroy(state.subscribeAll(['config.list.rows.*.parentId', 'config.chart.items.*.rowId'], generateTree, { bulk: true }));
  4304. function prepareExpanded() {
  4305. const configRows = state.get('config.list.rows');
  4306. const rowsWithParentsExpanded = api.getRowsFromIds(api.getRowsWithParentsExpanded(state.get('_internal.flatTreeMap'), state.get('_internal.flatTreeMapById'), configRows), configRows);
  4307. rowsHeight = api.getRowsHeight(rowsWithParentsExpanded);
  4308. state.update('_internal.list.rowsHeight', rowsHeight);
  4309. state.update('_internal.list.rowsWithParentsExpanded', rowsWithParentsExpanded);
  4310. update();
  4311. }
  4312. onDestroy(state.subscribeAll(['config.list.rows.*.expanded', '_internal.treeMap;', 'config.list.rows.*.height'], prepareExpanded, { bulk: true }));
  4313. /**
  4314. * Generate visible rows
  4315. */
  4316. function generateVisibleRowsAndItems() {
  4317. const { visibleRows, compensation } = api.getVisibleRowsAndCompensation(state.get('_internal.list.rowsWithParentsExpanded'));
  4318. const smoothScroll = state.get('config.scroll.smooth');
  4319. const currentVisibleRows = state.get('_internal.list.visibleRows');
  4320. let shouldUpdate = true;
  4321. state.update('config.scroll.compensation.y', smoothScroll ? -compensation : 0);
  4322. if (visibleRows.length !== currentVisibleRows.length) {
  4323. shouldUpdate = true;
  4324. }
  4325. else if (visibleRows.length) {
  4326. shouldUpdate = visibleRows.some((row, index) => {
  4327. if (typeof currentVisibleRows[index] === 'undefined') {
  4328. return true;
  4329. }
  4330. return row.id !== currentVisibleRows[index].id;
  4331. });
  4332. }
  4333. if (shouldUpdate) {
  4334. state.update('_internal.list.visibleRows', visibleRows);
  4335. }
  4336. const visibleItems = [];
  4337. for (const row of visibleRows) {
  4338. for (const item of row._internal.items) {
  4339. visibleItems.push(item);
  4340. }
  4341. }
  4342. state.update('_internal.chart.visibleItems', visibleItems);
  4343. update();
  4344. }
  4345. onDestroy(state.subscribeAll(['_internal.list.rowsWithParentsExpanded;', 'config.scroll.top', 'config.chart.items'], generateVisibleRowsAndItems, { bulk: true }));
  4346. let elementScrollTop = 0;
  4347. function onVisibleRowsChange() {
  4348. const top = state.get('config.scroll.top');
  4349. verticalScrollAreaStyleMap.style.width = '1px';
  4350. verticalScrollAreaStyleMap.style.height = rowsHeight + 'px';
  4351. if (elementScrollTop !== top && verticalScrollBarElement) {
  4352. elementScrollTop = top;
  4353. verticalScrollBarElement.scrollTop = top;
  4354. }
  4355. update();
  4356. }
  4357. onDestroy(state.subscribe('_internal.list.visibleRows;', onVisibleRowsChange));
  4358. /**
  4359. * Generate and add period dates
  4360. * @param {string} period
  4361. * @param {object} internalTime
  4362. */
  4363. const generatePeriodDates = (period, internalTime) => {
  4364. const dates = [];
  4365. let leftGlobal = internalTime.leftGlobal;
  4366. const timePerPixel = internalTime.timePerPixel;
  4367. let startOfLeft = api.time
  4368. .date(leftGlobal)
  4369. .startOf(period)
  4370. .valueOf();
  4371. if (startOfLeft < leftGlobal)
  4372. startOfLeft = leftGlobal;
  4373. let sub = leftGlobal - startOfLeft;
  4374. let subPx = sub / timePerPixel;
  4375. let leftPx = 0;
  4376. const diff = Math.ceil(api.time
  4377. .date(internalTime.rightGlobal)
  4378. .endOf(period)
  4379. .diff(api.time.date(leftGlobal).startOf(period), period, true));
  4380. for (let i = 0; i < diff; i++) {
  4381. const date = {
  4382. sub,
  4383. subPx,
  4384. leftGlobal,
  4385. rightGlobal: api.time
  4386. .date(leftGlobal)
  4387. .endOf(period)
  4388. .valueOf(),
  4389. width: 0,
  4390. leftPx: 0,
  4391. rightPx: 0,
  4392. period
  4393. };
  4394. date.width = (date.rightGlobal - date.leftGlobal + sub) / timePerPixel;
  4395. date.leftPx = leftPx;
  4396. leftPx += date.width;
  4397. date.rightPx = leftPx;
  4398. dates.push(date);
  4399. leftGlobal = date.rightGlobal + 1;
  4400. sub = 0;
  4401. subPx = 0;
  4402. }
  4403. return dates;
  4404. };
  4405. function triggerLoadedEvent() {
  4406. if (state.get('_internal.loadedEventTriggered'))
  4407. return;
  4408. Promise.resolve().then(() => {
  4409. const element = state.get('_internal.elements.main');
  4410. const parent = element.parentNode;
  4411. const event = new Event('gstc-loaded');
  4412. element.dispatchEvent(event);
  4413. parent.dispatchEvent(event);
  4414. });
  4415. state.update('_internal.loadedEventTriggered', true);
  4416. }
  4417. function limitGlobalAndSetCenter(time) {
  4418. if (time.leftGlobal < time.finalFrom)
  4419. time.leftGlobal = time.finalFrom;
  4420. if (time.rightGlobal > time.finalTo)
  4421. time.rightGlobal = time.finalTo;
  4422. time.centerGlobal = time.leftGlobal + Math.round((time.rightGlobal - time.leftGlobal) / 2);
  4423. return time;
  4424. }
  4425. function guessPeriod(time, calendar) {
  4426. if (!time.zoom)
  4427. return time;
  4428. for (const level of calendar.levels) {
  4429. const formatting = level.formats.find(format => +time.zoom <= +format.zoomTo);
  4430. if (formatting && level.main) {
  4431. time.period = formatting.period;
  4432. }
  4433. }
  4434. return time;
  4435. }
  4436. function updateLevels(time, calendar) {
  4437. time.levels = [];
  4438. let index = 0;
  4439. for (const level of calendar.levels) {
  4440. const formatting = level.formats.find(format => +time.zoom <= +format.zoomTo);
  4441. if (level.main) {
  4442. time.format = formatting;
  4443. time.level = index;
  4444. }
  4445. if (formatting) {
  4446. time.levels.push(generatePeriodDates(formatting.period, time));
  4447. }
  4448. index++;
  4449. }
  4450. }
  4451. let working = false;
  4452. function recalculateTimes(reason) {
  4453. if (working)
  4454. return;
  4455. working = true;
  4456. const configTime = state.get('config.chart.time');
  4457. const chartWidth = state.get('_internal.chart.dimensions.width');
  4458. const calendar = state.get('config.chart.calendar');
  4459. const oldTime = Object.assign({}, state.get('_internal.chart.time'));
  4460. let time = api.mergeDeep({}, configTime);
  4461. if ((!time.from || !time.to) && !Object.keys(state.get('config.chart.items')).length) {
  4462. return;
  4463. }
  4464. let mainLevel = calendar.levels.find(level => level.main);
  4465. if (!mainLevel) {
  4466. throw new Error('Main calendar level not found (config.chart.calendar.levels).');
  4467. }
  4468. if (!time.calculatedZoomMode) {
  4469. if (time.period !== oldTime.period) {
  4470. let periodFormat = mainLevel.formats.find(format => format.period === time.period && format.default);
  4471. if (periodFormat) {
  4472. time.zoom = periodFormat.zoomTo;
  4473. }
  4474. }
  4475. guessPeriod(time, calendar);
  4476. }
  4477. // If _internal.chart.time (leftGlobal, centerGlobal, rightGlobal, from , to) was changed
  4478. // then we need to apply those values - no recalculation is needed (values form plugins etc)
  4479. const justApply = ['leftGlobal', 'centerGlobal', 'rightGlobal', 'from', 'to'].includes(reason.name);
  4480. if (justApply) {
  4481. time = Object.assign(Object.assign({}, time), { leftGlobal: configTime.leftGlobal, centerGlobal: configTime.centerGlobal, rightGlobal: configTime.rightGlobal, from: configTime.from, to: configTime.to });
  4482. }
  4483. let scrollLeft = 0;
  4484. // source of everything = time.timePerPixel
  4485. if (time.calculatedZoomMode && chartWidth) {
  4486. time.finalFrom = time.from;
  4487. time.finalTo = time.to;
  4488. time.totalViewDurationMs = api.time.date(time.finalTo).diff(time.finalFrom, 'milliseconds');
  4489. time.timePerPixel = time.totalViewDurationMs / chartWidth;
  4490. time.zoom = Math.log(time.timePerPixel) / Math.log(2);
  4491. guessPeriod(time, calendar);
  4492. time.totalViewDurationPx = Math.round(time.totalViewDurationMs / time.timePerPixel);
  4493. time.leftGlobal = time.from;
  4494. time.rightGlobal = time.to;
  4495. }
  4496. else {
  4497. time.timePerPixel = Math.pow(2, time.zoom);
  4498. time = api.time.recalculateFromTo(time);
  4499. time.totalViewDurationMs = api.time.date(time.finalTo).diff(time.finalFrom, 'milliseconds');
  4500. time.totalViewDurationPx = Math.round(time.totalViewDurationMs / time.timePerPixel);
  4501. scrollLeft = state.get('config.scroll.left');
  4502. }
  4503. if (!justApply && !time.calculatedZoomMode) {
  4504. // If time.zoom (or time.period) has been changed
  4505. // then we need to recalculate basing on time.centerGlobal
  4506. // and update scroll left
  4507. // if not then we need to calculate from scroll left
  4508. // because change was triggered by scroll
  4509. if (time.zoom !== oldTime.zoom && oldTime.centerGlobal) {
  4510. const chartWidthInMs = chartWidth * time.timePerPixel;
  4511. const halfChartInMs = Math.round(chartWidthInMs / 2);
  4512. time.leftGlobal = oldTime.centerGlobal - halfChartInMs;
  4513. time.rightGlobal = time.leftGlobal + chartWidthInMs;
  4514. scrollLeft = (time.leftGlobal - time.finalFrom) / time.timePerPixel;
  4515. scrollLeft = api.limitScrollLeft(time.totalViewDurationPx, chartWidth, scrollLeft);
  4516. }
  4517. else {
  4518. time.leftGlobal = scrollLeft * time.timePerPixel + time.finalFrom;
  4519. time.rightGlobal = time.leftGlobal + chartWidth * time.timePerPixel;
  4520. }
  4521. }
  4522. limitGlobalAndSetCenter(time);
  4523. time.leftInner = time.leftGlobal - time.finalFrom;
  4524. time.rightInner = time.rightGlobal - time.finalFrom;
  4525. time.leftPx = time.leftInner / time.timePerPixel;
  4526. time.rightPx = time.rightInner / time.timePerPixel;
  4527. updateLevels(time, calendar);
  4528. let xCompensation = 0;
  4529. if (time.levels[time.level] && time.levels[time.level].length !== 0) {
  4530. xCompensation = time.levels[time.level][0].subPx;
  4531. }
  4532. state.update(`_internal.chart.time`, time);
  4533. state.update('config.scroll.compensation.x', xCompensation);
  4534. state.update('config.chart.time', configTime => {
  4535. configTime.zoom = time.zoom;
  4536. configTime.period = time.format.period;
  4537. configTime.leftGlobal = time.leftGlobal;
  4538. configTime.centerGlobal = time.centerGlobal;
  4539. configTime.rightGlobal = time.rightGlobal;
  4540. configTime.from = time.from;
  4541. configTime.to = time.to;
  4542. configTime.finalFrom = time.finalFrom;
  4543. configTime.finalTo = time.finalTo;
  4544. return configTime;
  4545. });
  4546. state.update('config.scroll.left', scrollLeft);
  4547. update().then(() => {
  4548. if (!state.get('_internal.loaded.time')) {
  4549. state.update('_internal.loaded.time', true);
  4550. }
  4551. });
  4552. working = false;
  4553. }
  4554. const recalculationTriggerCache = {
  4555. initialized: false,
  4556. zoom: 0,
  4557. period: '',
  4558. scrollLeft: 0,
  4559. chartWidth: 0,
  4560. leftGlobal: 0,
  4561. centerGlobal: 0,
  4562. rightGlobal: 0,
  4563. from: 0,
  4564. to: 0
  4565. };
  4566. function recalculationIsNeeded() {
  4567. const configTime = state.get('config.chart.time');
  4568. const scrollLeft = state.get('config.scroll.left');
  4569. const chartWidth = state.get('_internal.chart.dimensions.width');
  4570. const cache = Object.assign({}, recalculationTriggerCache);
  4571. recalculationTriggerCache.zoom = configTime.zoom;
  4572. recalculationTriggerCache.period = configTime.period;
  4573. recalculationTriggerCache.leftGlobal = configTime.leftGlobal;
  4574. recalculationTriggerCache.centerGlobal = configTime.centerGlobal;
  4575. recalculationTriggerCache.rightGlobal = configTime.rightGlobal;
  4576. recalculationTriggerCache.from = configTime.from;
  4577. recalculationTriggerCache.to = configTime.to;
  4578. recalculationTriggerCache.scrollLeft = scrollLeft;
  4579. recalculationTriggerCache.chartWidth = chartWidth;
  4580. if (!recalculationTriggerCache.initialized) {
  4581. recalculationTriggerCache.initialized = true;
  4582. return { name: 'all' };
  4583. }
  4584. if (configTime.zoom !== cache.zoom)
  4585. return { name: 'zoom', oldValue: cache.zoom, newValue: configTime.zoom };
  4586. if (configTime.period !== cache.period)
  4587. return { name: 'period', oldValue: cache.period, newValue: configTime.period };
  4588. if (configTime.leftGlobal !== cache.leftGlobal)
  4589. return { name: 'leftGlobal', oldValue: cache.leftGlobal, newValue: configTime.leftGlobal };
  4590. if (configTime.centerGlobal !== cache.centerGlobal)
  4591. return { name: 'centerGlobal', oldValue: cache.centerGlobal, newValue: configTime.centerGlobal };
  4592. if (configTime.rightGlobal !== cache.rightGlobal)
  4593. return { name: 'rightGlobal', oldValue: cache.rightGlobal, newValue: configTime.rightGlobal };
  4594. if (configTime.from !== cache.from)
  4595. return { name: 'from', oldValue: cache.from, newValue: configTime.from };
  4596. if (configTime.to !== cache.to)
  4597. return { name: 'to', oldValue: cache.to, newValue: configTime.to };
  4598. if (scrollLeft !== cache.scrollLeft)
  4599. return { name: 'scroll', oldValue: cache.scrollLeft, newValue: scrollLeft };
  4600. if (chartWidth !== cache.chartWidth)
  4601. return { name: 'chartWidth', oldValue: cache.chartWidth, newValue: chartWidth };
  4602. return false;
  4603. }
  4604. onDestroy(state.subscribeAll(['config.chart.time', 'config.chart.calendar.levels', 'config.scroll.left', '_internal.chart.dimensions.width'], () => {
  4605. let reason = recalculationIsNeeded();
  4606. if (reason)
  4607. recalculateTimes(reason);
  4608. }, { bulk: true }));
  4609. // When time.from and time.to is not specified and items are reloaded;
  4610. // check if item is outside current time scope and extend it if needed
  4611. onDestroy(state.subscribe('config.chart.items.*.time', items => {
  4612. recalculateTimes({ name: 'items' });
  4613. }, { bulk: true }));
  4614. if (state.get('config.usageStatistics') === true && !localStorage.getItem('gstcus')) {
  4615. try {
  4616. fetch('https://gstc-us.neuronet.io/', {
  4617. method: 'POST',
  4618. cache: 'force-cache',
  4619. mode: 'cors',
  4620. credentials: 'omit',
  4621. redirect: 'follow',
  4622. body: JSON.stringify({ location: { href: location.href, host: location.host } })
  4623. }).catch(e => { });
  4624. localStorage.setItem('gstcus', 'true');
  4625. }
  4626. catch (e) { }
  4627. }
  4628. let scrollTop = 0;
  4629. let propagate = true;
  4630. onDestroy(state.subscribe('config.scroll.propagate', prpgt => (propagate = prpgt)));
  4631. /**
  4632. * Handle scroll Event
  4633. * @param {MouseEvent} event
  4634. */
  4635. function handleEvent(event) {
  4636. if (!propagate) {
  4637. event.stopPropagation();
  4638. event.preventDefault();
  4639. }
  4640. if (event.type === 'scroll') {
  4641. // @ts-ignore
  4642. const top = event.target.scrollTop;
  4643. /**
  4644. * Handle on scroll event
  4645. * @param {object} scroll
  4646. * @returns {object} scroll
  4647. */
  4648. const handleOnScroll = scroll => {
  4649. scroll.top = top;
  4650. scrollTop = scroll.top;
  4651. const scrollInner = state.get('_internal.elements.vertical-scroll-inner');
  4652. if (scrollInner) {
  4653. const scrollHeight = scrollInner.clientHeight;
  4654. scroll.percent.top = scroll.top / scrollHeight;
  4655. }
  4656. return scroll;
  4657. };
  4658. if (scrollTop !== top)
  4659. state.update('config.scroll', handleOnScroll, {
  4660. only: ['top', 'percent.top']
  4661. });
  4662. }
  4663. }
  4664. const onScroll = {
  4665. handleEvent,
  4666. passive: false,
  4667. capture: false
  4668. };
  4669. const dimensions = { width: 0, height: 0 };
  4670. let ro;
  4671. /**
  4672. * Resize action
  4673. * @param {Element} element
  4674. */
  4675. class ResizeAction {
  4676. constructor(element) {
  4677. if (!ro) {
  4678. ro = new index((entries, observer) => {
  4679. const width = element.clientWidth;
  4680. const height = element.clientHeight;
  4681. if (dimensions.width !== width || dimensions.height !== height) {
  4682. dimensions.width = width;
  4683. dimensions.height = height;
  4684. state.update('_internal.dimensions', dimensions);
  4685. }
  4686. });
  4687. ro.observe(element);
  4688. state.update('_internal.elements.main', element);
  4689. }
  4690. }
  4691. update() { }
  4692. destroy(element) {
  4693. ro.unobserve(element);
  4694. }
  4695. }
  4696. if (!componentActions.includes(ResizeAction)) {
  4697. componentActions.push(ResizeAction);
  4698. }
  4699. onDestroy(() => {
  4700. ro.disconnect();
  4701. });
  4702. /**
  4703. * Bind scroll element
  4704. * @param {HTMLElement} element
  4705. */
  4706. function bindScrollElement(element) {
  4707. if (!verticalScrollBarElement) {
  4708. verticalScrollBarElement = element;
  4709. state.update('_internal.elements.vertical-scroll', element);
  4710. }
  4711. }
  4712. onDestroy(state.subscribeAll(['_internal.loaded', '_internal.chart.time.totalViewDurationPx'], () => {
  4713. if (state.get('_internal.loadedEventTriggered'))
  4714. return;
  4715. const loaded = state.get('_internal.loaded');
  4716. if (loaded.main && loaded.chart && loaded.time && loaded['horizontal-scroll-inner']) {
  4717. const scroll = state.get('_internal.elements.horizontal-scroll-inner');
  4718. const width = state.get('_internal.chart.time.totalViewDurationPx');
  4719. if (scroll && scroll.clientWidth === Math.round(width)) {
  4720. setTimeout(triggerLoadedEvent, 0);
  4721. }
  4722. }
  4723. }));
  4724. function LoadedEventAction() {
  4725. state.update('_internal.loaded.main', true);
  4726. }
  4727. if (!componentActions.includes(LoadedEventAction))
  4728. componentActions.push(LoadedEventAction);
  4729. /**
  4730. * Bind scroll inner element
  4731. * @param {Element} element
  4732. */
  4733. function bindScrollInnerElement(element) {
  4734. if (!state.get('_internal.elements.vertical-scroll-inner'))
  4735. state.update('_internal.elements.vertical-scroll-inner', element);
  4736. if (!state.get('_internal.loaded.vertical-scroll-inner'))
  4737. state.update('_internal.loaded.vertical-scroll-inner', true);
  4738. }
  4739. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  4740. const mainActions = Actions.create(componentActions, actionProps);
  4741. const verticalScrollActions = Actions.create([bindScrollElement]);
  4742. const verticalScrollAreaActions = Actions.create([bindScrollInnerElement]);
  4743. return templateProps => wrapper(html `
  4744. <div
  4745. data-info-url="https://github.com/neuronetio/gantt-schedule-timeline-calendar"
  4746. class=${className}
  4747. style=${styleMap}
  4748. @scroll=${onScroll}
  4749. @wheel=${onScroll}
  4750. data-actions=${mainActions}
  4751. >
  4752. ${List.html()}${Chart.html()}
  4753. <div
  4754. class=${classNameVerticalScroll}
  4755. style=${verticalScrollStyleMap}
  4756. @scroll=${onScroll}
  4757. @wheel=${onScroll}
  4758. data-actions=${verticalScrollActions}
  4759. >
  4760. <div style=${verticalScrollAreaStyleMap} data-actions=${verticalScrollAreaActions} />
  4761. </div>
  4762. </div>
  4763. `, { props, vido, templateProps });
  4764. }
  4765. /**
  4766. * List component
  4767. *
  4768. * @copyright Rafal Pospiech <https://neuronet.io>
  4769. * @author Rafal Pospiech <neuronet.io@gmail.com>
  4770. * @package gantt-schedule-timeline-calendar
  4771. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  4772. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  4773. */
  4774. function List(vido, props = {}) {
  4775. const { api, state, onDestroy, Actions, update, reuseComponents, html, schedule, StyleMap, cache } = vido;
  4776. const componentName = 'list';
  4777. const componentActions = api.getActions(componentName);
  4778. let wrapper;
  4779. onDestroy(state.subscribe('config.wrappers.List', value => (wrapper = value)));
  4780. let ListColumnComponent;
  4781. const listColumnUnsub = state.subscribe('config.components.ListColumn', value => (ListColumnComponent = value));
  4782. function renderExpanderIcons() {
  4783. const icons = state.get('config.list.expander.icons');
  4784. const rendered = {};
  4785. for (const iconName in icons) {
  4786. const html = icons[iconName];
  4787. rendered[iconName] = api.getSVGIconSrc(html);
  4788. }
  4789. state.update('_internal.list.expander.icons', rendered);
  4790. }
  4791. renderExpanderIcons();
  4792. function renderToggleIcons() {
  4793. const toggleIconsSrc = {
  4794. open: '',
  4795. close: ''
  4796. };
  4797. const icons = state.get('config.list.toggle.icons');
  4798. for (const iconName in icons) {
  4799. const html = icons[iconName];
  4800. toggleIconsSrc[iconName] = api.getSVGIconSrc(html);
  4801. }
  4802. state.update('_internal.list.toggle.icons', toggleIconsSrc);
  4803. }
  4804. renderToggleIcons();
  4805. let className;
  4806. let list, percent;
  4807. function onListChange() {
  4808. list = state.get('config.list');
  4809. percent = list.columns.percent;
  4810. update();
  4811. }
  4812. onDestroy(state.subscribe('config.list', onListChange));
  4813. onDestroy(state.subscribe('config.classNames', () => {
  4814. className = api.getClass(componentName, { list });
  4815. update();
  4816. }));
  4817. let listColumns = [];
  4818. function onListColumnsDataChange(data) {
  4819. const destroy = reuseComponents(listColumns, Object.values(data), column => ({ columnId: column.id }), ListColumnComponent);
  4820. update();
  4821. return destroy;
  4822. }
  4823. onDestroy(state.subscribe('config.list.columns.data;', onListColumnsDataChange));
  4824. const styleMap = new StyleMap({
  4825. height: '',
  4826. '--expander-padding-width': '',
  4827. '--expander-size': ''
  4828. });
  4829. onDestroy(state.subscribeAll(['config.height', 'config.list.expander'], bulk => {
  4830. const expander = state.get('config.list.expander');
  4831. styleMap.style['height'] = state.get('config.height') + 'px';
  4832. styleMap.style['--expander-padding-width'] = expander.padding + 'px';
  4833. styleMap.style['--expander-size'] = expander.size + 'px';
  4834. update();
  4835. }));
  4836. onDestroy(() => {
  4837. listColumns.forEach(listColumn => listColumn.destroy());
  4838. listColumnUnsub();
  4839. });
  4840. function onScroll(event) {
  4841. event.stopPropagation();
  4842. event.preventDefault();
  4843. if (event.type === 'scroll') {
  4844. state.update('config.scroll.top', event.target.scrollTop);
  4845. }
  4846. else {
  4847. const wheel = api.normalizeMouseWheelEvent(event);
  4848. state.update('config.scroll.top', top => {
  4849. const rowsHeight = state.get('_internal.list.rowsHeight');
  4850. const internalHeight = state.get('_internal.height');
  4851. return api.limitScrollTop(rowsHeight, internalHeight, (top += wheel.y * state.get('config.scroll.yMultiplier')));
  4852. });
  4853. }
  4854. }
  4855. let width;
  4856. function getWidth(element) {
  4857. if (!width) {
  4858. width = element.clientWidth;
  4859. if (percent === 0) {
  4860. width = 0;
  4861. }
  4862. state.update('_internal.list.width', width);
  4863. }
  4864. }
  4865. class ListAction {
  4866. constructor(element, data) {
  4867. data.state.update('_internal.elements.list', element);
  4868. getWidth(element);
  4869. }
  4870. update(element) {
  4871. return getWidth(element);
  4872. }
  4873. }
  4874. componentActions.push(ListAction);
  4875. const actions = Actions.create(componentActions, Object.assign(Object.assign({}, props), { api, state }));
  4876. return templateProps => wrapper(cache(list.columns.percent > 0
  4877. ? html `
  4878. <div class=${className} data-actions=${actions} style=${styleMap} @scroll=${onScroll} @wheel=${onScroll}>
  4879. ${listColumns.map(c => c.html())}
  4880. </div>
  4881. `
  4882. : ''), { vido, props: {}, templateProps });
  4883. }
  4884. /**
  4885. * ListColumn component
  4886. *
  4887. * @copyright Rafal Pospiech <https://neuronet.io>
  4888. * @author Rafal Pospiech <neuronet.io@gmail.com>
  4889. * @package gantt-schedule-timeline-calendar
  4890. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  4891. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  4892. */
  4893. /**
  4894. * Bind element action
  4895. */
  4896. class BindElementAction {
  4897. constructor(element, data) {
  4898. let shouldUpdate = false;
  4899. let elements = data.state.get('_internal.elements.list-columns');
  4900. if (typeof elements === 'undefined') {
  4901. elements = [];
  4902. shouldUpdate = true;
  4903. }
  4904. if (!elements.includes(element)) {
  4905. elements.push(element);
  4906. shouldUpdate = true;
  4907. }
  4908. if (shouldUpdate)
  4909. data.state.update('_internal.elements.list-columns', elements);
  4910. }
  4911. destroy(element, data) {
  4912. data.state.update('_internal.elements.list-columns', elements => {
  4913. return elements.filter(el => el !== element);
  4914. });
  4915. }
  4916. }
  4917. function ListColumn(vido, props) {
  4918. const { api, state, onDestroy, onChange, Actions, update, createComponent, reuseComponents, html, StyleMap } = vido;
  4919. let wrapper;
  4920. onDestroy(state.subscribe('config.wrappers.ListColumn', value => (wrapper = value)));
  4921. const componentsSub = [];
  4922. let ListColumnRowComponent;
  4923. componentsSub.push(state.subscribe('config.components.ListColumnRow', value => (ListColumnRowComponent = value)));
  4924. let ListColumnHeaderComponent;
  4925. componentsSub.push(state.subscribe('config.components.ListColumnHeader', value => (ListColumnHeaderComponent = value)));
  4926. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  4927. const componentName = 'list-column';
  4928. const rowsComponentName = componentName + '-rows';
  4929. const componentActions = api.getActions(componentName);
  4930. const rowsActions = api.getActions(rowsComponentName);
  4931. let className, classNameContainer, calculatedWidth;
  4932. const widthStyleMap = new StyleMap({ width: '', '--width': '' });
  4933. const containerStyleMap = new StyleMap({ width: '', height: '' });
  4934. const scrollCompensationStyleMap = new StyleMap({ width: '', height: '' });
  4935. let column, columnPath = `config.list.columns.data.${props.columnId}`;
  4936. let columnSub = state.subscribe(columnPath, function columnChanged(val) {
  4937. column = val;
  4938. update();
  4939. });
  4940. let width;
  4941. function calculateStyle() {
  4942. const list = state.get('config.list');
  4943. const compensationY = state.get('config.scroll.compensation.y');
  4944. calculatedWidth = list.columns.data[column.id].width * list.columns.percent * 0.01;
  4945. width = calculatedWidth;
  4946. const height = state.get('_internal.height');
  4947. widthStyleMap.style.width = width + 'px';
  4948. widthStyleMap.style['--width'] = width + 'px';
  4949. containerStyleMap.style.height = height + 'px';
  4950. scrollCompensationStyleMap.style.height = height + Math.abs(compensationY) + 'px';
  4951. scrollCompensationStyleMap.style.transform = `translate(0px, ${compensationY}px)`;
  4952. }
  4953. let styleSub = state.subscribeAll([
  4954. 'config.list.columns.percent',
  4955. 'config.list.columns.resizer.width',
  4956. `config.list.columns.data.${column.id}.width`,
  4957. '_internal.chart.dimensions.width',
  4958. '_internal.height',
  4959. 'config.scroll.compensation.y',
  4960. '_internal.list.width'
  4961. ], calculateStyle, { bulk: true });
  4962. const ListColumnHeader = createComponent(ListColumnHeaderComponent, { columnId: props.columnId });
  4963. onDestroy(ListColumnHeader.destroy);
  4964. onChange(changedProps => {
  4965. props = changedProps;
  4966. for (const prop in props) {
  4967. actionProps[prop] = props[prop];
  4968. }
  4969. if (columnSub)
  4970. columnSub();
  4971. ListColumnHeader.change({ columnId: props.columnId });
  4972. columnPath = `config.list.columns.data.${props.columnId}`;
  4973. columnSub = state.subscribe(columnPath, function columnChanged(val) {
  4974. column = val;
  4975. update();
  4976. });
  4977. if (styleSub)
  4978. styleSub();
  4979. styleSub = state.subscribeAll([
  4980. 'config.list.columns.percent',
  4981. 'config.list.columns.resizer.width',
  4982. `config.list.columns.data.${column.id}.width`,
  4983. '_internal.chart.dimensions.width',
  4984. '_internal.height',
  4985. 'config.scroll.compensation.y',
  4986. '_internal.list.width'
  4987. ], calculateStyle, { bulk: true });
  4988. ListColumnHeader.change(props);
  4989. });
  4990. onDestroy(() => {
  4991. columnSub();
  4992. styleSub();
  4993. });
  4994. onDestroy(state.subscribe('config.classNames', value => {
  4995. className = api.getClass(componentName);
  4996. classNameContainer = api.getClass(rowsComponentName);
  4997. update();
  4998. }));
  4999. const visibleRows = [];
  5000. const visibleRowsChange = val => {
  5001. const destroy = reuseComponents(visibleRows, val || [], row => row && { columnId: props.columnId, rowId: row.id, width }, ListColumnRowComponent);
  5002. update();
  5003. return destroy;
  5004. };
  5005. onDestroy(state.subscribe('_internal.list.visibleRows;', visibleRowsChange));
  5006. onDestroy(() => {
  5007. visibleRows.forEach(row => row.destroy());
  5008. componentsSub.forEach(unsub => unsub());
  5009. });
  5010. function getRowHtml(row) {
  5011. return row.html();
  5012. }
  5013. componentActions.push(BindElementAction);
  5014. const headerActions = Actions.create(componentActions, { column, state: state, api: api });
  5015. const rowActions = Actions.create(rowsActions, { api, state });
  5016. return templateProps => wrapper(html `
  5017. <div class=${className} data-actions=${headerActions} style=${widthStyleMap}>
  5018. ${ListColumnHeader.html()}
  5019. <div class=${classNameContainer} style=${containerStyleMap} data-actions=${rowActions}>
  5020. <div class=${classNameContainer + '--scroll-compensation'} style=${scrollCompensationStyleMap}>
  5021. ${visibleRows.map(getRowHtml)}
  5022. </div>
  5023. </div>
  5024. </div>
  5025. `, { vido, props, templateProps });
  5026. }
  5027. /**
  5028. * ListColumnHeader component
  5029. *
  5030. * @copyright Rafal Pospiech <https://neuronet.io>
  5031. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5032. * @package gantt-schedule-timeline-calendar
  5033. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5034. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5035. */
  5036. function ListColumnHeader(vido, props) {
  5037. const { api, state, onDestroy, onChange, Actions, update, createComponent, html, cache, StyleMap } = vido;
  5038. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  5039. let wrapper;
  5040. onDestroy(state.subscribe('config.wrappers.ListColumnHeader', value => (wrapper = value)));
  5041. const componentName = 'list-column-header';
  5042. const componentActions = api.getActions(componentName);
  5043. const componentsSubs = [];
  5044. let ListColumnHeaderResizerComponent;
  5045. componentsSubs.push(state.subscribe('config.components.ListColumnHeaderResizer', value => (ListColumnHeaderResizerComponent = value)));
  5046. const ListColumnHeaderResizer = createComponent(ListColumnHeaderResizerComponent, { columnId: props.columnId });
  5047. let ListColumnRowExpanderComponent;
  5048. componentsSubs.push(state.subscribe('config.components.ListColumnRowExpander', value => (ListColumnRowExpanderComponent = value)));
  5049. const ListColumnRowExpander = createComponent(ListColumnRowExpanderComponent, {});
  5050. onDestroy(() => {
  5051. ListColumnHeaderResizer.destroy();
  5052. ListColumnRowExpander.destroy();
  5053. componentsSubs.forEach(unsub => unsub());
  5054. });
  5055. let column;
  5056. let columnSub = state.subscribe(`config.list.columns.data.${props.columnId}`, val => {
  5057. column = val;
  5058. update();
  5059. });
  5060. onDestroy(columnSub);
  5061. onChange(changedProps => {
  5062. props = changedProps;
  5063. for (const prop in props) {
  5064. actionProps[prop] = props[prop];
  5065. }
  5066. if (columnSub)
  5067. columnSub();
  5068. columnSub = state.subscribe(`config.list.columns.data.${props.columnId}`, val => {
  5069. column = val;
  5070. update();
  5071. });
  5072. });
  5073. let className, contentClass;
  5074. onDestroy(state.subscribe('config.classNames', () => {
  5075. className = api.getClass(componentName);
  5076. contentClass = api.getClass(componentName + '-content');
  5077. }));
  5078. const styleMap = new StyleMap({
  5079. height: '',
  5080. '--height': '',
  5081. '--paddings-count': ''
  5082. });
  5083. onDestroy(state.subscribe('config.headerHeight', () => {
  5084. const value = state.get('config');
  5085. styleMap.style['height'] = value.headerHeight + 'px';
  5086. styleMap.style['--height'] = value.headerHeight + 'px';
  5087. styleMap.style['--paddings-count'] = '1';
  5088. update();
  5089. }));
  5090. function withExpander() {
  5091. return html `
  5092. <div class=${contentClass}>
  5093. ${ListColumnRowExpander.html()}${ListColumnHeaderResizer.html(column)}
  5094. </div>
  5095. `;
  5096. }
  5097. function withoutExpander() {
  5098. return html `
  5099. <div class=${contentClass}>
  5100. ${ListColumnHeaderResizer.html(column)}
  5101. </div>
  5102. `;
  5103. }
  5104. const actions = Actions.create(componentActions, actionProps);
  5105. return templateProps => wrapper(html `
  5106. <div class=${className} style=${styleMap} data-actions=${actions}>
  5107. ${cache(column.expander ? withExpander() : withoutExpander())}
  5108. </div>
  5109. `, { vido, props, templateProps });
  5110. }
  5111. /**
  5112. * ListColumnHeaderResizer component
  5113. *
  5114. * @copyright Rafal Pospiech <https://neuronet.io>
  5115. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5116. * @package gantt-schedule-timeline-calendar
  5117. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5118. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5119. */
  5120. function ListColumnHeaderResizer(vido, props) {
  5121. const { api, state, onDestroy, update, html, schedule, Actions, PointerAction, cache, StyleMap } = vido;
  5122. const componentName = 'list-column-header-resizer';
  5123. const componentActions = api.getActions(componentName);
  5124. const componentDotsActions = api.getActions(componentName + '-dots');
  5125. let wrapper;
  5126. onDestroy(state.subscribe('config.wrappers.ListColumnHeaderResizer', value => (wrapper = value)));
  5127. let column;
  5128. onDestroy(state.subscribe(`config.list.columns.data.${props.columnId}`, val => {
  5129. column = val;
  5130. update();
  5131. }));
  5132. let className, containerClass, dotsClass, dotClass, calculatedWidth;
  5133. const dotsStyleMap = new StyleMap({ width: '' });
  5134. let inRealTime = false;
  5135. onDestroy(state.subscribe('config.classNames', value => {
  5136. className = api.getClass(componentName, { column });
  5137. containerClass = api.getClass(componentName + '-container', { column });
  5138. dotsClass = api.getClass(componentName + '-dots', { column });
  5139. dotClass = api.getClass(componentName + '-dots-dot', { column });
  5140. update();
  5141. }));
  5142. onDestroy(state.subscribeAll([
  5143. `config.list.columns.data.${column.id}.width`,
  5144. 'config.list.columns.percent',
  5145. 'config.list.columns.resizer.width',
  5146. 'config.list.columns.resizer.inRealTime'
  5147. ], (value, path) => {
  5148. const list = state.get('config.list');
  5149. calculatedWidth = column.width * list.columns.percent * 0.01;
  5150. dotsStyleMap.style['--width'] = list.columns.resizer.width + 'px';
  5151. inRealTime = list.columns.resizer.inRealTime;
  5152. state.update('_internal.list.width', calculatedWidth);
  5153. update();
  5154. }));
  5155. let dots = [1, 2, 3, 4, 5, 6, 7, 8];
  5156. onDestroy(state.subscribe('config.list.columns.resizer.dots', value => {
  5157. dots = [];
  5158. for (let i = 0; i < value; i++) {
  5159. dots.push(i);
  5160. }
  5161. update();
  5162. }));
  5163. /*
  5164. let isMoving = false;
  5165. const lineStyleMap = new StyleMap({
  5166. '--display': 'none',
  5167. '--left': left + 'px'
  5168. });*/
  5169. let left = calculatedWidth;
  5170. const columnWidthPath = `config.list.columns.data.${column.id}.width`;
  5171. const actionProps = {
  5172. column,
  5173. api,
  5174. state,
  5175. pointerOptions: {
  5176. axis: 'x',
  5177. onMove: function onMove({ movementX }) {
  5178. let minWidth = state.get('config.list.columns.minWidth');
  5179. if (typeof column.minWidth === 'number') {
  5180. minWidth = column.minWidth;
  5181. }
  5182. left += movementX;
  5183. if (left < minWidth) {
  5184. left = minWidth;
  5185. }
  5186. if (inRealTime) {
  5187. state.update(columnWidthPath, left);
  5188. }
  5189. }
  5190. }
  5191. };
  5192. componentActions.push(PointerAction);
  5193. const actions = Actions.create(componentActions, actionProps);
  5194. const dotsActions = Actions.create(componentDotsActions, actionProps);
  5195. return templateProps => wrapper(html `
  5196. <div class=${className} data-actions=${actions}>
  5197. <div class=${containerClass}>
  5198. ${cache(column.header.html
  5199. ? html `
  5200. ${column.header.html}
  5201. `
  5202. : column.header.content)}
  5203. </div>
  5204. <div class=${dotsClass} style=${dotsStyleMap} data-actions=${dotsActions}>
  5205. ${dots.map(dot => html `
  5206. <div class=${dotClass} />
  5207. `)}
  5208. </div>
  5209. </div>
  5210. `, { vido, props, templateProps });
  5211. }
  5212. /**
  5213. * ListColumnRow component
  5214. *
  5215. * @copyright Rafal Pospiech <https://neuronet.io>
  5216. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5217. * @package gantt-schedule-timeline-calendar
  5218. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5219. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5220. */
  5221. /**
  5222. * Bind element action
  5223. */
  5224. class BindElementAction$1 {
  5225. constructor(element, data) {
  5226. let elements = data.state.get('_internal.elements.list-column-rows');
  5227. let shouldUpdate = false;
  5228. if (typeof elements === 'undefined') {
  5229. shouldUpdate = true;
  5230. elements = [];
  5231. }
  5232. if (!elements.includes(element)) {
  5233. elements.push(element);
  5234. shouldUpdate = true;
  5235. }
  5236. if (shouldUpdate)
  5237. data.state.update('_internal.elements.list-column-rows', elements);
  5238. }
  5239. destroy(element, data) {
  5240. data.state.update('_internal.elements.list-column-rows', elements => {
  5241. return elements.filter(el => el !== element);
  5242. });
  5243. }
  5244. }
  5245. function ListColumnRow(vido, props) {
  5246. const { api, state, onDestroy, Detach, Actions, update, html, createComponent, onChange, StyleMap, unsafeHTML, PointerAction } = vido;
  5247. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  5248. let shouldDetach = false;
  5249. const detach = new Detach(() => shouldDetach);
  5250. let wrapper;
  5251. onDestroy(state.subscribe('config.wrappers.ListColumnRow', value => (wrapper = value)));
  5252. let ListColumnRowExpanderComponent;
  5253. onDestroy(state.subscribe('config.components.ListColumnRowExpander', value => (ListColumnRowExpanderComponent = value)));
  5254. let rowPath = `_internal.flatTreeMapById.${props.rowId}`, row = state.get(rowPath);
  5255. let colPath = `config.list.columns.data.${props.columnId}`, column = state.get(colPath);
  5256. const styleMap = new StyleMap(column.expander
  5257. ? {
  5258. height: '',
  5259. top: '',
  5260. '--height': '',
  5261. '--expander-padding-width': '',
  5262. '--expander-size': ''
  5263. }
  5264. : {
  5265. height: '',
  5266. top: '',
  5267. '--height': ''
  5268. }, true);
  5269. let rowSub, colSub;
  5270. const ListColumnRowExpander = createComponent(ListColumnRowExpanderComponent, { row });
  5271. const onPropsChange = (changedProps, options) => {
  5272. if (options.leave || changedProps.rowId === undefined || changedProps.columnId === undefined) {
  5273. shouldDetach = true;
  5274. if (rowSub)
  5275. rowSub();
  5276. if (colSub)
  5277. colSub();
  5278. update();
  5279. return;
  5280. }
  5281. shouldDetach = false;
  5282. props = changedProps;
  5283. for (const prop in props) {
  5284. actionProps[prop] = props[prop];
  5285. }
  5286. const rowId = props.rowId;
  5287. const columnId = props.columnId;
  5288. if (rowSub)
  5289. rowSub();
  5290. if (colSub)
  5291. colSub();
  5292. rowPath = `_internal.flatTreeMapById.${rowId}`;
  5293. colPath = `config.list.columns.data.${columnId}`;
  5294. rowSub = state.subscribeAll([rowPath, colPath, 'config.list.expander'], bulk => {
  5295. column = state.get(colPath);
  5296. row = state.get(rowPath);
  5297. if (column === undefined || row === undefined) {
  5298. shouldDetach = true;
  5299. update();
  5300. return;
  5301. }
  5302. if (column === undefined || row === undefined)
  5303. return;
  5304. const expander = state.get('config.list.expander');
  5305. // @ts-ignore
  5306. styleMap.setStyle({}); // we must reset style because of user specified styling
  5307. styleMap.style['height'] = row.height + 'px';
  5308. styleMap.style['--height'] = row.height + 'px';
  5309. if (column.expander) {
  5310. styleMap.style['--expander-padding-width'] = expander.padding * (row._internal.parents.length + 1) + 'px';
  5311. }
  5312. for (const parentId of row._internal.parents) {
  5313. const parent = state.get(`_internal.flatTreeMapById.${parentId}`);
  5314. if (typeof parent.style === 'object' && parent.style.constructor.name === 'Object') {
  5315. if (typeof parent.style.children === 'object') {
  5316. const childrenStyle = parent.style.children;
  5317. for (const name in childrenStyle) {
  5318. styleMap.style[name] = childrenStyle[name];
  5319. }
  5320. }
  5321. }
  5322. }
  5323. if (typeof row.style === 'object' &&
  5324. row.style.constructor.name === 'Object' &&
  5325. typeof row.style.current === 'object') {
  5326. const rowCurrentStyle = row.style.current;
  5327. for (const name in rowCurrentStyle) {
  5328. styleMap.style[name] = rowCurrentStyle[name];
  5329. }
  5330. }
  5331. update();
  5332. }, { bulk: true });
  5333. if (ListColumnRowExpander) {
  5334. ListColumnRowExpander.change({ row });
  5335. }
  5336. colSub = state.subscribe(colPath, val => {
  5337. column = val;
  5338. update();
  5339. });
  5340. };
  5341. onChange(onPropsChange);
  5342. onDestroy(() => {
  5343. if (ListColumnRowExpander)
  5344. ListColumnRowExpander.destroy();
  5345. colSub();
  5346. rowSub();
  5347. });
  5348. const componentName = 'list-column-row';
  5349. const componentActions = api.getActions(componentName);
  5350. let className;
  5351. onDestroy(state.subscribe('config.classNames', value => {
  5352. className = api.getClass(componentName);
  5353. update();
  5354. }));
  5355. function getHtml() {
  5356. if (row === undefined)
  5357. return null;
  5358. if (typeof column.data === 'function')
  5359. return unsafeHTML(column.data(row));
  5360. return unsafeHTML(row[column.data]);
  5361. }
  5362. function getText() {
  5363. if (row === undefined)
  5364. return null;
  5365. if (typeof column.data === 'function')
  5366. return column.data(row);
  5367. return row[column.data];
  5368. }
  5369. if (!componentActions.includes(BindElementAction$1))
  5370. componentActions.push(BindElementAction$1);
  5371. actionProps.pointerOptions = {
  5372. axis: 'x|y',
  5373. onMove({ event, movementX, movementY }) {
  5374. event.stopPropagation();
  5375. event.preventDefault();
  5376. if (movementX) {
  5377. state.update('config.list.columns.percent', percent => {
  5378. percent += movementX * state.get('config.scroll.xMultiplier');
  5379. if (percent < 0)
  5380. percent = 0;
  5381. if (percent > 100)
  5382. percent = 100;
  5383. return percent;
  5384. });
  5385. }
  5386. else if (movementY) {
  5387. state.update('config.scroll.top', top => {
  5388. top -= movementY * state.get('config.scroll.yMultiplier');
  5389. const rowsHeight = state.get('_internal.list.rowsHeight');
  5390. const internalHeight = state.get('_internal.height');
  5391. top = api.limitScrollTop(rowsHeight, internalHeight, top);
  5392. return top;
  5393. });
  5394. }
  5395. }
  5396. };
  5397. componentActions.push(PointerAction);
  5398. const actions = Actions.create(componentActions, actionProps);
  5399. return templateProps => wrapper(html `
  5400. <div detach=${detach} class=${className} style=${styleMap} data-actions=${actions}>
  5401. ${column.expander ? ListColumnRowExpander.html() : null}
  5402. <div class=${className + '-content'}>
  5403. ${column.isHTML ? getHtml() : getText()}
  5404. </div>
  5405. </div>
  5406. `, { vido, props, templateProps });
  5407. }
  5408. /**
  5409. * ListColumnRowExpander component
  5410. *
  5411. * @copyright Rafal Pospiech <https://neuronet.io>
  5412. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5413. * @package gantt-schedule-timeline-calendar
  5414. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5415. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5416. */
  5417. function ListColumnRowExpander(vido, props) {
  5418. const { api, state, onDestroy, Actions, update, html, createComponent, onChange } = vido;
  5419. const componentName = 'list-column-row-expander';
  5420. const componentActions = api.getActions(componentName);
  5421. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  5422. let className;
  5423. let ListColumnRowExpanderToggleComponent;
  5424. const toggleUnsub = state.subscribe('config.components.ListColumnRowExpanderToggle', value => (ListColumnRowExpanderToggleComponent = value));
  5425. const ListColumnRowExpanderToggle = createComponent(ListColumnRowExpanderToggleComponent, props.row ? { row: props.row } : {});
  5426. onDestroy(() => {
  5427. ListColumnRowExpanderToggle.destroy();
  5428. toggleUnsub();
  5429. });
  5430. let wrapper;
  5431. onDestroy(state.subscribe('config.wrappers.ListColumnRowExpander', value => (wrapper = value)));
  5432. onDestroy(state.subscribe('config.classNames', value => {
  5433. className = api.getClass(componentName);
  5434. update();
  5435. }));
  5436. if (props.row) {
  5437. function onPropsChange(changedProps) {
  5438. props = changedProps;
  5439. for (const prop in props) {
  5440. actionProps[prop] = props[prop];
  5441. }
  5442. ListColumnRowExpanderToggle.change(props);
  5443. }
  5444. onChange(onPropsChange);
  5445. }
  5446. const actions = Actions.create(componentActions, actionProps);
  5447. return templateProps => wrapper(html `
  5448. <div class=${className} data-action=${actions}>
  5449. ${ListColumnRowExpanderToggle.html()}
  5450. </div>
  5451. `, { vido, props, templateProps });
  5452. }
  5453. /**
  5454. * ListColumnRowExpanderToggle component
  5455. *
  5456. * @copyright Rafal Pospiech <https://neuronet.io>
  5457. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5458. * @package gantt-schedule-timeline-calendar
  5459. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5460. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5461. */
  5462. function ListColumnRowExpanderToggle(vido, props) {
  5463. const { api, state, onDestroy, Actions, update, html, onChange, cache } = vido;
  5464. const componentName = 'list-column-row-expander-toggle';
  5465. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  5466. let wrapper;
  5467. onDestroy(state.subscribe('config.wrappers.ListColumnRowExpanderToggle', value => (wrapper = value)));
  5468. const componentActions = api.getActions(componentName);
  5469. let className, classNameChild, classNameOpen, classNameClosed;
  5470. let expanded = false;
  5471. let iconChild, iconOpen, iconClosed;
  5472. onDestroy(state.subscribe('config.classNames', value => {
  5473. className = api.getClass(componentName);
  5474. classNameChild = className + '-child';
  5475. classNameOpen = className + '-open';
  5476. classNameClosed = className + '-closed';
  5477. update();
  5478. }));
  5479. onDestroy(state.subscribe('_internal.list.expander.icons', icons => {
  5480. if (icons) {
  5481. iconChild = icons.child;
  5482. iconOpen = icons.open;
  5483. iconClosed = icons.closed;
  5484. }
  5485. update();
  5486. }));
  5487. if (props.row) {
  5488. function expandedChange(isExpanded) {
  5489. expanded = isExpanded;
  5490. update();
  5491. }
  5492. let expandedSub;
  5493. function onPropsChange(changedProps) {
  5494. var _a, _b;
  5495. props = changedProps;
  5496. for (const prop in props) {
  5497. actionProps[prop] = props[prop];
  5498. }
  5499. if (expandedSub)
  5500. expandedSub();
  5501. if ((_b = (_a = props) === null || _a === void 0 ? void 0 : _a.row) === null || _b === void 0 ? void 0 : _b.id)
  5502. expandedSub = state.subscribe(`config.list.rows.${props.row.id}.expanded`, expandedChange);
  5503. }
  5504. onChange(onPropsChange);
  5505. onDestroy(function listToggleDestroy() {
  5506. if (expandedSub)
  5507. expandedSub();
  5508. });
  5509. }
  5510. else {
  5511. function expandedChange(bulk) {
  5512. for (const rowExpanded of bulk) {
  5513. if (rowExpanded.value) {
  5514. expanded = true;
  5515. break;
  5516. }
  5517. }
  5518. update();
  5519. }
  5520. onDestroy(state.subscribe('config.list.rows.*.expanded', expandedChange, { bulk: true }));
  5521. }
  5522. function toggle() {
  5523. expanded = !expanded;
  5524. if (props.row) {
  5525. state.update(`config.list.rows.${props.row.id}.expanded`, expanded);
  5526. }
  5527. else {
  5528. state.update(`config.list.rows`, rows => {
  5529. for (const rowId in rows) {
  5530. rows[rowId].expanded = expanded;
  5531. }
  5532. return rows;
  5533. }, { only: ['*.expanded'] });
  5534. }
  5535. }
  5536. const getIcon = () => {
  5537. var _a, _b, _c;
  5538. if (iconChild) {
  5539. if (((_c = (_b = (_a = props.row) === null || _a === void 0 ? void 0 : _a._internal) === null || _b === void 0 ? void 0 : _b.children) === null || _c === void 0 ? void 0 : _c.length) === 0) {
  5540. return html `
  5541. <img width="16" height="16" class=${classNameChild} src=${iconChild} />
  5542. `;
  5543. }
  5544. return expanded
  5545. ? html `
  5546. <img width="16" height="16" class=${classNameOpen} src=${iconOpen} />
  5547. `
  5548. : html `
  5549. <img width="16" height="16" class=${classNameClosed} src=${iconClosed} />
  5550. `;
  5551. }
  5552. return '';
  5553. };
  5554. const actions = Actions.create(componentActions, actionProps);
  5555. return templateProps => wrapper(html `
  5556. <div class=${className} data-action=${actions} @click=${toggle}>
  5557. ${cache(getIcon())}
  5558. </div>
  5559. `, { vido, props, templateProps });
  5560. }
  5561. /**
  5562. * ListToggle component
  5563. *
  5564. * @copyright Rafal Pospiech <https://neuronet.io>
  5565. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5566. * @package gantt-schedule-timeline-calendar
  5567. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5568. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5569. */
  5570. function ListToggle(vido, props = {}) {
  5571. const { html, onDestroy, api, state, update } = vido;
  5572. const componentName = 'list-toggle';
  5573. let className;
  5574. onDestroy(state.subscribe('config.classNames', classNames => {
  5575. className = api.getClass(componentName);
  5576. }));
  5577. let wrapper;
  5578. onDestroy(state.subscribe('config.wrappers.ListToggle', ListToggleWrapper => (wrapper = ListToggleWrapper)));
  5579. let toggleIconsSrc = {
  5580. open: '',
  5581. close: ''
  5582. };
  5583. onDestroy(state.subscribe('_internal.list.toggle.icons', value => {
  5584. if (value) {
  5585. toggleIconsSrc = value;
  5586. update();
  5587. }
  5588. }));
  5589. let open = true;
  5590. onDestroy(state.subscribe('config.list.columns.percent', percent => (percent === 0 ? (open = false) : (open = true))));
  5591. function toggle(ev) {
  5592. state.update('config.list.columns.percent', percent => {
  5593. return percent === 0 ? 100 : 0;
  5594. });
  5595. }
  5596. return templateProps => wrapper(html `
  5597. <div class=${className} @click=${toggle}><img src=${open ? toggleIconsSrc.close : toggleIconsSrc.open} /></div>
  5598. `, { props, vido, templateProps });
  5599. }
  5600. /**
  5601. * Chart component
  5602. *
  5603. * @copyright Rafal Pospiech <https://neuronet.io>
  5604. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5605. * @package gantt-schedule-timeline-calendar
  5606. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5607. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5608. */
  5609. function Chart(vido, props = {}) {
  5610. const { api, state, onDestroy, Actions, update, html, StyleMap, createComponent } = vido;
  5611. const componentName = 'chart';
  5612. const ChartCalendarComponent = state.get('config.components.ChartCalendar');
  5613. const ChartTimelineComponent = state.get('config.components.ChartTimeline');
  5614. let wrapper;
  5615. onDestroy(state.subscribe('config.wrappers.Chart', value => (wrapper = value)));
  5616. const Calendar = createComponent(ChartCalendarComponent);
  5617. onDestroy(Calendar.destroy);
  5618. const Timeline = createComponent(ChartTimelineComponent);
  5619. onDestroy(Timeline.destroy);
  5620. let className, classNameScroll, classNameScrollInner, scrollElement, scrollInnerElement;
  5621. const componentActions = api.getActions(componentName);
  5622. onDestroy(state.subscribe('config.classNames', value => {
  5623. className = api.getClass(componentName);
  5624. classNameScroll = api.getClass('horizontal-scroll');
  5625. classNameScrollInner = api.getClass('horizontal-scroll-inner');
  5626. update();
  5627. }));
  5628. onDestroy(state.subscribeAll(['_internal.chart.dimensions.width', '_internal.chart.time.totalViewDurationPx'], function horizontalScroll() {
  5629. if (scrollElement)
  5630. scrollElement.style.width = state.get('_internal.chart.dimensions.width') + 'px';
  5631. if (scrollInnerElement)
  5632. scrollInnerElement.style.width = state.get('_internal.chart.time.totalViewDurationPx') + 'px';
  5633. }));
  5634. onDestroy(state.subscribe('config.scroll.left', left => {
  5635. if (scrollElement) {
  5636. scrollElement.scrollLeft = left;
  5637. }
  5638. }));
  5639. function onScrollHandler(event) {
  5640. if (event.type === 'scroll') {
  5641. // @ts-ignore
  5642. const left = event.target.scrollLeft;
  5643. state.update('config.scroll.left', left);
  5644. }
  5645. }
  5646. const onScroll = {
  5647. handleEvent: onScrollHandler,
  5648. passive: true,
  5649. capture: false
  5650. };
  5651. function onWheelHandler(event) {
  5652. if (event.type === 'wheel') {
  5653. const wheel = api.normalizeMouseWheelEvent(event);
  5654. const xMultiplier = state.get('config.scroll.xMultiplier');
  5655. const yMultiplier = state.get('config.scroll.yMultiplier');
  5656. const currentScrollLeft = state.get('config.scroll.left');
  5657. const totalViewDurationPx = state.get('_internal.chart.time.totalViewDurationPx');
  5658. if (event.shiftKey && wheel.y) {
  5659. const newScrollLeft = api.limitScrollLeft(totalViewDurationPx, chartWidth, currentScrollLeft + wheel.y * xMultiplier);
  5660. state.update('config.scroll.left', newScrollLeft); // will trigger scrollbar to move which will trigger scroll event
  5661. }
  5662. else if (event.ctrlKey && wheel.y) {
  5663. event.preventDefault();
  5664. state.update('config.chart.time.zoom', currentZoom => {
  5665. if (wheel.y < 0) {
  5666. return currentZoom - 1;
  5667. }
  5668. return currentZoom + 1;
  5669. });
  5670. }
  5671. else if (wheel.x) {
  5672. const currentScrollLeft = state.get('config.scroll.left');
  5673. state.update('config.scroll.left', api.limitScrollLeft(totalViewDurationPx, chartWidth, currentScrollLeft + wheel.x * xMultiplier));
  5674. }
  5675. else {
  5676. state.update('config.scroll.top', top => {
  5677. const rowsHeight = state.get('_internal.list.rowsHeight');
  5678. const internalHeight = state.get('_internal.height');
  5679. return api.limitScrollTop(rowsHeight, internalHeight, (top += wheel.y * yMultiplier));
  5680. });
  5681. }
  5682. }
  5683. }
  5684. const onWheel = {
  5685. handleEvent: onWheelHandler,
  5686. passive: false,
  5687. capture: false
  5688. };
  5689. function bindElement(element) {
  5690. if (!scrollElement) {
  5691. scrollElement = element;
  5692. state.update('_internal.elements.horizontal-scroll', element);
  5693. }
  5694. }
  5695. function bindInnerScroll(element) {
  5696. scrollInnerElement = element;
  5697. const old = state.get('_internal.elements.horizontal-scroll-inner');
  5698. if (old !== element)
  5699. state.update('_internal.elements.horizontal-scroll-inner', element);
  5700. if (!state.get('_internal.loaded.horizontal-scroll-inner'))
  5701. state.update('_internal.loaded.horizontal-scroll-inner', true);
  5702. }
  5703. let chartWidth = 0;
  5704. let ro;
  5705. componentActions.push(function bindElement(element) {
  5706. if (!ro) {
  5707. ro = new index((entries, observer) => {
  5708. const width = element.clientWidth;
  5709. const height = element.clientHeight;
  5710. const innerWidth = width - state.get('_internal.scrollBarHeight');
  5711. if (chartWidth !== width) {
  5712. chartWidth = width;
  5713. state.update('_internal.chart.dimensions', { width, innerWidth, height });
  5714. }
  5715. });
  5716. ro.observe(element);
  5717. state.update('_internal.elements.chart', element);
  5718. state.update('_internal.loaded.chart', true);
  5719. }
  5720. });
  5721. onDestroy(() => {
  5722. ro.disconnect();
  5723. });
  5724. const actions = Actions.create(componentActions, { api, state });
  5725. const scrollActions = Actions.create([bindElement]);
  5726. const scrollAreaActions = Actions.create([bindInnerScroll]);
  5727. return templateProps => wrapper(html `
  5728. <div class=${className} data-actions=${actions} @wheel=${onWheel} @scroll=${onScroll}>
  5729. ${Calendar.html()}${Timeline.html()}
  5730. <div class=${classNameScroll} data-actions=${scrollActions} @scroll=${onScroll}>
  5731. <div class=${classNameScrollInner} style="height: 1px" data-actions=${scrollAreaActions} />
  5732. </div>
  5733. </div>
  5734. `, { vido, props: {}, templateProps });
  5735. }
  5736. /**
  5737. * ChartCalendar component
  5738. *
  5739. * @copyright Rafal Pospiech <https://neuronet.io>
  5740. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5741. * @package gantt-schedule-timeline-calendar
  5742. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5743. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5744. */
  5745. function ChartCalendar(vido, props) {
  5746. const { api, state, onDestroy, Actions, update, reuseComponents, html, StyleMap } = vido;
  5747. const componentName = 'chart-calendar';
  5748. const componentActions = api.getActions(componentName);
  5749. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  5750. const ChartCalendarDateComponent = state.get('config.components.ChartCalendarDate');
  5751. let wrapper;
  5752. onDestroy(state.subscribe('config.wrappers.ChartCalendar', value => (wrapper = value)));
  5753. let className;
  5754. onDestroy(state.subscribe('config.classNames', value => {
  5755. className = api.getClass(componentName);
  5756. update();
  5757. }));
  5758. let headerHeight;
  5759. const styleMap = new StyleMap({ height: '', '--headerHeight': '', 'margin-left': '' });
  5760. onDestroy(state.subscribe('config.headerHeight', value => {
  5761. headerHeight = value;
  5762. styleMap.style['height'] = headerHeight + 'px';
  5763. styleMap.style['--calendar-height'] = headerHeight + 'px';
  5764. update();
  5765. }));
  5766. onDestroy(state.subscribe('config.scroll.compensation.x', compensation => {
  5767. styleMap.style['margin-left'] = -compensation + 'px';
  5768. update();
  5769. }));
  5770. const components = [[], []];
  5771. onDestroy(state.subscribe(`_internal.chart.time.levels`, levels => {
  5772. let level = 0;
  5773. for (const dates of levels) {
  5774. if (!dates.length)
  5775. continue;
  5776. let currentDateFormat = 'YYYY-MM-DD HH'; // hour
  5777. switch (dates[0].period) {
  5778. case 'day':
  5779. currentDateFormat = 'YYYY-MM-DD';
  5780. break;
  5781. case 'week':
  5782. currentDateFormat = 'YYYY-MM-ww';
  5783. break;
  5784. case 'month':
  5785. currentDateFormat = 'YYYY-MM';
  5786. break;
  5787. case 'year':
  5788. currentDateFormat = 'YYYY';
  5789. break;
  5790. }
  5791. const currentDate = api.time.date().format(currentDateFormat);
  5792. reuseComponents(components[level], dates, date => date && { level, date, currentDate, currentDateFormat }, ChartCalendarDateComponent);
  5793. level++;
  5794. }
  5795. update();
  5796. }));
  5797. onDestroy(() => {
  5798. components.forEach(level => level.forEach(component => component.destroy()));
  5799. });
  5800. componentActions.push(element => {
  5801. state.update('_internal.elements.chart-calendar', element);
  5802. });
  5803. const actions = Actions.create(componentActions, actionProps);
  5804. return templateProps => wrapper(html `
  5805. <div class=${className} data-actions=${actions} style=${styleMap}>
  5806. ${components.map((components, level) => html `
  5807. <div class=${className + '-dates ' + className + `-dates--level-${level}`}>
  5808. ${components.map(m => m.html())}
  5809. </div>
  5810. `)}
  5811. </div>
  5812. `, { props, vido, templateProps });
  5813. }
  5814. class Action$1 {
  5815. constructor() {
  5816. this.isAction = true;
  5817. }
  5818. }
  5819. Action$1.prototype.isAction = true;
  5820. /**
  5821. * ChartCalendarDay component
  5822. *
  5823. * @copyright Rafal Pospiech <https://neuronet.io>
  5824. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5825. * @package gantt-schedule-timeline-calendar
  5826. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5827. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5828. */
  5829. /**
  5830. * Save element
  5831. * @param {HTMLElement} element
  5832. * @param {object} data
  5833. */
  5834. class BindElementAction$2 extends Action$1 {
  5835. constructor(element, data) {
  5836. super();
  5837. data.state.update('_internal.elements.chart-calendar-dates', elements => {
  5838. if (typeof elements === 'undefined') {
  5839. elements = [];
  5840. }
  5841. if (!elements.includes(element)) {
  5842. elements.push(element);
  5843. }
  5844. return elements;
  5845. });
  5846. }
  5847. }
  5848. function ChartCalendarDay(vido, props) {
  5849. const { api, state, onDestroy, Actions, update, onChange, html, StyleMap, Detach } = vido;
  5850. const componentName = 'chart-calendar-date';
  5851. const componentActions = api.getActions(componentName);
  5852. let wrapper;
  5853. onDestroy(state.subscribe('config.wrappers.ChartCalendarDate', value => (wrapper = value)));
  5854. let className;
  5855. onDestroy(state.subscribe('config.classNames', () => {
  5856. className = api.getClass(componentName, props);
  5857. }));
  5858. let current = '';
  5859. let time, htmlFormatted;
  5860. const styleMap = new StyleMap({ width: '', visibility: 'visible' }), scrollStyleMap = new StyleMap({
  5861. overflow: 'hidden',
  5862. 'text-align': 'left'
  5863. });
  5864. let formatClassName = '';
  5865. function updateDate() {
  5866. if (!props)
  5867. return;
  5868. const cache = state.get('_internal.cache.calendar');
  5869. const level = state.get(`config.chart.calendar.levels.${props.level}`);
  5870. styleMap.style.width = props.date.width + 'px';
  5871. styleMap.style.visibility = 'visible';
  5872. scrollStyleMap.style = { overflow: 'hidden', 'text-align': 'left', 'margin-left': props.date.subPx + 8 + 'px' };
  5873. time = state.get('_internal.chart.time');
  5874. const cacheKey = `${new Date(props.date.leftGlobal).toISOString()}-${props.date.period}-${props.level}-${time.zoom}`;
  5875. if (!cache[cacheKey]) {
  5876. cache[cacheKey] = {};
  5877. }
  5878. let timeStart, timeEnd;
  5879. {
  5880. timeStart = api.time.date(props.date.leftGlobal);
  5881. timeEnd = api.time.date(props.date.rightGlobal);
  5882. cache[cacheKey].timeStart = timeStart;
  5883. cache[cacheKey].timeEnd = timeEnd;
  5884. }
  5885. const formats = level.formats;
  5886. const formatting = formats.find(formatting => +time.zoom <= +formatting.zoomTo);
  5887. let format;
  5888. {
  5889. format = formatting ? formatting.format({ timeStart, timeEnd, className, vido, props }) : null;
  5890. cache[cacheKey].format = format;
  5891. }
  5892. {
  5893. if (timeStart.format(props.currentDateFormat) === props.currentDate) {
  5894. current = ' gstc-current';
  5895. }
  5896. else if (timeStart.subtract(1, props.date.period).format(props.currentDateFormat) === props.currentDate) {
  5897. current = ' gstc-next';
  5898. }
  5899. else if (timeStart.add(1, props.date.period).format(props.currentDateFormat) === props.currentDate) {
  5900. current = ' gstc-previous';
  5901. }
  5902. else {
  5903. current = '';
  5904. }
  5905. cache[cacheKey].current = current;
  5906. }
  5907. let finalClassName = className + '-content ' + className + `-content--${props.date.period}` + current;
  5908. if (formatting.className) {
  5909. finalClassName += ' ' + formatting.className;
  5910. formatClassName = ' ' + formatting.className;
  5911. }
  5912. else {
  5913. formatClassName = '';
  5914. }
  5915. // updating cache state is not necessary because it is object and nobody listen to cache
  5916. htmlFormatted = html `
  5917. <div class=${finalClassName}>
  5918. ${format}
  5919. </div>
  5920. `;
  5921. update();
  5922. }
  5923. let shouldDetach = false;
  5924. const detach = new Detach(() => shouldDetach);
  5925. let timeSub;
  5926. const actionProps = { date: props.date, period: props.period, api, state };
  5927. onChange((changedProps, options) => {
  5928. if (options.leave) {
  5929. shouldDetach = true;
  5930. return update();
  5931. }
  5932. shouldDetach = false;
  5933. props = changedProps;
  5934. actionProps.date = props.date;
  5935. actionProps.period = props.period;
  5936. if (timeSub) {
  5937. timeSub();
  5938. }
  5939. timeSub = state.subscribeAll(['_internal.chart.time', 'config.chart.calendar.levels'], updateDate, {
  5940. bulk: true
  5941. });
  5942. });
  5943. onDestroy(() => {
  5944. timeSub();
  5945. });
  5946. if (!componentActions.includes(BindElementAction$2))
  5947. componentActions.push(BindElementAction$2);
  5948. const actions = Actions.create(componentActions, actionProps);
  5949. return templateProps => wrapper(html `
  5950. <div
  5951. detach=${detach}
  5952. class=${className +
  5953. ' ' +
  5954. className +
  5955. `--${props.date.period}` +
  5956. ' ' +
  5957. className +
  5958. `--level-${props.level}` +
  5959. current +
  5960. formatClassName}
  5961. style=${styleMap}
  5962. data-actions=${actions}
  5963. >
  5964. ${htmlFormatted}
  5965. </div>
  5966. `, { props, vido, templateProps });
  5967. }
  5968. /**
  5969. * ChartTimeline component
  5970. *
  5971. * @copyright Rafal Pospiech <https://neuronet.io>
  5972. * @author Rafal Pospiech <neuronet.io@gmail.com>
  5973. * @package gantt-schedule-timeline-calendar
  5974. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  5975. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  5976. */
  5977. function ChartTimeline(vido, props) {
  5978. const { api, state, onDestroy, Action, Actions, update, html, createComponent, StyleMap } = vido;
  5979. const componentName = 'chart-timeline';
  5980. const componentActions = api.getActions(componentName);
  5981. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  5982. let wrapper;
  5983. onDestroy(state.subscribe('config.wrappers.ChartTimeline', value => (wrapper = value)));
  5984. const GridComponent = state.get('config.components.ChartTimelineGrid');
  5985. const ItemsComponent = state.get('config.components.ChartTimelineItems');
  5986. const ListToggleComponent = state.get('config.components.ListToggle');
  5987. const Grid = createComponent(GridComponent);
  5988. onDestroy(Grid.destroy);
  5989. const Items = createComponent(ItemsComponent);
  5990. onDestroy(Items.destroy);
  5991. const ListToggle = createComponent(ListToggleComponent);
  5992. onDestroy(ListToggle.destroy);
  5993. let className, classNameInner;
  5994. onDestroy(state.subscribe('config.classNames', () => {
  5995. className = api.getClass(componentName);
  5996. classNameInner = api.getClass(componentName + '-inner');
  5997. update();
  5998. }));
  5999. let showToggle;
  6000. onDestroy(state.subscribe('config.list.toggle.display', val => (showToggle = val)));
  6001. const styleMap = new StyleMap({}), innerStyleMap = new StyleMap({});
  6002. function calculateStyle() {
  6003. const xCompensation = api.getCompensationX();
  6004. const yCompensation = api.getCompensationY();
  6005. const width = state.get('_internal.chart.dimensions.width');
  6006. const height = state.get('_internal.list.rowsHeight');
  6007. styleMap.style.height = state.get('_internal.height') + 'px';
  6008. styleMap.style['--height'] = styleMap.style.height;
  6009. styleMap.style['--negative-compensation-x'] = xCompensation + 'px';
  6010. styleMap.style['--compensation-x'] = Math.round(Math.abs(xCompensation)) + 'px';
  6011. styleMap.style['--negative-compensation-y'] = yCompensation + 'px';
  6012. styleMap.style['--compensation-y'] = Math.abs(yCompensation) + 'px';
  6013. if (width) {
  6014. styleMap.style.width = width + 'px';
  6015. styleMap.style['--width'] = width + 'px';
  6016. }
  6017. else {
  6018. styleMap.style.width = '0px';
  6019. styleMap.style['--width'] = '0px';
  6020. }
  6021. innerStyleMap.style.height = height + 'px';
  6022. if (width) {
  6023. innerStyleMap.style.width = width + xCompensation + 'px';
  6024. }
  6025. else {
  6026. innerStyleMap.style.width = '0px';
  6027. }
  6028. //innerStyleMap.style.transform = `translate(-${xCompensation}px, ${yCompensation}px)`;
  6029. innerStyleMap.style['margin-left'] = -xCompensation + 'px';
  6030. update();
  6031. }
  6032. onDestroy(state.subscribeAll([
  6033. '_internal.height',
  6034. '_internal.chart.dimensions.width',
  6035. '_internal.list.rowsHeight',
  6036. 'config.scroll.compensation',
  6037. '_internal.chart.time.dates.day'
  6038. ], calculateStyle));
  6039. componentActions.push(class BindElementAction extends Action {
  6040. constructor(element) {
  6041. super();
  6042. const old = state.get('_internal.elements.chart-timeline');
  6043. if (old !== element)
  6044. state.update('_internal.elements.chart-timeline', element);
  6045. }
  6046. });
  6047. const actions = Actions.create(componentActions, actionProps);
  6048. return templateProps => wrapper(html `
  6049. <div class=${className} style=${styleMap} data-actions=${actions} @wheel=${api.onScroll}>
  6050. <div class=${classNameInner} style=${innerStyleMap}>
  6051. ${Grid.html()}${Items.html()}${showToggle ? ListToggle.html() : ''}
  6052. </div>
  6053. </div>
  6054. `, { props, vido, templateProps });
  6055. }
  6056. /**
  6057. * ChartTimelineGrid component
  6058. *
  6059. * @copyright Rafal Pospiech <https://neuronet.io>
  6060. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6061. * @package gantt-schedule-timeline-calendar
  6062. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  6063. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  6064. */
  6065. /**
  6066. * Bind element action
  6067. */
  6068. class BindElementAction$3 {
  6069. constructor(element, data) {
  6070. const old = data.state.get('_internal.elements.chart-timeline-grid');
  6071. if (old !== element)
  6072. data.state.update('_internal.elements.chart-timeline-grid', element);
  6073. }
  6074. destroy(element, data) {
  6075. data.state.update('_internal.elements', elements => {
  6076. delete elements['chart-timeline-grid'];
  6077. return elements;
  6078. });
  6079. }
  6080. }
  6081. function ChartTimelineGrid(vido, props) {
  6082. const { api, state, onDestroy, Actions, update, html, reuseComponents, StyleMap } = vido;
  6083. const componentName = 'chart-timeline-grid';
  6084. const componentActions = api.getActions(componentName);
  6085. const actionProps = { api, state };
  6086. let wrapper;
  6087. onDestroy(state.subscribe('config.wrappers.ChartTimelineGrid', value => (wrapper = value)));
  6088. const GridRowComponent = state.get('config.components.ChartTimelineGridRow');
  6089. let className;
  6090. onDestroy(state.subscribe('config.classNames', () => {
  6091. className = api.getClass(componentName);
  6092. update();
  6093. }));
  6094. let onBlockCreate;
  6095. onDestroy(state.subscribe('config.chart.grid.block.onCreate', onCreate => (onBlockCreate = onCreate)));
  6096. const rowsComponents = [];
  6097. const rowsWithBlocks = [];
  6098. const formatCache = new Map();
  6099. const styleMap = new StyleMap({});
  6100. /**
  6101. * Generate blocks
  6102. */
  6103. function generateBlocks() {
  6104. const width = state.get('_internal.chart.dimensions.width');
  6105. const height = state.get('_internal.height');
  6106. const time = state.get('_internal.chart.time');
  6107. const periodDates = state.get(`_internal.chart.time.levels.${time.level}`);
  6108. if (!periodDates || periodDates.length === 0) {
  6109. state.update('_internal.chart.grid.rowsWithBlocks', []);
  6110. return;
  6111. }
  6112. const visibleRows = state.get('_internal.list.visibleRows');
  6113. const xCompensation = api.getCompensationX();
  6114. const yCompensation = api.getCompensationY();
  6115. styleMap.style.height = height + Math.abs(yCompensation) + 'px';
  6116. styleMap.style.width = width + xCompensation + 'px';
  6117. let top = 0;
  6118. rowsWithBlocks.length = 0;
  6119. for (const row of visibleRows) {
  6120. const blocks = [];
  6121. for (const time of periodDates) {
  6122. let format;
  6123. if (formatCache.has(time.leftGlobal)) {
  6124. format = formatCache.get(time.leftGlobal);
  6125. }
  6126. else {
  6127. format = api.time.date(time.leftGlobal).format('YYYY-MM-DD HH:mm');
  6128. formatCache.set(time.leftGlobal, format);
  6129. }
  6130. const id = row.id + ':' + format;
  6131. let block = { id, time, row, top };
  6132. for (const onCreate of onBlockCreate) {
  6133. block = onCreate(block);
  6134. }
  6135. blocks.push(block);
  6136. }
  6137. rowsWithBlocks.push({ row, blocks, top, width });
  6138. top += row.height;
  6139. }
  6140. state.update('_internal.chart.grid.rowsWithBlocks', rowsWithBlocks);
  6141. }
  6142. onDestroy(state.subscribeAll([
  6143. '_internal.list.visibleRows;',
  6144. `_internal.chart.time.levels`,
  6145. '_internal.height',
  6146. '_internal.chart.dimensions.width'
  6147. ], generateBlocks, {
  6148. bulk: true
  6149. }));
  6150. /**
  6151. * Generate rows components
  6152. * @param {array} rowsWithBlocks
  6153. */
  6154. function generateRowsComponents(rowsWithBlocks) {
  6155. reuseComponents(rowsComponents, rowsWithBlocks || [], row => row, GridRowComponent);
  6156. update();
  6157. }
  6158. onDestroy(state.subscribe('_internal.chart.grid.rowsWithBlocks', generateRowsComponents));
  6159. onDestroy(() => {
  6160. rowsComponents.forEach(row => row.destroy());
  6161. });
  6162. componentActions.push(BindElementAction$3);
  6163. const actions = Actions.create(componentActions, actionProps);
  6164. return templateProps => wrapper(html `
  6165. <div class=${className} data-actions=${actions} style=${styleMap}>
  6166. ${rowsComponents.map(r => r.html())}
  6167. </div>
  6168. `, { props, vido, templateProps });
  6169. }
  6170. /**
  6171. * ChartTimelineGridRow component
  6172. *
  6173. * @copyright Rafal Pospiech <https://neuronet.io>
  6174. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6175. * @package gantt-schedule-timeline-calendar
  6176. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  6177. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  6178. */
  6179. /**
  6180. * Bind element action
  6181. */
  6182. class BindElementAction$4 {
  6183. constructor(element, data) {
  6184. let shouldUpdate = false;
  6185. let rows = data.state.get('_internal.elements.chart-timeline-grid-rows');
  6186. if (typeof rows === 'undefined') {
  6187. rows = [];
  6188. shouldUpdate = true;
  6189. }
  6190. if (!rows.includes(element)) {
  6191. rows.push(element);
  6192. shouldUpdate = true;
  6193. }
  6194. if (shouldUpdate)
  6195. data.state.update('_internal.elements.chart-timeline-grid-rows', rows, { only: null });
  6196. }
  6197. destroy(element, data) {
  6198. data.state.update('_internal.elements.chart-timeline-grid-rows', rows => {
  6199. return rows.filter(el => el !== element);
  6200. });
  6201. }
  6202. }
  6203. function ChartTimelineGridRow(vido, props) {
  6204. const { api, state, onDestroy, Detach, Actions, update, html, reuseComponents, onChange, StyleMap } = vido;
  6205. const componentName = 'chart-timeline-grid-row';
  6206. const actionProps = Object.assign(Object.assign({}, props), { api,
  6207. state });
  6208. let wrapper;
  6209. onDestroy(state.subscribe('config.wrappers.ChartTimelineGridRow', value => {
  6210. wrapper = value;
  6211. update();
  6212. }));
  6213. const GridBlockComponent = state.get('config.components.ChartTimelineGridRowBlock');
  6214. const componentActions = api.getActions(componentName);
  6215. let className;
  6216. onDestroy(state.subscribe('config.classNames', () => {
  6217. className = api.getClass(componentName);
  6218. }));
  6219. const styleMap = new StyleMap({
  6220. width: props.width + 'px',
  6221. height: props.row.height + 'px',
  6222. overflow: 'hidden'
  6223. }, true);
  6224. let shouldDetach = false;
  6225. const detach = new Detach(() => shouldDetach);
  6226. const rowsBlocksComponents = [];
  6227. onChange(function onPropsChange(changedProps, options) {
  6228. var _a, _b, _c, _d, _e, _f, _g, _h, _j;
  6229. if (options.leave || changedProps.row === undefined) {
  6230. shouldDetach = true;
  6231. reuseComponents(rowsBlocksComponents, [], block => block, GridBlockComponent);
  6232. update();
  6233. return;
  6234. }
  6235. shouldDetach = false;
  6236. props = changedProps;
  6237. reuseComponents(rowsBlocksComponents, props.blocks, block => block, GridBlockComponent);
  6238. styleMap.setStyle({});
  6239. styleMap.style.height = props.row.height + 'px';
  6240. styleMap.style.width = props.width + 'px';
  6241. const rows = state.get('config.list.rows');
  6242. for (const parentId of props.row._internal.parents) {
  6243. const parent = rows[parentId];
  6244. const childrenStyle = (_d = (_c = (_b = (_a = parent) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.grid) === null || _c === void 0 ? void 0 : _c.row) === null || _d === void 0 ? void 0 : _d.children;
  6245. if (childrenStyle)
  6246. for (const name in childrenStyle) {
  6247. styleMap.style[name] = childrenStyle[name];
  6248. }
  6249. }
  6250. const currentStyle = (_j = (_h = (_g = (_f = (_e = props) === null || _e === void 0 ? void 0 : _e.row) === null || _f === void 0 ? void 0 : _f.style) === null || _g === void 0 ? void 0 : _g.grid) === null || _h === void 0 ? void 0 : _h.row) === null || _j === void 0 ? void 0 : _j.current;
  6251. if (currentStyle)
  6252. for (const name in currentStyle) {
  6253. styleMap.style[name] = currentStyle[name];
  6254. }
  6255. for (const prop in props) {
  6256. actionProps[prop] = props[prop];
  6257. }
  6258. update();
  6259. });
  6260. onDestroy(function destroy() {
  6261. rowsBlocksComponents.forEach(rowBlock => rowBlock.destroy());
  6262. });
  6263. if (componentActions.indexOf(BindElementAction$4) === -1) {
  6264. componentActions.push(BindElementAction$4);
  6265. }
  6266. const actions = Actions.create(componentActions, actionProps);
  6267. return templateProps => {
  6268. return wrapper(html `
  6269. <div detach=${detach} class=${className} data-actions=${actions} style=${styleMap}>
  6270. ${rowsBlocksComponents.map(r => r.html())}
  6271. </div>
  6272. `, { vido, props, templateProps });
  6273. };
  6274. }
  6275. /**
  6276. * ChartTimelineGridRowBlock component
  6277. *
  6278. * @copyright Rafal Pospiech <https://neuronet.io>
  6279. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6280. * @package gantt-schedule-timeline-calendar
  6281. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  6282. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  6283. */
  6284. /**
  6285. * Bind element action
  6286. * @param {Element} element
  6287. * @param {any} data
  6288. * @returns {object} with update and destroy
  6289. */
  6290. class BindElementAction$5 {
  6291. constructor(element, data) {
  6292. let shouldUpdate = false;
  6293. let blocks = data.state.get('_internal.elements.chart-timeline-grid-row-blocks');
  6294. if (typeof blocks === 'undefined') {
  6295. blocks = [];
  6296. shouldUpdate = true;
  6297. }
  6298. if (!blocks.includes(element)) {
  6299. blocks.push(element);
  6300. shouldUpdate = true;
  6301. }
  6302. if (shouldUpdate)
  6303. data.state.update('_internal.elements.chart-timeline-grid-row-blocks', blocks, { only: null });
  6304. }
  6305. destroy(element, data) {
  6306. data.state.update('_internal.elements.chart-timeline-grid-row-blocks', blocks => {
  6307. return blocks.filter(el => el !== element);
  6308. }, { only: [''] });
  6309. }
  6310. }
  6311. const ChartTimelineGridRowBlock = (vido, props) => {
  6312. const { api, state, onDestroy, Detach, Actions, update, html, onChange, StyleMap } = vido;
  6313. const componentName = 'chart-timeline-grid-row-block';
  6314. const actionProps = Object.assign(Object.assign({}, props), { api,
  6315. state });
  6316. let shouldDetach = false;
  6317. const detach = new Detach(() => shouldDetach);
  6318. const componentActions = api.getActions(componentName);
  6319. let wrapper;
  6320. onDestroy(state.subscribe('config.wrappers.ChartTimelineGridRowBlock', value => {
  6321. wrapper = value;
  6322. update();
  6323. }));
  6324. let className;
  6325. function updateClassName(time) {
  6326. const currentTime = api.time
  6327. .date()
  6328. .startOf(time.period)
  6329. .valueOf();
  6330. className = api.getClass(componentName);
  6331. if (time.leftGlobal === currentTime) {
  6332. className += ' current';
  6333. }
  6334. }
  6335. updateClassName(props.time);
  6336. const styleMap = new StyleMap({ width: '', height: '' });
  6337. /**
  6338. * On props change
  6339. * @param {any} changedProps
  6340. */
  6341. function onPropsChange(changedProps, options) {
  6342. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
  6343. if (options.leave || changedProps.row === undefined) {
  6344. shouldDetach = true;
  6345. return update();
  6346. }
  6347. shouldDetach = false;
  6348. props = changedProps;
  6349. for (const prop in props) {
  6350. actionProps[prop] = props[prop];
  6351. }
  6352. updateClassName(props.time);
  6353. styleMap.setStyle({});
  6354. styleMap.style.width = (((_b = (_a = props) === null || _a === void 0 ? void 0 : _a.time) === null || _b === void 0 ? void 0 : _b.width) || 0) + 'px';
  6355. styleMap.style.height = (((_d = (_c = props) === null || _c === void 0 ? void 0 : _c.row) === null || _d === void 0 ? void 0 : _d.height) || 0) + 'px';
  6356. const rows = state.get('config.list.rows');
  6357. for (const parentId of props.row._internal.parents) {
  6358. const parent = rows[parentId];
  6359. const childrenStyle = (_h = (_g = (_f = (_e = parent) === null || _e === void 0 ? void 0 : _e.style) === null || _f === void 0 ? void 0 : _f.grid) === null || _g === void 0 ? void 0 : _g.block) === null || _h === void 0 ? void 0 : _h.children;
  6360. if (childrenStyle)
  6361. styleMap.setStyle(Object.assign(Object.assign({}, styleMap.style), childrenStyle));
  6362. }
  6363. const currentStyle = (_o = (_m = (_l = (_k = (_j = props) === null || _j === void 0 ? void 0 : _j.row) === null || _k === void 0 ? void 0 : _k.style) === null || _l === void 0 ? void 0 : _l.grid) === null || _m === void 0 ? void 0 : _m.block) === null || _o === void 0 ? void 0 : _o.current;
  6364. if (currentStyle)
  6365. styleMap.setStyle(Object.assign(Object.assign({}, styleMap.style), currentStyle));
  6366. update();
  6367. }
  6368. onChange(onPropsChange);
  6369. componentActions.push(BindElementAction$5);
  6370. const actions = Actions.create(componentActions, actionProps);
  6371. return templateProps => {
  6372. return wrapper(html `
  6373. <div detach=${detach} class=${className} data-actions=${actions} style=${styleMap}></div>
  6374. `, { props, vido, templateProps });
  6375. };
  6376. };
  6377. /**
  6378. * ChartTimelineItems component
  6379. *
  6380. * @copyright Rafal Pospiech <https://neuronet.io>
  6381. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6382. * @package gantt-schedule-timeline-calendar
  6383. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  6384. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  6385. */
  6386. function ChartTimelineItems(vido, props = {}) {
  6387. const { api, state, onDestroy, Actions, update, html, reuseComponents, StyleMap } = vido;
  6388. const componentName = 'chart-timeline-items';
  6389. const componentActions = api.getActions(componentName);
  6390. let wrapper;
  6391. onDestroy(state.subscribe('config.wrappers.ChartTimelineItems', value => (wrapper = value)));
  6392. let ItemsRowComponent;
  6393. onDestroy(state.subscribe('config.components.ChartTimelineItemsRow', value => (ItemsRowComponent = value)));
  6394. let className;
  6395. onDestroy(state.subscribe('config.classNames', () => {
  6396. className = api.getClass(componentName);
  6397. update();
  6398. }));
  6399. const styleMap = new StyleMap({}, true);
  6400. function calculateStyle() {
  6401. const width = state.get('_internal.chart.dimensions.width');
  6402. const height = state.get('_internal.height');
  6403. const yCompensation = api.getCompensationY();
  6404. const xCompensation = api.getCompensationX();
  6405. styleMap.style.width = width + xCompensation + 'px';
  6406. styleMap.style.height = height + Math.abs(yCompensation) + 'px';
  6407. }
  6408. onDestroy(state.subscribeAll([
  6409. '_internal.height',
  6410. '_internal.chart.dimensions.width',
  6411. 'config.scroll.compensation',
  6412. '_internal.chart.time.dates.day'
  6413. ], calculateStyle));
  6414. const rowsComponents = [];
  6415. function createRowComponents() {
  6416. const visibleRows = state.get('_internal.list.visibleRows');
  6417. reuseComponents(rowsComponents, visibleRows || [], row => ({ row }), ItemsRowComponent);
  6418. update();
  6419. }
  6420. onDestroy(state.subscribeAll(['_internal.list.visibleRows;', 'config.chart.items'], createRowComponents));
  6421. onDestroy(() => {
  6422. rowsComponents.forEach(row => row.destroy());
  6423. });
  6424. const actions = Actions.create(componentActions, { api, state });
  6425. return templateProps => wrapper(html `
  6426. <div class=${className} style=${styleMap} data-actions=${actions}>
  6427. ${rowsComponents.map(r => r.html())}
  6428. </div>
  6429. `, { props, vido, templateProps });
  6430. }
  6431. /**
  6432. * ChartTimelineItemsRow component
  6433. *
  6434. * @copyright Rafal Pospiech <https://neuronet.io>
  6435. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6436. * @package gantt-schedule-timeline-calendar
  6437. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  6438. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  6439. */
  6440. /**
  6441. * Bind element action
  6442. * @param {Element} element
  6443. * @param {any} data
  6444. */
  6445. class BindElementAction$6 {
  6446. constructor(element, data) {
  6447. let shouldUpdate = false;
  6448. let rows = data.state.get('_internal.elements.chart-timeline-items-rows');
  6449. if (typeof rows === 'undefined') {
  6450. rows = [];
  6451. shouldUpdate = true;
  6452. }
  6453. if (!rows.includes(element)) {
  6454. rows.push(element);
  6455. shouldUpdate = true;
  6456. }
  6457. if (shouldUpdate)
  6458. data.state.update('_internal.elements.chart-timeline-items-rows', rows, { only: null });
  6459. }
  6460. destroy(element, data) {
  6461. data.state.update('_internal.elements.chart-timeline-items-rows', rows => {
  6462. return rows.filter(el => el !== element);
  6463. });
  6464. }
  6465. }
  6466. const ChartTimelineItemsRow = (vido, props) => {
  6467. const { api, state, onDestroy, Detach, Actions, update, html, onChange, reuseComponents, StyleMap } = vido;
  6468. const actionProps = Object.assign(Object.assign({}, props), { api, state });
  6469. let wrapper;
  6470. onDestroy(state.subscribe('config.wrappers.ChartTimelineItemsRow', value => (wrapper = value)));
  6471. let ItemComponent;
  6472. onDestroy(state.subscribe('config.components.ChartTimelineItemsRowItem', value => (ItemComponent = value)));
  6473. let itemsPath = `_internal.flatTreeMapById.${props.row.id}._internal.items`;
  6474. let rowSub, itemsSub;
  6475. const itemComponents = [], styleMap = new StyleMap({ width: '', height: '' }, true);
  6476. let shouldDetach = false;
  6477. const detach = new Detach(() => shouldDetach);
  6478. const updateDom = () => {
  6479. const chart = state.get('_internal.chart');
  6480. shouldDetach = false;
  6481. const xCompensation = api.getCompensationX();
  6482. styleMap.style.width = chart.dimensions.width + xCompensation + 'px';
  6483. if (!props) {
  6484. shouldDetach = true;
  6485. return;
  6486. }
  6487. styleMap.style.height = props.row.height + 'px';
  6488. styleMap.style['--row-height'] = props.row.height + 'px';
  6489. };
  6490. function updateRow(row) {
  6491. itemsPath = `_internal.flatTreeMapById.${row.id}._internal.items`;
  6492. if (typeof rowSub === 'function') {
  6493. rowSub();
  6494. }
  6495. if (typeof itemsSub === 'function') {
  6496. itemsSub();
  6497. }
  6498. rowSub = state.subscribe('_internal.chart', value => {
  6499. if (value === undefined) {
  6500. shouldDetach = true;
  6501. return update();
  6502. }
  6503. updateDom();
  6504. update();
  6505. });
  6506. itemsSub = state.subscribe(itemsPath, value => {
  6507. if (value === undefined) {
  6508. shouldDetach = true;
  6509. reuseComponents(itemComponents, [], item => ({ row, item }), ItemComponent);
  6510. return update();
  6511. }
  6512. reuseComponents(itemComponents, value, item => ({ row, item }), ItemComponent);
  6513. updateDom();
  6514. update();
  6515. });
  6516. }
  6517. /**
  6518. * On props change
  6519. * @param {any} changedProps
  6520. */
  6521. onChange((changedProps, options) => {
  6522. if (options.leave || changedProps.row === undefined) {
  6523. shouldDetach = true;
  6524. return update();
  6525. }
  6526. props = changedProps;
  6527. for (const prop in props) {
  6528. actionProps[prop] = props[prop];
  6529. }
  6530. updateRow(props.row);
  6531. });
  6532. onDestroy(() => {
  6533. itemsSub();
  6534. rowSub();
  6535. itemComponents.forEach(item => item.destroy());
  6536. });
  6537. const componentName = 'chart-timeline-items-row';
  6538. const componentActions = api.getActions(componentName);
  6539. let className;
  6540. onDestroy(state.subscribe('config.classNames', () => {
  6541. className = api.getClass(componentName, props);
  6542. update();
  6543. }));
  6544. componentActions.push(BindElementAction$6);
  6545. const actions = Actions.create(componentActions, actionProps);
  6546. return templateProps => {
  6547. return wrapper(html `
  6548. <div detach=${detach} class=${className} data-actions=${actions} style=${styleMap}>
  6549. ${itemComponents.map(i => i.html())}
  6550. </div>
  6551. `, { props, vido, templateProps });
  6552. };
  6553. };
  6554. /**
  6555. * ChartTimelineItemsRowItem component
  6556. *
  6557. * @copyright Rafal Pospiech <https://neuronet.io>
  6558. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6559. * @package gantt-schedule-timeline-calendar
  6560. * @license AGPL-3.0 (https://github.com/neuronetio/gantt-schedule-timeline-calendar/blob/master/LICENSE)
  6561. * @link https://github.com/neuronetio/gantt-schedule-timeline-calendar
  6562. */
  6563. /**
  6564. * Bind element action
  6565. */
  6566. class BindElementAction$7 {
  6567. constructor(element, data) {
  6568. let shouldUpdate = false;
  6569. let items = data.state.get('_internal.elements.chart-timeline-items-row-items');
  6570. if (typeof items === 'undefined') {
  6571. items = [];
  6572. shouldUpdate = true;
  6573. }
  6574. if (!items.includes(element)) {
  6575. items.push(element);
  6576. shouldUpdate = true;
  6577. }
  6578. if (shouldUpdate)
  6579. data.state.update('_internal.elements.chart-timeline-items-row-items', items, { only: null });
  6580. }
  6581. destroy(element, data) {
  6582. data.state.update('_internal.elements.chart-timeline-items-row-items', items => {
  6583. return items.filter(el => el !== element);
  6584. });
  6585. }
  6586. }
  6587. function ChartTimelineItemsRowItem(vido, props) {
  6588. const { api, state, onDestroy, Detach, Actions, update, html, onChange, unsafeHTML, StyleMap } = vido;
  6589. let wrapper;
  6590. onDestroy(state.subscribe('config.wrappers.ChartTimelineItemsRowItem', value => (wrapper = value)));
  6591. let itemLeftPx = 0, itemWidthPx = 0, leave = false, cutLeft = false, cutRight = false;
  6592. const styleMap = new StyleMap({ width: '', height: '', left: '' }), leftCutStyleMap = new StyleMap({ 'margin-left': '0px' }), rightCutStyleMap = new StyleMap({ 'margin-right': '0px' }), actionProps = {
  6593. item: props.item,
  6594. row: props.row,
  6595. left: itemLeftPx,
  6596. width: itemWidthPx,
  6597. api,
  6598. state
  6599. };
  6600. let shouldDetach = false;
  6601. function updateItem() {
  6602. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
  6603. if (leave)
  6604. return;
  6605. const time = state.get('_internal.chart.time');
  6606. itemLeftPx = api.time.globalTimeToViewPixelOffset(props.item.time.start);
  6607. itemLeftPx = Math.round(itemLeftPx * 10) * 0.1;
  6608. itemWidthPx = (props.item.time.end - props.item.time.start) / time.timePerPixel;
  6609. itemWidthPx -= state.get('config.chart.spacing') || 0;
  6610. if (itemWidthPx) {
  6611. itemWidthPx = Math.round(itemWidthPx * 10) * 0.1;
  6612. }
  6613. if (props.item.time.start < time.leftGlobal) {
  6614. leftCutStyleMap.style['margin-left'] = (time.leftGlobal - props.item.time.start) / time.timePerPixel + 'px';
  6615. cutLeft = true;
  6616. }
  6617. else {
  6618. leftCutStyleMap.style['margin-left'] = '0px';
  6619. cutLeft = false;
  6620. }
  6621. if (props.item.time.end > time.rightGlobal) {
  6622. rightCutStyleMap.style['margin-right'] = (props.item.time.end - time.rightGlobal) / time.timePerPixel + 'px';
  6623. cutRight = true;
  6624. }
  6625. else {
  6626. cutRight = false;
  6627. rightCutStyleMap.style['margin-right'] = '0px';
  6628. }
  6629. const oldWidth = styleMap.style.width;
  6630. const oldLeft = styleMap.style.left;
  6631. const xCompensation = api.getCompensationX();
  6632. styleMap.setStyle({});
  6633. const inViewPort = api.isItemInViewport(props.item, time.leftGlobal, time.rightGlobal);
  6634. shouldDetach = !inViewPort;
  6635. if (inViewPort) {
  6636. // update style only when visible to prevent browser's recalculate style
  6637. styleMap.style.width = itemWidthPx + 'px';
  6638. styleMap.style.left = itemLeftPx + xCompensation + 'px';
  6639. }
  6640. else {
  6641. styleMap.style.width = oldWidth;
  6642. styleMap.style.left = oldLeft;
  6643. }
  6644. const rows = state.get('config.list.rows');
  6645. for (const parentId of props.row._internal.parents) {
  6646. const parent = rows[parentId];
  6647. const childrenStyle = (_d = (_c = (_b = (_a = parent) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.items) === null || _c === void 0 ? void 0 : _c.item) === null || _d === void 0 ? void 0 : _d.children;
  6648. if (childrenStyle)
  6649. styleMap.setStyle(Object.assign(Object.assign({}, styleMap.style), childrenStyle));
  6650. }
  6651. const currentRowItemsStyle = (_j = (_h = (_g = (_f = (_e = props) === null || _e === void 0 ? void 0 : _e.row) === null || _f === void 0 ? void 0 : _f.style) === null || _g === void 0 ? void 0 : _g.items) === null || _h === void 0 ? void 0 : _h.item) === null || _j === void 0 ? void 0 : _j.current;
  6652. if (currentRowItemsStyle)
  6653. styleMap.setStyle(Object.assign(Object.assign({}, styleMap.style), currentRowItemsStyle));
  6654. const currentStyle = (_l = (_k = props) === null || _k === void 0 ? void 0 : _k.item) === null || _l === void 0 ? void 0 : _l.style;
  6655. if (currentStyle)
  6656. styleMap.setStyle(Object.assign(Object.assign({}, styleMap.style), currentStyle));
  6657. actionProps.left = itemLeftPx + xCompensation;
  6658. actionProps.width = itemWidthPx;
  6659. update();
  6660. }
  6661. const componentName = 'chart-timeline-items-row-item';
  6662. const cutterName = api.getClass(componentName) + '-cut';
  6663. const cutterLeft = html `
  6664. <div class=${cutterName} style=${leftCutStyleMap}>
  6665. <svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 18 16" width="16">
  6666. <path fill-opacity="0.5" fill="#ffffff" d="m5,3l-5,5l5,5l0,-10z" />
  6667. </svg>
  6668. </div>
  6669. `;
  6670. const cutterRight = html `
  6671. <div class=${cutterName} style=${rightCutStyleMap}>
  6672. <svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 4 16" width="16">
  6673. <path transform="rotate(-180 2.5,8) " fill-opacity="0.5" fill="#ffffff" d="m5,3l-5,5l5,5l0,-10z" />
  6674. </svg>
  6675. </div>
  6676. `;
  6677. function onPropsChange(changedProps, options) {
  6678. if (options.leave || changedProps.row === undefined || changedProps.item === undefined) {
  6679. leave = true;
  6680. shouldDetach = true;
  6681. return update();
  6682. }
  6683. else {
  6684. shouldDetach = false;
  6685. leave = false;
  6686. }
  6687. props = changedProps;
  6688. actionProps.item = props.item;
  6689. actionProps.row = props.row;
  6690. updateItem();
  6691. }
  6692. onChange(onPropsChange);
  6693. const componentActions = api.getActions(componentName);
  6694. let className, labelClassName;
  6695. onDestroy(state.subscribe('config.classNames', () => {
  6696. className = api.getClass(componentName, props);
  6697. labelClassName = api.getClass(componentName + '-label', props);
  6698. update();
  6699. }));
  6700. onDestroy(state.subscribeAll(['_internal.chart.time', 'config.scroll.compensation.x'], updateItem));
  6701. componentActions.push(BindElementAction$7);
  6702. const actions = Actions.create(componentActions, actionProps);
  6703. const detach = new Detach(() => shouldDetach);
  6704. return templateProps => {
  6705. return wrapper(html `
  6706. <div detach=${detach} class=${className} data-actions=${actions} style=${styleMap}>
  6707. ${cutLeft ? cutterLeft : ''}
  6708. <div class=${labelClassName}>
  6709. ${props.item.isHtml ? unsafeHTML(props.item.label) : props.item.label}
  6710. </div>
  6711. ${cutRight ? cutterRight : ''}
  6712. </div>
  6713. `, { vido, props, templateProps });
  6714. };
  6715. }
  6716. /**
  6717. * Gantt-Schedule-Timeline-Calendar
  6718. *
  6719. * @copyright Rafal Pospiech <https://neuronet.io>
  6720. * @author Rafal Pospiech <neuronet.io@gmail.com>
  6721. * @package gantt-schedule-timeline-calendar
  6722. * @license AGPL-3.0
  6723. */
  6724. const actionNames = [
  6725. 'main',
  6726. 'list',
  6727. 'list-column',
  6728. 'list-column-header',
  6729. 'list-column-header-resizer',
  6730. 'list-column-header-resizer-dots',
  6731. 'list-column-row',
  6732. 'list-column-row-expander',
  6733. 'list-column-row-expander-toggle',
  6734. 'list-toggle',
  6735. 'chart',
  6736. 'chart-calendar',
  6737. 'chart-calendar-date',
  6738. 'chart-timeline',
  6739. 'chart-timeline-grid',
  6740. 'chart-timeline-grid-row',
  6741. 'chart-timeline-grid-row-block',
  6742. 'chart-timeline-items',
  6743. 'chart-timeline-items-row',
  6744. 'chart-timeline-items-row-item'
  6745. ];
  6746. function generateEmptyActions() {
  6747. const actions = {};
  6748. actionNames.forEach(name => (actions[name] = []));
  6749. return actions;
  6750. }
  6751. function generateEmptySlots() {
  6752. const slots = {};
  6753. actionNames.forEach(name => {
  6754. slots[name] = { before: [], after: [] };
  6755. });
  6756. return slots;
  6757. }
  6758. // default configuration
  6759. function defaultConfig() {
  6760. const actions = generateEmptyActions();
  6761. const slots = generateEmptySlots();
  6762. return {
  6763. plugins: [],
  6764. plugin: {},
  6765. height: 822,
  6766. headerHeight: 72,
  6767. components: {
  6768. Main,
  6769. List,
  6770. ListColumn,
  6771. ListColumnHeader,
  6772. ListColumnHeaderResizer,
  6773. ListColumnRow,
  6774. ListColumnRowExpander,
  6775. ListColumnRowExpanderToggle,
  6776. ListToggle,
  6777. Chart,
  6778. ChartCalendar,
  6779. ChartCalendarDate: ChartCalendarDay,
  6780. ChartTimeline,
  6781. ChartTimelineGrid,
  6782. ChartTimelineGridRow,
  6783. ChartTimelineGridRowBlock,
  6784. ChartTimelineItems,
  6785. ChartTimelineItemsRow,
  6786. ChartTimelineItemsRowItem
  6787. },
  6788. wrappers: {
  6789. Main(input) {
  6790. return input;
  6791. },
  6792. List(input) {
  6793. return input;
  6794. },
  6795. ListColumn(input) {
  6796. return input;
  6797. },
  6798. ListColumnHeader(input) {
  6799. return input;
  6800. },
  6801. ListColumnHeaderResizer(input) {
  6802. return input;
  6803. },
  6804. ListColumnRow(input) {
  6805. return input;
  6806. },
  6807. ListColumnRowExpander(input) {
  6808. return input;
  6809. },
  6810. ListColumnRowExpanderToggle(input) {
  6811. return input;
  6812. },
  6813. ListToggle(input) {
  6814. return input;
  6815. },
  6816. Chart(input) {
  6817. return input;
  6818. },
  6819. ChartCalendar(input) {
  6820. return input;
  6821. },
  6822. ChartCalendarDate(input) {
  6823. return input;
  6824. },
  6825. ChartTimeline(input) {
  6826. return input;
  6827. },
  6828. ChartTimelineGrid(input) {
  6829. return input;
  6830. },
  6831. ChartTimelineGridRow(input) {
  6832. return input;
  6833. },
  6834. ChartTimelineGridRowBlock(input) {
  6835. return input;
  6836. },
  6837. ChartTimelineItems(input) {
  6838. return input;
  6839. },
  6840. ChartTimelineItemsRow(input) {
  6841. return input;
  6842. },
  6843. ChartTimelineItemsRowItem(input) {
  6844. return input;
  6845. }
  6846. },
  6847. list: {
  6848. rows: {},
  6849. rowHeight: 40,
  6850. columns: {
  6851. percent: 100,
  6852. resizer: {
  6853. width: 10,
  6854. inRealTime: true,
  6855. dots: 6
  6856. },
  6857. minWidth: 50,
  6858. data: {}
  6859. },
  6860. expander: {
  6861. padding: 18,
  6862. size: 20,
  6863. icon: {
  6864. width: 16,
  6865. height: 16
  6866. },
  6867. icons: {
  6868. child: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><ellipse ry="4" rx="4" id="svg_1" cy="12" cx="12" fill="#000000B0"/></svg>',
  6869. open: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>',
  6870. closed: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>'
  6871. }
  6872. },
  6873. toggle: {
  6874. display: true,
  6875. icons: {
  6876. open: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path stroke="null" d="m16.406954,16.012672l4.00393,-4.012673l-4.00393,-4.012673l1.232651,-1.232651l5.245324,5.245324l-5.245324,5.245324l-1.232651,-1.232651z"/><path stroke="null" d="m-0.343497,12.97734zm1.620144,0l11.341011,0l0,-1.954681l-11.341011,0l0,1.954681zm0,3.909362l11.341011,0l0,-1.954681l-11.341011,0l0,1.954681zm0,-9.773404l0,1.95468l11.341011,0l0,-1.95468l-11.341011,0z"/></svg>`,
  6877. close: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path transform="rotate(-180 4.392796516418457,12) " stroke="null" d="m1.153809,16.012672l4.00393,-4.012673l-4.00393,-4.012673l1.232651,-1.232651l5.245324,5.245324l-5.245324,5.245324l-1.232651,-1.232651z"/><path stroke="null" d="m9.773297,12.97734zm1.620144,0l11.341011,0l0,-1.954681l-11.341011,0l0,1.954681zm0,3.909362l11.341011,0l0,-1.954681l-11.341011,0l0,1.954681zm0,-9.773404l0,1.95468l11.341011,0l0,-1.95468l-11.341011,0z"/></svg>`
  6878. }
  6879. }
  6880. },
  6881. scroll: {
  6882. propagate: true,
  6883. smooth: false,
  6884. top: 0,
  6885. left: 0,
  6886. xMultiplier: 3,
  6887. yMultiplier: 3,
  6888. percent: {
  6889. top: 0,
  6890. left: 0
  6891. },
  6892. compensation: {
  6893. x: 0,
  6894. y: 0
  6895. }
  6896. },
  6897. chart: {
  6898. time: {
  6899. period: 'day',
  6900. from: 0,
  6901. to: 0,
  6902. finalFrom: 0,
  6903. finalTo: 0,
  6904. zoom: 21,
  6905. leftGlobal: 0,
  6906. centerGlobal: 0,
  6907. rightGlobal: 0,
  6908. levels: [],
  6909. calculatedZoomMode: false
  6910. },
  6911. calendar: {
  6912. expand: true,
  6913. levels: [
  6914. {
  6915. formats: [
  6916. {
  6917. zoomTo: 17,
  6918. period: 'day',
  6919. className: 'gstc-date-medium gstc-date-left',
  6920. format({ timeStart }) {
  6921. return timeStart.format('DD MMMM YYYY (dddd)');
  6922. }
  6923. },
  6924. {
  6925. zoomTo: 23,
  6926. period: 'month',
  6927. format({ timeStart }) {
  6928. return timeStart.format('MMMM YYYY');
  6929. }
  6930. },
  6931. {
  6932. zoomTo: 24,
  6933. period: 'month',
  6934. format({ timeStart, className, vido }) {
  6935. return timeStart.format("MMMM 'YY");
  6936. }
  6937. },
  6938. {
  6939. zoomTo: 25,
  6940. period: 'month',
  6941. format({ timeStart }) {
  6942. return timeStart.format('MMM YYYY');
  6943. }
  6944. },
  6945. {
  6946. zoomTo: 27,
  6947. period: 'year',
  6948. format({ timeStart }) {
  6949. return timeStart.format('YYYY');
  6950. }
  6951. },
  6952. {
  6953. zoomTo: 100,
  6954. period: 'year',
  6955. default: true,
  6956. format() {
  6957. return null;
  6958. }
  6959. }
  6960. ]
  6961. },
  6962. {
  6963. main: true,
  6964. formats: [
  6965. {
  6966. zoomTo: 16,
  6967. period: 'hour',
  6968. format({ timeStart }) {
  6969. return timeStart.format('HH:mm');
  6970. }
  6971. },
  6972. {
  6973. zoomTo: 17,
  6974. period: 'hour',
  6975. default: true,
  6976. format({ timeStart }) {
  6977. return timeStart.format('HH');
  6978. }
  6979. },
  6980. {
  6981. zoomTo: 19,
  6982. period: 'day',
  6983. className: 'gstc-date-medium',
  6984. format({ timeStart, className, vido }) {
  6985. return vido.html `<span class="${className}-content gstc-date-bold">${timeStart.format('DD')}</span> <span class="${className}-content gstc-date-thin">${timeStart.format('dddd')}</span>`;
  6986. }
  6987. },
  6988. {
  6989. zoomTo: 20,
  6990. period: 'day',
  6991. default: true,
  6992. format({ timeStart, vido, className }) {
  6993. return vido.html `<div class="${className}-content gstc-date-top">${timeStart.format('DD')}</div><div class="${className}-content gstc-date-small">${timeStart.format('dddd')}</div>`;
  6994. }
  6995. },
  6996. {
  6997. zoomTo: 21,
  6998. period: 'day',
  6999. format({ timeStart, vido, className }) {
  7000. return vido.html `<div class="${className}-content gstc-date-top">${timeStart.format('DD')}</div><div class="${className}-content gstc-date-small">${timeStart.format('ddd')}</div>`;
  7001. }
  7002. },
  7003. {
  7004. zoomTo: 22,
  7005. period: 'day',
  7006. className: 'gstc-date-vertical',
  7007. format({ timeStart, className, vido }) {
  7008. return vido.html `<div class="${className}-content gstc-date-top">${timeStart.format('DD')}</div><div class="${className}-content gstc-date-extra-small">${timeStart.format('ddd')}</div>`;
  7009. }
  7010. },
  7011. {
  7012. zoomTo: 23,
  7013. period: 'week',
  7014. default: true,
  7015. format({ timeStart, timeEnd, className, vido }) {
  7016. return vido.html `<div class="${className}-content gstc-date-top">${timeStart.format('DD')} - ${timeEnd.format('DD')}</div><div class="${className}-content gstc-date-small gstc-date-thin">${timeStart.format('ddd')} - ${timeEnd.format('dd')}</div>`;
  7017. }
  7018. },
  7019. {
  7020. zoomTo: 25,
  7021. period: 'week',
  7022. className: 'gstc-date-vertical',
  7023. format({ timeStart, timeEnd, className, vido }) {
  7024. return vido.html `<div class="${className}-content gstc-date-top gstc-date-small gstc-date-normal">${timeStart.format('DD')}</div><div class="gstc-dash gstc-date-small">-</div><div class="${className}-content gstc-date-small gstc-date-normal">${timeEnd.format('DD')}</div>`;
  7025. }
  7026. },
  7027. {
  7028. zoomTo: 26,
  7029. period: 'month',
  7030. default: true,
  7031. className: 'gstc-date-month-level-1',
  7032. format({ timeStart, vido, className }) {
  7033. return vido.html `<div class="${className}-content gstc-date-top">${timeStart.format('MMM')}</div><div class="${className}-content gstc-date-small gstc-date-bottom">${timeStart.format('MM')}</div>`;
  7034. }
  7035. },
  7036. {
  7037. zoomTo: 27,
  7038. period: 'month',
  7039. className: 'gstc-date-vertical',
  7040. format({ timeStart, className, vido }) {
  7041. return vido.html `<div class="${className}-content gstc-date-top">${timeStart.format('MM')}</div><div class="${className}-content gstc-date-extra-small">${timeStart.format('MMM')}</div>`;
  7042. }
  7043. },
  7044. {
  7045. zoomTo: 28,
  7046. period: 'year',
  7047. default: true,
  7048. className: 'gstc-date-big',
  7049. format({ timeStart }) {
  7050. return timeStart.format('YYYY');
  7051. }
  7052. },
  7053. {
  7054. zoomTo: 29,
  7055. period: 'year',
  7056. className: 'gstc-date-medium',
  7057. format({ timeStart }) {
  7058. return timeStart.format('YYYY');
  7059. }
  7060. },
  7061. {
  7062. zoomTo: 30,
  7063. period: 'year',
  7064. className: 'gstc-date-medium',
  7065. format({ timeStart }) {
  7066. return timeStart.format('YY');
  7067. }
  7068. },
  7069. {
  7070. zoomTo: 100,
  7071. period: 'year',
  7072. default: true,
  7073. format() {
  7074. return null;
  7075. }
  7076. }
  7077. ]
  7078. }
  7079. ]
  7080. },
  7081. grid: {
  7082. block: {
  7083. onCreate: []
  7084. }
  7085. },
  7086. items: {},
  7087. spacing: 1
  7088. },
  7089. slots,
  7090. classNames: {},
  7091. actions,
  7092. locale: {
  7093. name: 'en',
  7094. weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
  7095. weekdaysShort: 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
  7096. weekdaysMin: 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
  7097. months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
  7098. monthsShort: 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
  7099. weekStart: 1,
  7100. relativeTime: {
  7101. future: 'in %s',
  7102. past: '%s ago',
  7103. s: 'a few seconds',
  7104. m: 'a minute',
  7105. mm: '%d minutes',
  7106. h: 'an hour',
  7107. hh: '%d hours',
  7108. d: 'a day',
  7109. dd: '%d days',
  7110. M: 'a month',
  7111. MM: '%d months',
  7112. y: 'a year',
  7113. yy: '%d years'
  7114. },
  7115. formats: {
  7116. LT: 'HH:mm',
  7117. LTS: 'HH:mm:ss',
  7118. L: 'DD/MM/YYYY',
  7119. LL: 'D MMMM YYYY',
  7120. LLL: 'D MMMM YYYY HH:mm',
  7121. LLLL: 'dddd, D MMMM YYYY HH:mm'
  7122. },
  7123. ordinal: (n) => {
  7124. const s = ['th', 'st', 'nd', 'rd'];
  7125. const v = n % 100;
  7126. return `[${n}${s[(v - 20) % 10] || s[v] || s[0]}]`;
  7127. }
  7128. },
  7129. utcMode: false,
  7130. usageStatistics: true
  7131. };
  7132. }
  7133. var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  7134. function createCommonjsModule(fn, module) {
  7135. return module = { exports: {} }, fn(module, module.exports), module.exports;
  7136. }
  7137. var dayjs_min = createCommonjsModule(function (module, exports) {
  7138. !function(t,n){module.exports=n();}(commonjsGlobal,function(){var t="millisecond",n="second",e="minute",r="hour",i="day",s="week",u="month",o="quarter",a="year",h=/^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/,f=/\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,c=function(t,n,e){var r=String(t);return !r||r.length>=n?t:""+Array(n+1-r.length).join(e)+t},d={s:c,z:function(t){var n=-t.utcOffset(),e=Math.abs(n),r=Math.floor(e/60),i=e%60;return (n<=0?"+":"-")+c(r,2,"0")+":"+c(i,2,"0")},m:function(t,n){var e=12*(n.year()-t.year())+(n.month()-t.month()),r=t.clone().add(e,u),i=n-r<0,s=t.clone().add(e+(i?-1:1),u);return Number(-(e+(n-r)/(i?r-s:s-r))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(h){return {M:u,y:a,w:s,d:i,D:"date",h:r,m:e,s:n,ms:t,Q:o}[h]||String(h||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},$={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},l="en",m={};m[l]=$;var y=function(t){return t instanceof v},M=function(t,n,e){var r;if(!t)return l;if("string"==typeof t)m[t]&&(r=t),n&&(m[t]=n,r=t);else{var i=t.name;m[i]=t,r=i;}return !e&&r&&(l=r),r||!e&&l},g=function(t,n,e){if(y(t))return t.clone();var r=n?"string"==typeof n?{format:n,pl:e}:n:{};return r.date=t,new v(r)},D=d;D.l=M,D.i=y,D.w=function(t,n){return g(t,{locale:n.$L,utc:n.$u,$offset:n.$offset})};var v=function(){function c(t){this.$L=this.$L||M(t.locale,null,!0),this.parse(t);}var d=c.prototype;return d.parse=function(t){this.$d=function(t){var n=t.date,e=t.utc;if(null===n)return new Date(NaN);if(D.u(n))return new Date;if(n instanceof Date)return new Date(n);if("string"==typeof n&&!/Z$/i.test(n)){var r=n.match(h);if(r)return e?new Date(Date.UTC(r[1],r[2]-1,r[3]||1,r[4]||0,r[5]||0,r[6]||0,r[7]||0)):new Date(r[1],r[2]-1,r[3]||1,r[4]||0,r[5]||0,r[6]||0,r[7]||0)}return new Date(n)}(t),this.init();},d.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds();},d.$utils=function(){return D},d.isValid=function(){return !("Invalid Date"===this.$d.toString())},d.isSame=function(t,n){var e=g(t);return this.startOf(n)<=e&&e<=this.endOf(n)},d.isAfter=function(t,n){return g(t)<this.startOf(n)},d.isBefore=function(t,n){return this.endOf(n)<g(t)},d.$g=function(t,n,e){return D.u(t)?this[n]:this.set(e,t)},d.year=function(t){return this.$g(t,"$y",a)},d.month=function(t){return this.$g(t,"$M",u)},d.day=function(t){return this.$g(t,"$W",i)},d.date=function(t){return this.$g(t,"$D","date")},d.hour=function(t){return this.$g(t,"$H",r)},d.minute=function(t){return this.$g(t,"$m",e)},d.second=function(t){return this.$g(t,"$s",n)},d.millisecond=function(n){return this.$g(n,"$ms",t)},d.unix=function(){return Math.floor(this.valueOf()/1e3)},d.valueOf=function(){return this.$d.getTime()},d.startOf=function(t,o){var h=this,f=!!D.u(o)||o,c=D.p(t),d=function(t,n){var e=D.w(h.$u?Date.UTC(h.$y,n,t):new Date(h.$y,n,t),h);return f?e:e.endOf(i)},$=function(t,n){return D.w(h.toDate()[t].apply(h.toDate(),(f?[0,0,0,0]:[23,59,59,999]).slice(n)),h)},l=this.$W,m=this.$M,y=this.$D,M="set"+(this.$u?"UTC":"");switch(c){case a:return f?d(1,0):d(31,11);case u:return f?d(1,m):d(0,m+1);case s:var g=this.$locale().weekStart||0,v=(l<g?l+7:l)-g;return d(f?y-v:y+(6-v),m);case i:case"date":return $(M+"Hours",0);case r:return $(M+"Minutes",1);case e:return $(M+"Seconds",2);case n:return $(M+"Milliseconds",3);default:return this.clone()}},d.endOf=function(t){return this.startOf(t,!1)},d.$set=function(s,o){var h,f=D.p(s),c="set"+(this.$u?"UTC":""),d=(h={},h[i]=c+"Date",h.date=c+"Date",h[u]=c+"Month",h[a]=c+"FullYear",h[r]=c+"Hours",h[e]=c+"Minutes",h[n]=c+"Seconds",h[t]=c+"Milliseconds",h)[f],$=f===i?this.$D+(o-this.$W):o;if(f===u||f===a){var l=this.clone().set("date",1);l.$d[d]($),l.init(),this.
  7139. });
  7140. var utc = createCommonjsModule(function (module, exports) {
  7141. !function(t,i){module.exports=i();}(commonjsGlobal,function(){return function(t,i,e){var s=(new Date).getTimezoneOffset(),n=i.prototype;e.utc=function(t,e){return new i({date:t,utc:!0,format:e})},n.utc=function(){return e(this.toDate(),{locale:this.$L,utc:!0})},n.local=function(){return e(this.toDate(),{locale:this.$L,utc:!1})};var u=n.parse;n.parse=function(t){t.utc&&(this.$u=!0),this.$utils().u(t.$offset)||(this.$offset=t.$offset),u.call(this,t);};var o=n.init;n.init=function(){if(this.$u){var t=this.$d;this.$y=t.getUTCFullYear(),this.$M=t.getUTCMonth(),this.$D=t.getUTCDate(),this.$W=t.getUTCDay(),this.$H=t.getUTCHours(),this.$m=t.getUTCMinutes(),this.$s=t.getUTCSeconds(),this.$ms=t.getUTCMilliseconds();}else o.call(this);};var f=n.utcOffset;n.utcOffset=function(t){var i=this.$utils().u;if(i(t))return this.$u?0:i(this.$offset)?f.call(this):this.$offset;var e,n=Math.abs(t)<=16?60*t:t;return 0!==t?(e=this.local().add(n+s,"minute")).$offset=n:e=this.utc(),e};var r=n.format;n.format=function(t){var i=t||(this.$u?"YYYY-MM-DDTHH:mm:ss[Z]":"");return r.call(this,i)},n.valueOf=function(){var t=this.$utils().u(this.$offset)?0:this.$offset+s;return this.$d.valueOf()-6e4*t},n.isUTC=function(){return !!this.$u},n.toISOString=function(){return this.toDate().toISOString()},n.toString=function(){return this.toDate().toUTCString()};}});
  7142. });
  7143. var advancedFormat = createCommonjsModule(function (module, exports) {
  7144. !function(e,t){module.exports=t();}(commonjsGlobal,function(){return function(e,t,r){var n=t.prototype,o=n.format;r.en.ordinal=function(e){var t=["th","st","nd","rd"],r=e%100;return "["+e+(t[(r-20)%10]||t[r]||t[0])+"]"},n.format=function(e){var t=this,r=this.$locale(),n=this.$utils(),a=(e||"YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|gggg|Do|X|x|k{1,2}|S/g,function(e){switch(e){case"Q":return Math.ceil((t.$M+1)/3);case"Do":return r.ordinal(t.$D);case"gggg":return t.weekYear();case"wo":return r.ordinal(t.week(),"W");case"w":case"ww":return n.s(t.week(),"w"===e?1:2,"0");case"k":case"kk":return n.s(String(0===t.$H?24:t.$H),"k"===e?1:2,"0");case"X":return Math.floor(t.$d.getTime()/1e3);case"x":return t.$d.getTime();default:return e}});return o.bind(this)(a)};}});
  7145. });
  7146. var weekOfYear = createCommonjsModule(function (module, exports) {
  7147. !function(e,t){module.exports=t();}(commonjsGlobal,function(){var e="week",t="year";return function(i,n){var r=n.prototype;r.week=function(i){if(void 0===i&&(i=null),null!==i)return this.add(7*(i-this.week()),"day");var n=this.$locale().yearStart||1;if(11===this.month()&&this.date()>25){var r=this.startOf(t).add(1,t).date(n),f=this.endOf(e);if(r.isBefore(f))return 1}var s=this.startOf(t).date(n).startOf(e).subtract(1,"millisecond"),a=this.diff(s,e,!0);return a<0?this.startOf("week").week():Math.ceil(a)},r.weeks=function(e){return void 0===e&&(e=null),this.week(e)};}});
  7148. });
  7149. /**
  7150. * Gantt-Schedule-Timeline-Calendar
  7151. *
  7152. * @copyright Rafal Pospiech <https://neuronet.io>
  7153. * @author Rafal Pospiech <neuronet.io@gmail.com>
  7154. * @package gantt-schedule-timeline-calendar
  7155. * @license AGPL-3.0
  7156. */
  7157. dayjs_min.extend(advancedFormat);
  7158. dayjs_min.extend(weekOfYear);
  7159. class TimeApi {
  7160. constructor(state) {
  7161. this.utcMode = false;
  7162. this.state = state;
  7163. this.locale = state.get('config.locale');
  7164. this.utcMode = state.get('config.utcMode');
  7165. if (this.utcMode) {
  7166. dayjs_min.extend(utc);
  7167. }
  7168. // @ts-ignore
  7169. dayjs_min.locale(this.locale, null, true);
  7170. }
  7171. date(time) {
  7172. const _dayjs = this.utcMode ? dayjs_min.utc : dayjs_min;
  7173. return time ? _dayjs(time).locale(this.locale.name) : _dayjs().locale(this.locale.name);
  7174. }
  7175. addAdditionalSpace(time) {
  7176. if (time.additionalSpaces && time.additionalSpaces[time.period]) {
  7177. const add = time.additionalSpaces[time.period];
  7178. if (add.before) {
  7179. time.finalFrom = this.date(time.from)
  7180. .subtract(add.before, add.period)
  7181. .valueOf();
  7182. }
  7183. if (add.after) {
  7184. time.finalTo = this.date(time.to)
  7185. .add(add.after, add.period)
  7186. .valueOf();
  7187. }
  7188. }
  7189. return time;
  7190. }
  7191. recalculateFromTo(time) {
  7192. const period = time.period;
  7193. time = Object.assign({}, time);
  7194. time.from = +time.from;
  7195. time.to = +time.to;
  7196. let from = Number.MAX_SAFE_INTEGER, to = 0;
  7197. const items = this.state.get('config.chart.items');
  7198. if (Object.keys(items).length === 0) {
  7199. return time;
  7200. }
  7201. if (time.from === 0 || time.to === 0) {
  7202. for (const itemId in items) {
  7203. const item = items[itemId];
  7204. if (item.time.start < from && item.time.start) {
  7205. from = item.time.start;
  7206. }
  7207. if (item.time.end > to) {
  7208. to = item.time.end;
  7209. }
  7210. }
  7211. if (time.from === 0) {
  7212. time.from = this.date(from)
  7213. .startOf(period)
  7214. .valueOf();
  7215. }
  7216. if (time.to === 0) {
  7217. time.to = this.date(to)
  7218. .endOf(period)
  7219. .valueOf();
  7220. }
  7221. }
  7222. time.finalFrom = time.from;
  7223. time.finalTo = time.to;
  7224. time = this.addAdditionalSpace(time);
  7225. return time;
  7226. }
  7227. getCenter(time) {
  7228. return time.leftGlobal + (time.rightGlobal - time.leftGlobal) / 2;
  7229. }
  7230. timeToPixelOffset(milliseconds) {
  7231. const timePerPixel = this.state.get('_internal.chart.time.timePerPixel') || 1;
  7232. return milliseconds / timePerPixel;
  7233. }
  7234. globalTimeToViewPixelOffset(milliseconds, withCompensation = false) {
  7235. const time = this.state.get('_internal.chart.time');
  7236. let xCompensation = this.state.get('config.scroll.compensation.x') || 0;
  7237. const viewPixelOffset = (milliseconds - time.leftGlobal) / time.timePerPixel;
  7238. if (withCompensation)
  7239. return viewPixelOffset + xCompensation;
  7240. return viewPixelOffset;
  7241. }
  7242. }
  7243. // forked from https://github.com/joonhocho/superwild
  7244. function Matcher(pattern, wchar = '*') {
  7245. this.wchar = wchar;
  7246. this.pattern = pattern;
  7247. this.segments = [];
  7248. this.starCount = 0;
  7249. this.minLength = 0;
  7250. this.maxLength = 0;
  7251. this.segStartIndex = 0;
  7252. for (let i = 0, len = pattern.length; i < len; i += 1) {
  7253. const char = pattern[i];
  7254. if (char === wchar) {
  7255. this.starCount += 1;
  7256. if (i > this.segStartIndex) {
  7257. this.segments.push(pattern.substring(this.segStartIndex, i));
  7258. }
  7259. this.segments.push(char);
  7260. this.segStartIndex = i + 1;
  7261. }
  7262. }
  7263. if (this.segStartIndex < pattern.length) {
  7264. this.segments.push(pattern.substring(this.segStartIndex));
  7265. }
  7266. if (this.starCount) {
  7267. this.minLength = pattern.length - this.starCount;
  7268. this.maxLength = Infinity;
  7269. }
  7270. else {
  7271. this.maxLength = this.minLength = pattern.length;
  7272. }
  7273. }
  7274. Matcher.prototype.match = function match(match) {
  7275. if (this.pattern === this.wchar) {
  7276. return true;
  7277. }
  7278. if (this.segments.length === 0) {
  7279. return this.pattern === match;
  7280. }
  7281. const { length } = match;
  7282. if (length < this.minLength || length > this.maxLength) {
  7283. return false;
  7284. }
  7285. let segLeftIndex = 0;
  7286. let segRightIndex = this.segments.length - 1;
  7287. let rightPos = match.length - 1;
  7288. let rightIsStar = false;
  7289. while (true) {
  7290. const segment = this.segments[segRightIndex];
  7291. segRightIndex -= 1;
  7292. if (segment === this.wchar) {
  7293. rightIsStar = true;
  7294. }
  7295. else {
  7296. const lastIndex = rightPos + 1 - segment.length;
  7297. const index = match.lastIndexOf(segment, lastIndex);
  7298. if (index === -1 || index > lastIndex) {
  7299. return false;
  7300. }
  7301. if (rightIsStar) {
  7302. rightPos = index - 1;
  7303. rightIsStar = false;
  7304. }
  7305. else {
  7306. if (index !== lastIndex) {
  7307. return false;
  7308. }
  7309. rightPos -= segment.length;
  7310. }
  7311. }
  7312. if (segLeftIndex > segRightIndex) {
  7313. break;
  7314. }
  7315. }
  7316. return true;
  7317. };
  7318. function WildcardObject(obj, delimeter, wildcard) {
  7319. this.obj = obj;
  7320. this.delimeter = delimeter;
  7321. this.wildcard = wildcard;
  7322. }
  7323. WildcardObject.prototype.simpleMatch = function simpleMatch(first, second) {
  7324. if (first === second)
  7325. return true;
  7326. if (first === this.wildcard)
  7327. return true;
  7328. const index = first.indexOf(this.wildcard);
  7329. if (index > -1) {
  7330. const end = first.substr(index + 1);
  7331. if (index === 0 || second.substring(0, index) === first.substring(0, index)) {
  7332. const len = end.length;
  7333. if (len > 0) {
  7334. return second.substr(-len) === end;
  7335. }
  7336. return true;
  7337. }
  7338. }
  7339. return false;
  7340. };
  7341. WildcardObject.prototype.match = function match(first, second) {
  7342. return (first === second ||
  7343. first === this.wildcard ||
  7344. second === this.wildcard ||
  7345. this.simpleMatch(first, second) ||
  7346. new Matcher(first).match(second));
  7347. };
  7348. WildcardObject.prototype.handleArray = function handleArray(wildcard, currentArr, partIndex, path, result = {}) {
  7349. let nextPartIndex = wildcard.indexOf(this.delimeter, partIndex);
  7350. let end = false;
  7351. if (nextPartIndex === -1) {
  7352. end = true;
  7353. nextPartIndex = wildcard.length;
  7354. }
  7355. const currentWildcardPath = wildcard.substring(partIndex, nextPartIndex);
  7356. let index = 0;
  7357. for (const item of currentArr) {
  7358. const key = index.toString();
  7359. const currentPath = path === '' ? key : path + this.delimeter + index;
  7360. if (currentWildcardPath === this.wildcard ||
  7361. currentWildcardPath === key ||
  7362. this.simpleMatch(currentWildcardPath, key)) {
  7363. end ? (result[currentPath] = item) : this.goFurther(wildcard, item, nextPartIndex + 1, currentPath, result);
  7364. }
  7365. index++;
  7366. }
  7367. return result;
  7368. };
  7369. WildcardObject.prototype.handleObject = function handleObject(wildcard, currentObj, partIndex, path, result = {}) {
  7370. let nextPartIndex = wildcard.indexOf(this.delimeter, partIndex);
  7371. let end = false;
  7372. if (nextPartIndex === -1) {
  7373. end = true;
  7374. nextPartIndex = wildcard.length;
  7375. }
  7376. const currentWildcardPath = wildcard.substring(partIndex, nextPartIndex);
  7377. for (let key in currentObj) {
  7378. key = key.toString();
  7379. const currentPath = path === '' ? key : path + this.delimeter + key;
  7380. if (currentWildcardPath === this.wildcard ||
  7381. currentWildcardPath === key ||
  7382. this.simpleMatch(currentWildcardPath, key)) {
  7383. end
  7384. ? (result[currentPath] = currentObj[key])
  7385. : this.goFurther(wildcard, currentObj[key], nextPartIndex + 1, currentPath, result);
  7386. }
  7387. }
  7388. return result;
  7389. };
  7390. WildcardObject.prototype.goFurther = function goFurther(wildcard, currentObj, partIndex, currentPath, result = {}) {
  7391. if (Array.isArray(currentObj)) {
  7392. return this.handleArray(wildcard, currentObj, partIndex, currentPath, result);
  7393. }
  7394. return this.handleObject(wildcard, currentObj, partIndex, currentPath, result);
  7395. };
  7396. WildcardObject.prototype.get = function get(wildcard) {
  7397. return this.goFurther(wildcard, this.obj, 0, '');
  7398. };
  7399. class ObjectPath {
  7400. static get(path, obj, copiedPath = null) {
  7401. if (copiedPath === null) {
  7402. copiedPath = path.slice();
  7403. }
  7404. if (copiedPath.length === 0 || typeof obj === "undefined") {
  7405. return obj;
  7406. }
  7407. const currentPath = copiedPath.shift();
  7408. if (!obj.hasOwnProperty(currentPath)) {
  7409. return undefined;
  7410. }
  7411. if (copiedPath.length === 0) {
  7412. return obj[currentPath];
  7413. }
  7414. return ObjectPath.get(path, obj[currentPath], copiedPath);
  7415. }
  7416. static set(path, newValue, obj, copiedPath = null) {
  7417. if (copiedPath === null) {
  7418. copiedPath = path.slice();
  7419. }
  7420. if (copiedPath.length === 0) {
  7421. for (const key in obj) {
  7422. delete obj[key];
  7423. }
  7424. for (const key in newValue) {
  7425. obj[key] = newValue[key];
  7426. }
  7427. return;
  7428. }
  7429. const currentPath = copiedPath.shift();
  7430. if (copiedPath.length === 0) {
  7431. obj[currentPath] = newValue;
  7432. return;
  7433. }
  7434. if (!obj) {
  7435. obj = {};
  7436. }
  7437. if (!obj.hasOwnProperty(currentPath)) {
  7438. obj[currentPath] = {};
  7439. }
  7440. ObjectPath.set(path, newValue, obj[currentPath], copiedPath);
  7441. }
  7442. }
  7443. function log(message, info) {
  7444. console.debug(message, info);
  7445. }
  7446. const defaultOptions$1 = {
  7447. delimeter: `.`,
  7448. notRecursive: `;`,
  7449. param: `:`,
  7450. wildcard: `*`,
  7451. log
  7452. };
  7453. const defaultListenerOptions = {
  7454. bulk: false,
  7455. debug: false,
  7456. source: "",
  7457. data: undefined
  7458. };
  7459. const defaultUpdateOptions = {
  7460. only: [],
  7461. source: "",
  7462. debug: false,
  7463. data: undefined,
  7464. updateAfter: false
  7465. };
  7466. class DeepState {
  7467. constructor(data = {}, options = defaultOptions$1) {
  7468. this.listeners = new Map();
  7469. this.waitingListeners = new Map();
  7470. this.data = data;
  7471. this.options = Object.assign(Object.assign({}, defaultOptions$1), options);
  7472. this.id = 0;
  7473. this.pathGet = ObjectPath.get;
  7474. this.pathSet = ObjectPath.set;
  7475. this.scan = new WildcardObject(this.data, this.options.delimeter, this.options.wildcard);
  7476. }
  7477. getListeners() {
  7478. return this.listeners;
  7479. }
  7480. destroy() {
  7481. this.data = undefined;
  7482. this.listeners = new Map();
  7483. }
  7484. match(first, second) {
  7485. if (first === second)
  7486. return true;
  7487. if (first === this.options.wildcard || second === this.options.wildcard)
  7488. return true;
  7489. return this.scan.match(first, second);
  7490. }
  7491. getIndicesOf(searchStr, str) {
  7492. const searchStrLen = searchStr.length;
  7493. if (searchStrLen == 0) {
  7494. return [];
  7495. }
  7496. let startIndex = 0, index, indices = [];
  7497. while ((index = str.indexOf(searchStr, startIndex)) > -1) {
  7498. indices.push(index);
  7499. startIndex = index + searchStrLen;
  7500. }
  7501. return indices;
  7502. }
  7503. getIndicesCount(searchStr, str) {
  7504. const searchStrLen = searchStr.length;
  7505. if (searchStrLen == 0) {
  7506. return 0;
  7507. }
  7508. let startIndex = 0, index, indices = 0;
  7509. while ((index = str.indexOf(searchStr, startIndex)) > -1) {
  7510. indices++;
  7511. startIndex = index + searchStrLen;
  7512. }
  7513. return indices;
  7514. }
  7515. cutPath(longer, shorter) {
  7516. longer = this.cleanNotRecursivePath(longer);
  7517. shorter = this.cleanNotRecursivePath(shorter);
  7518. const shorterPartsLen = this.getIndicesCount(this.options.delimeter, shorter);
  7519. const longerParts = this.getIndicesOf(this.options.delimeter, longer);
  7520. return longer.substr(0, longerParts[shorterPartsLen]);
  7521. }
  7522. trimPath(path) {
  7523. path = this.cleanNotRecursivePath(path);
  7524. if (path.charAt(0) === this.options.delimeter) {
  7525. return path.substr(1);
  7526. }
  7527. return path;
  7528. }
  7529. split(path) {
  7530. return path === "" ? [] : path.split(this.options.delimeter);
  7531. }
  7532. isWildcard(path) {
  7533. return path.includes(this.options.wildcard);
  7534. }
  7535. isNotRecursive(path) {
  7536. return path.endsWith(this.options.notRecursive);
  7537. }
  7538. cleanNotRecursivePath(path) {
  7539. return this.isNotRecursive(path) ? path.substring(0, path.length - 1) : path;
  7540. }
  7541. hasParams(path) {
  7542. return path.includes(this.options.param);
  7543. }
  7544. getParamsInfo(path) {
  7545. let paramsInfo = { replaced: "", original: path, params: {} };
  7546. let partIndex = 0;
  7547. let fullReplaced = [];
  7548. for (const part of this.split(path)) {
  7549. paramsInfo.params[partIndex] = {
  7550. original: part,
  7551. replaced: "",
  7552. name: ""
  7553. };
  7554. const reg = new RegExp(`\\${this.options.param}([^\\${this.options.delimeter}\\${this.options.param}]+)`, "g");
  7555. let param = reg.exec(part);
  7556. if (param) {
  7557. paramsInfo.params[partIndex].name = param[1];
  7558. }
  7559. else {
  7560. delete paramsInfo.params[partIndex];
  7561. fullReplaced.push(part);
  7562. partIndex++;
  7563. continue;
  7564. }
  7565. reg.lastIndex = 0;
  7566. paramsInfo.params[partIndex].replaced = part.replace(reg, this.options.wildcard);
  7567. fullReplaced.push(paramsInfo.params[partIndex].replaced);
  7568. partIndex++;
  7569. }
  7570. paramsInfo.replaced = fullReplaced.join(this.options.delimeter);
  7571. return paramsInfo;
  7572. }
  7573. getParams(paramsInfo, path) {
  7574. if (!paramsInfo) {
  7575. return undefined;
  7576. }
  7577. const split = this.split(path);
  7578. const result = {};
  7579. for (const partIndex in paramsInfo.params) {
  7580. const param = paramsInfo.params[partIndex];
  7581. result[param.name] = split[partIndex];
  7582. }
  7583. return result;
  7584. }
  7585. waitForAll(userPaths, fn) {
  7586. const paths = {};
  7587. for (let path of userPaths) {
  7588. paths[path] = { dirty: false };
  7589. if (this.hasParams(path)) {
  7590. paths[path].paramsInfo = this.getParamsInfo(path);
  7591. }
  7592. paths[path].isWildcard = this.isWildcard(path);
  7593. paths[path].isRecursive = !this.isNotRecursive(path);
  7594. }
  7595. this.waitingListeners.set(userPaths, { fn, paths });
  7596. fn(paths);
  7597. return function unsubscribe() {
  7598. this.waitingListeners.delete(userPaths);
  7599. };
  7600. }
  7601. executeWaitingListeners(updatePath) {
  7602. for (const waitingListener of this.waitingListeners.values()) {
  7603. const { fn, paths } = waitingListener;
  7604. let dirty = 0;
  7605. let all = 0;
  7606. for (let path in paths) {
  7607. const pathInfo = paths[path];
  7608. let match = false;
  7609. if (pathInfo.isRecursive)
  7610. updatePath = this.cutPath(updatePath, path);
  7611. if (pathInfo.isWildcard && this.match(path, updatePath))
  7612. match = true;
  7613. if (updatePath === path)
  7614. match = true;
  7615. if (match) {
  7616. pathInfo.dirty = true;
  7617. }
  7618. if (pathInfo.dirty) {
  7619. dirty++;
  7620. }
  7621. all++;
  7622. }
  7623. if (dirty === all) {
  7624. fn(paths);
  7625. }
  7626. }
  7627. }
  7628. subscribeAll(userPaths, fn, options = defaultListenerOptions) {
  7629. let unsubscribers = [];
  7630. for (const userPath of userPaths) {
  7631. unsubscribers.push(this.subscribe(userPath, fn, options));
  7632. }
  7633. return function unsubscribe() {
  7634. for (const unsubscribe of unsubscribers) {
  7635. unsubscribe();
  7636. }
  7637. };
  7638. }
  7639. getCleanListenersCollection(values = {}) {
  7640. return Object.assign({ listeners: new Map(), isRecursive: false, isWildcard: false, hasParams: false, match: undefined, paramsInfo: undefined, path: undefined, count: 0 }, values);
  7641. }
  7642. getCleanListener(fn, options = defaultListenerOptions) {
  7643. return {
  7644. fn,
  7645. options: Object.assign(Object.assign({}, defaultListenerOptions), options)
  7646. };
  7647. }
  7648. getListenerCollectionMatch(listenerPath, isRecursive, isWildcard) {
  7649. listenerPath = this.cleanNotRecursivePath(listenerPath);
  7650. const self = this;
  7651. return function listenerCollectionMatch(path) {
  7652. if (isRecursive)
  7653. path = self.cutPath(path, listenerPath);
  7654. if (isWildcard && self.match(listenerPath, path))
  7655. return true;
  7656. return listenerPath === path;
  7657. };
  7658. }
  7659. getListenersCollection(listenerPath, listener) {
  7660. if (this.listeners.has(listenerPath)) {
  7661. let listenersCollection = this.listeners.get(listenerPath);
  7662. listenersCollection.listeners.set(++this.id, listener);
  7663. return listenersCollection;
  7664. }
  7665. let collCfg = {
  7666. isRecursive: true,
  7667. isWildcard: false,
  7668. hasParams: false,
  7669. paramsInfo: undefined,
  7670. originalPath: listenerPath,
  7671. path: listenerPath
  7672. };
  7673. if (this.hasParams(collCfg.path)) {
  7674. collCfg.paramsInfo = this.getParamsInfo(collCfg.path);
  7675. collCfg.path = collCfg.paramsInfo.replaced;
  7676. collCfg.hasParams = true;
  7677. }
  7678. collCfg.isWildcard = this.isWildcard(collCfg.path);
  7679. if (this.isNotRecursive(collCfg.path)) {
  7680. collCfg.isRecursive = false;
  7681. }
  7682. let listenersCollection = this.getCleanListenersCollection(Object.assign(Object.assign({}, collCfg), { match: this.getListenerCollectionMatch(collCfg.path, collCfg.isRecursive, collCfg.isWildcard) }));
  7683. this.id++;
  7684. listenersCollection.listeners.set(this.id, listener);
  7685. this.listeners.set(collCfg.path, listenersCollection);
  7686. return listenersCollection;
  7687. }
  7688. subscribe(listenerPath, fn, options = defaultListenerOptions, type = "subscribe") {
  7689. let listener = this.getCleanListener(fn, options);
  7690. const listenersCollection = this.getListenersCollection(listenerPath, listener);
  7691. listenersCollection.count++;
  7692. listenerPath = listenersCollection.path;
  7693. if (!listenersCollection.isWildcard) {
  7694. fn(this.pathGet(this.split(this.cleanNotRecursivePath(listenerPath)), this.data), {
  7695. type,
  7696. listener,
  7697. listenersCollection,
  7698. path: {
  7699. listener: listenerPath,
  7700. update: undefined,
  7701. resolved: this.cleanNotRecursivePath(listenerPath)
  7702. },
  7703. params: this.getParams(listenersCollection.paramsInfo, listenerPath),
  7704. options
  7705. });
  7706. }
  7707. else {
  7708. const paths = this.scan.get(this.cleanNotRecursivePath(listenerPath));
  7709. if (options.bulk) {
  7710. const bulkValue = [];
  7711. for (const path in paths) {
  7712. bulkValue.push({
  7713. path,
  7714. params: this.getParams(listenersCollection.paramsInfo, path),
  7715. value: paths[path]
  7716. });
  7717. }
  7718. fn(bulkValue, {
  7719. type,
  7720. listener,
  7721. listenersCollection,
  7722. path: {
  7723. listener: listenerPath,
  7724. update: undefined,
  7725. resolved: undefined
  7726. },
  7727. options,
  7728. params: undefined
  7729. });
  7730. }
  7731. else {
  7732. for (const path in paths) {
  7733. fn(paths[path], {
  7734. type,
  7735. listener,
  7736. listenersCollection,
  7737. path: {
  7738. listener: listenerPath,
  7739. update: undefined,
  7740. resolved: this.cleanNotRecursivePath(path)
  7741. },
  7742. params: this.getParams(listenersCollection.paramsInfo, path),
  7743. options
  7744. });
  7745. }
  7746. }
  7747. }
  7748. this.debugSubscribe(listener, listenersCollection, listenerPath);
  7749. return this.unsubscribe(listenerPath, this.id);
  7750. }
  7751. unsubscribe(path, id) {
  7752. const listeners = this.listeners;
  7753. const listenersCollection = listeners.get(path);
  7754. return function unsub() {
  7755. listenersCollection.listeners.delete(id);
  7756. listenersCollection.count--;
  7757. if (listenersCollection.count === 0) {
  7758. listeners.delete(path);
  7759. }
  7760. };
  7761. }
  7762. same(newValue, oldValue) {
  7763. return ((["number", "string", "undefined", "boolean"].includes(typeof newValue) || newValue === null) &&
  7764. oldValue === newValue);
  7765. }
  7766. notifyListeners(listeners, exclude = [], returnNotified = true) {
  7767. const alreadyNotified = [];
  7768. for (const path in listeners) {
  7769. let { single, bulk } = listeners[path];
  7770. for (const singleListener of single) {
  7771. if (exclude.includes(singleListener))
  7772. continue;
  7773. const time = this.debugTime(singleListener);
  7774. singleListener.listener.fn(singleListener.value(), singleListener.eventInfo);
  7775. if (returnNotified)
  7776. alreadyNotified.push(singleListener);
  7777. this.debugListener(time, singleListener);
  7778. }
  7779. for (const bulkListener of bulk) {
  7780. if (exclude.includes(bulkListener))
  7781. continue;
  7782. const time = this.debugTime(bulkListener);
  7783. const bulkValue = [];
  7784. for (const bulk of bulkListener.value) {
  7785. bulkValue.push(Object.assign(Object.assign({}, bulk), { value: bulk.value() }));
  7786. }
  7787. bulkListener.listener.fn(bulkValue, bulkListener.eventInfo);
  7788. if (returnNotified)
  7789. alreadyNotified.push(bulkListener);
  7790. this.debugListener(time, bulkListener);
  7791. }
  7792. }
  7793. return alreadyNotified;
  7794. }
  7795. getSubscribedListeners(updatePath, newValue, options, type = "update", originalPath = null) {
  7796. options = Object.assign(Object.assign({}, defaultUpdateOptions), options);
  7797. const listeners = {};
  7798. for (let [listenerPath, listenersCollection] of this.listeners) {
  7799. listeners[listenerPath] = { single: [], bulk: [], bulkData: [] };
  7800. if (listenersCollection.match(updatePath)) {
  7801. const params = listenersCollection.paramsInfo
  7802. ? this.getParams(listenersCollection.paramsInfo, updatePath)
  7803. : undefined;
  7804. const value = listenersCollection.isRecursive || listenersCollection.isWildcard
  7805. ? () => this.get(this.cutPath(updatePath, listenerPath))
  7806. : () => newValue;
  7807. const bulkValue = [{ value, path: updatePath, params }];
  7808. for (const listener of listenersCollection.listeners.values()) {
  7809. if (listener.options.bulk) {
  7810. listeners[listenerPath].bulk.push({
  7811. listener,
  7812. listenersCollection,
  7813. eventInfo: {
  7814. type,
  7815. listener,
  7816. path: {
  7817. listener: listenerPath,
  7818. update: originalPath ? originalPath : updatePath,
  7819. resolved: undefined
  7820. },
  7821. params,
  7822. options
  7823. },
  7824. value: bulkValue
  7825. });
  7826. }
  7827. else {
  7828. listeners[listenerPath].single.push({
  7829. listener,
  7830. listenersCollection,
  7831. eventInfo: {
  7832. type,
  7833. listener,
  7834. path: {
  7835. listener: listenerPath,
  7836. update: originalPath ? originalPath : updatePath,
  7837. resolved: this.cleanNotRecursivePath(updatePath)
  7838. },
  7839. params,
  7840. options
  7841. },
  7842. value
  7843. });
  7844. }
  7845. }
  7846. }
  7847. }
  7848. return listeners;
  7849. }
  7850. notifySubscribedListeners(updatePath, newValue, options, type = "update", originalPath = null) {
  7851. return this.notifyListeners(this.getSubscribedListeners(updatePath, newValue, options, type, originalPath));
  7852. }
  7853. getNestedListeners(updatePath, newValue, options, type = "update", originalPath = null) {
  7854. const listeners = {};
  7855. for (let [listenerPath, listenersCollection] of this.listeners) {
  7856. listeners[listenerPath] = { single: [], bulk: [] };
  7857. const currentCuttedPath = this.cutPath(listenerPath, updatePath);
  7858. if (this.match(currentCuttedPath, updatePath)) {
  7859. const restPath = this.trimPath(listenerPath.substr(currentCuttedPath.length));
  7860. const values = new WildcardObject(newValue, this.options.delimeter, this.options.wildcard).get(restPath);
  7861. const params = listenersCollection.paramsInfo
  7862. ? this.getParams(listenersCollection.paramsInfo, updatePath)
  7863. : undefined;
  7864. const bulk = [];
  7865. const bulkListeners = {};
  7866. for (const currentRestPath in values) {
  7867. const value = () => values[currentRestPath];
  7868. const fullPath = [updatePath, currentRestPath].join(this.options.delimeter);
  7869. for (const [listenerId, listener] of listenersCollection.listeners) {
  7870. const eventInfo = {
  7871. type,
  7872. listener,
  7873. listenersCollection,
  7874. path: {
  7875. listener: listenerPath,
  7876. update: originalPath ? originalPath : updatePath,
  7877. resolved: this.cleanNotRecursivePath(fullPath)
  7878. },
  7879. params,
  7880. options
  7881. };
  7882. if (listener.options.bulk) {
  7883. bulk.push({ value, path: fullPath, params });
  7884. bulkListeners[listenerId] = listener;
  7885. }
  7886. else {
  7887. listeners[listenerPath].single.push({
  7888. listener,
  7889. listenersCollection,
  7890. eventInfo,
  7891. value
  7892. });
  7893. }
  7894. }
  7895. }
  7896. for (const listenerId in bulkListeners) {
  7897. const listener = bulkListeners[listenerId];
  7898. const eventInfo = {
  7899. type,
  7900. listener,
  7901. listenersCollection,
  7902. path: {
  7903. listener: listenerPath,
  7904. update: updatePath,
  7905. resolved: undefined
  7906. },
  7907. options,
  7908. params
  7909. };
  7910. listeners[listenerPath].bulk.push({
  7911. listener,
  7912. listenersCollection,
  7913. eventInfo,
  7914. value: bulk
  7915. });
  7916. }
  7917. }
  7918. }
  7919. return listeners;
  7920. }
  7921. notifyNestedListeners(updatePath, newValue, options, type = "update", alreadyNotified, originalPath = null) {
  7922. return this.notifyListeners(this.getNestedListeners(updatePath, newValue, options, type, originalPath), alreadyNotified, false);
  7923. }
  7924. getNotifyOnlyListeners(updatePath, newValue, options, type = "update", originalPath = null) {
  7925. const listeners = {};
  7926. if (typeof options.only !== "object" ||
  7927. !Array.isArray(options.only) ||
  7928. typeof options.only[0] === "undefined" ||
  7929. !this.canBeNested(newValue)) {
  7930. return listeners;
  7931. }
  7932. for (const notifyPath of options.only) {
  7933. const wildcardScan = new WildcardObject(newValue, this.options.delimeter, this.options.wildcard).get(notifyPath);
  7934. listeners[notifyPath] = { bulk: [], single: [] };
  7935. for (const wildcardPath in wildcardScan) {
  7936. const fullPath = updatePath + this.options.delimeter + wildcardPath;
  7937. for (const [listenerPath, listenersCollection] of this.listeners) {
  7938. const params = listenersCollection.paramsInfo
  7939. ? this.getParams(listenersCollection.paramsInfo, fullPath)
  7940. : undefined;
  7941. if (this.match(listenerPath, fullPath)) {
  7942. const value = () => wildcardScan[wildcardPath];
  7943. const bulkValue = [{ value, path: fullPath, params }];
  7944. for (const listener of listenersCollection.listeners.values()) {
  7945. const eventInfo = {
  7946. type,
  7947. listener,
  7948. listenersCollection,
  7949. path: {
  7950. listener: listenerPath,
  7951. update: originalPath ? originalPath : updatePath,
  7952. resolved: this.cleanNotRecursivePath(fullPath)
  7953. },
  7954. params,
  7955. options
  7956. };
  7957. if (listener.options.bulk) {
  7958. if (!listeners[notifyPath].bulk.some(bulkListener => bulkListener.listener === listener)) {
  7959. listeners[notifyPath].bulk.push({
  7960. listener,
  7961. listenersCollection,
  7962. eventInfo,
  7963. value: bulkValue
  7964. });
  7965. }
  7966. }
  7967. else {
  7968. listeners[notifyPath].single.push({
  7969. listener,
  7970. listenersCollection,
  7971. eventInfo,
  7972. value
  7973. });
  7974. }
  7975. }
  7976. }
  7977. }
  7978. }
  7979. }
  7980. return listeners;
  7981. }
  7982. notifyOnly(updatePath, newValue, options, type = "update", originalPath = "") {
  7983. return (typeof this.notifyListeners(this.getNotifyOnlyListeners(updatePath, newValue, options, type, originalPath))[0] !==
  7984. "undefined");
  7985. }
  7986. canBeNested(newValue) {
  7987. return typeof newValue === "object" && newValue !== null;
  7988. }
  7989. getUpdateValues(oldValue, split, fn) {
  7990. if (typeof oldValue === "object" && oldValue !== null) {
  7991. Array.isArray(oldValue) ? (oldValue = oldValue.slice()) : (oldValue = Object.assign({}, oldValue));
  7992. }
  7993. let newValue = fn;
  7994. if (typeof fn === "function") {
  7995. newValue = fn(this.pathGet(split, this.data));
  7996. }
  7997. return { newValue, oldValue };
  7998. }
  7999. wildcardUpdate(updatePath, fn, options = defaultUpdateOptions) {
  8000. options = Object.assign(Object.assign({}, defaultUpdateOptions), options);
  8001. const scanned = this.scan.get(updatePath);
  8002. const bulk = {};
  8003. for (const path in scanned) {
  8004. const split = this.split(path);
  8005. const { oldValue, newValue } = this.getUpdateValues(scanned[path], split, fn);
  8006. if (!this.same(newValue, oldValue))
  8007. bulk[path] = newValue;
  8008. }
  8009. const groupedListenersPack = [];
  8010. const waitingPaths = [];
  8011. for (const path in bulk) {
  8012. const newValue = bulk[path];
  8013. if (options.only.length) {
  8014. groupedListenersPack.push(this.getNotifyOnlyListeners(path, newValue, options, "update", updatePath));
  8015. }
  8016. else {
  8017. groupedListenersPack.push(this.getSubscribedListeners(path, newValue, options, "update", updatePath));
  8018. this.canBeNested(newValue) &&
  8019. groupedListenersPack.push(this.getNestedListeners(path, newValue, options, "update", updatePath));
  8020. }
  8021. options.debug && this.options.log("Wildcard update", { path, newValue });
  8022. this.pathSet(this.split(path), newValue, this.data);
  8023. waitingPaths.push(path);
  8024. }
  8025. let alreadyNotified = [];
  8026. for (const groupedListeners of groupedListenersPack) {
  8027. alreadyNotified = [...alreadyNotified, ...this.notifyListeners(groupedListeners, alreadyNotified)];
  8028. }
  8029. for (const path of waitingPaths) {
  8030. this.executeWaitingListeners(path);
  8031. }
  8032. }
  8033. update(updatePath, fn, options = defaultUpdateOptions) {
  8034. if (this.isWildcard(updatePath)) {
  8035. return this.wildcardUpdate(updatePath, fn, options);
  8036. }
  8037. const split = this.split(updatePath);
  8038. const { oldValue, newValue } = this.getUpdateValues(this.pathGet(split, this.data), split, fn);
  8039. if (options.debug) {
  8040. this.options.log(`Updating ${updatePath} ${options.source ? `from ${options.source}` : ""}`, {
  8041. oldValue,
  8042. newValue
  8043. });
  8044. }
  8045. if (this.same(newValue, oldValue)) {
  8046. return newValue;
  8047. }
  8048. if (!options.updateAfter) {
  8049. this.pathSet(split, newValue, this.data);
  8050. }
  8051. options = Object.assign(Object.assign({}, defaultUpdateOptions), options);
  8052. if (options.only === null) {
  8053. return newValue;
  8054. }
  8055. if (options.only.length) {
  8056. this.notifyOnly(updatePath, newValue, options);
  8057. this.executeWaitingListeners(updatePath);
  8058. return newValue;
  8059. }
  8060. const alreadyNotified = this.notifySubscribedListeners(updatePath, newValue, options);
  8061. if (this.canBeNested(newValue)) {
  8062. this.notifyNestedListeners(updatePath, newValue, options, "update", alreadyNotified);
  8063. }
  8064. this.executeWaitingListeners(updatePath);
  8065. if (options.updateAfter) {
  8066. this.pathSet(split, newValue, this.data);
  8067. }
  8068. return newValue;
  8069. }
  8070. get(userPath = undefined) {
  8071. if (typeof userPath === "undefined" || userPath === "") {
  8072. return this.data;
  8073. }
  8074. return this.pathGet(this.split(userPath), this.data);
  8075. }
  8076. debugSubscribe(listener, listenersCollection, listenerPath) {
  8077. if (listener.options.debug) {
  8078. this.options.log("listener subscribed", {
  8079. listenerPath,
  8080. listener,
  8081. listenersCollection
  8082. });
  8083. }
  8084. }
  8085. debugListener(time, groupedListener) {
  8086. if (groupedListener.eventInfo.options.debug || groupedListener.listener.options.debug) {
  8087. this.options.log("Listener fired", {
  8088. time: Date.now() - time,
  8089. info: groupedListener
  8090. });
  8091. }
  8092. }
  8093. debugTime(groupedListener) {
  8094. return groupedListener.listener.options.debug || groupedListener.eventInfo.options.debug ? Date.now() : 0;
  8095. }
  8096. }
  8097. /**
  8098. * Schedule - a throttle function that uses requestAnimationFrame to limit the rate at which a function is called.
  8099. *
  8100. * @param {function} fn
  8101. * @returns {function}
  8102. */
  8103. /**
  8104. * Is object - helper function to determine if specified variable is an object
  8105. *
  8106. * @param {any} item
  8107. * @returns {boolean}
  8108. */
  8109. function isObject$1(item) {
  8110. return item && typeof item === 'object' && !Array.isArray(item);
  8111. }
  8112. /**
  8113. * Merge deep - helper function which will merge objects recursively - creating brand new one - like clone
  8114. *
  8115. * @param {object} target
  8116. * @params {object} sources
  8117. * @returns {object}
  8118. */
  8119. function mergeDeep$1(target, ...sources) {
  8120. const source = sources.shift();
  8121. if (isObject$1(target) && isObject$1(source)) {
  8122. for (const key in source) {
  8123. if (isObject$1(source[key])) {
  8124. if (typeof target[key] === 'undefined') {
  8125. target[key] = {};
  8126. }
  8127. target[key] = mergeDeep$1(target[key], source[key]);
  8128. }
  8129. else if (Array.isArray(source[key])) {
  8130. target[key] = [];
  8131. for (let item of source[key]) {
  8132. if (isObject$1(item)) {
  8133. target[key].push(mergeDeep$1({}, item));
  8134. continue;
  8135. }
  8136. target[key].push(item);
  8137. }
  8138. }
  8139. else {
  8140. target[key] = source[key];
  8141. }
  8142. }
  8143. }
  8144. if (!sources.length) {
  8145. return target;
  8146. }
  8147. return mergeDeep$1(target, ...sources);
  8148. }
  8149. /**
  8150. * Api functions
  8151. *
  8152. * @copyright Rafal Pospiech <https://neuronet.io>
  8153. * @author Rafal Pospiech <neuronet.io@gmail.com>
  8154. * @package gantt-schedule-timeline-calendar
  8155. * @license AGPL-3.0
  8156. */
  8157. const lib = 'gantt-schedule-timeline-calendar';
  8158. function mergeActions(userConfig, defaultConfig) {
  8159. const defaultConfigActions = mergeDeep$1({}, defaultConfig.actions);
  8160. const userActions = mergeDeep$1({}, userConfig.actions);
  8161. let allActionNames = [...Object.keys(defaultConfigActions), ...Object.keys(userActions)];
  8162. allActionNames = allActionNames.filter(i => allActionNames.includes(i));
  8163. const actions = {};
  8164. for (const actionName of allActionNames) {
  8165. actions[actionName] = [];
  8166. if (typeof defaultConfigActions[actionName] !== 'undefined' && Array.isArray(defaultConfigActions[actionName])) {
  8167. actions[actionName] = [...defaultConfigActions[actionName]];
  8168. }
  8169. if (typeof userActions[actionName] !== 'undefined' && Array.isArray(userActions[actionName])) {
  8170. actions[actionName] = [...actions[actionName], ...userActions[actionName]];
  8171. }
  8172. }
  8173. delete userConfig.actions;
  8174. delete defaultConfig.actions;
  8175. return actions;
  8176. }
  8177. function stateFromConfig(userConfig) {
  8178. const defaultConfig$1 = defaultConfig();
  8179. const actions = mergeActions(userConfig, defaultConfig$1);
  8180. const state = { config: mergeDeep$1({}, defaultConfig$1, userConfig) };
  8181. state.config.actions = actions;
  8182. // @ts-ignore
  8183. return (this.state = new DeepState(state, { delimeter: '.' }));
  8184. }
  8185. const publicApi = {
  8186. name: lib,
  8187. stateFromConfig,
  8188. mergeDeep: mergeDeep$1,
  8189. date(time) {
  8190. return time ? dayjs_min(time) : dayjs_min();
  8191. },
  8192. setPeriod(period) {
  8193. this.state.update('config.chart.time.period', period);
  8194. return this.state.get('config.chart.time.zoom');
  8195. },
  8196. dayjs: dayjs_min
  8197. };
  8198. function getInternalApi(state) {
  8199. let $state = state.get();
  8200. let unsubscribes = [];
  8201. const iconsCache = {};
  8202. const api = {
  8203. name: lib,
  8204. debug: false,
  8205. setVido(Vido) {
  8206. },
  8207. log(...args) {
  8208. if (this.debug) {
  8209. console.log.call(console, ...args);
  8210. }
  8211. },
  8212. mergeDeep: mergeDeep$1,
  8213. getClass(name) {
  8214. let simple = `${lib}__${name}`;
  8215. if (name === this.name) {
  8216. simple = this.name;
  8217. }
  8218. return simple;
  8219. },
  8220. allActions: [],
  8221. getActions(name) {
  8222. if (!this.allActions.includes(name))
  8223. this.allActions.push(name);
  8224. let actions = state.get('config.actions.' + name);
  8225. if (typeof actions === 'undefined') {
  8226. actions = [];
  8227. }
  8228. return actions.slice();
  8229. },
  8230. isItemInViewport(item, left, right) {
  8231. return ((item.time.start >= left && item.time.start < right) ||
  8232. (item.time.end >= left && item.time.end < right) ||
  8233. (item.time.start <= left && item.time.end >= right));
  8234. },
  8235. prepareItems(items) {
  8236. for (const item of items) {
  8237. item.time.start = +item.time.start;
  8238. item.time.end = +item.time.end;
  8239. item.id = String(item.id);
  8240. }
  8241. return items;
  8242. },
  8243. fillEmptyRowValues(rows) {
  8244. let top = 0;
  8245. for (const rowId in rows) {
  8246. const row = rows[rowId];
  8247. row._internal = {
  8248. parents: [],
  8249. children: [],
  8250. items: []
  8251. };
  8252. if (typeof row.height !== 'number') {
  8253. row.height = $state.config.list.rowHeight;
  8254. }
  8255. if (typeof row.expanded !== 'boolean') {
  8256. row.expanded = false;
  8257. }
  8258. row.top = top;
  8259. top += row.height;
  8260. }
  8261. return rows;
  8262. },
  8263. generateParents(rows, parentName = 'parentId') {
  8264. const parents = {};
  8265. for (const row of rows) {
  8266. const parentId = row[parentName] !== undefined && row[parentName] !== null ? row[parentName] : '';
  8267. if (parents[parentId] === undefined) {
  8268. parents[parentId] = {};
  8269. }
  8270. parents[parentId][row.id] = row;
  8271. }
  8272. return parents;
  8273. },
  8274. fastTree(rowParents, node, parents = []) {
  8275. const children = rowParents[node.id];
  8276. node._internal.parents = parents;
  8277. if (typeof children === 'undefined') {
  8278. node._internal.children = [];
  8279. return node;
  8280. }
  8281. if (node.id !== '') {
  8282. parents = [...parents, node.id];
  8283. }
  8284. node._internal.children = Object.values(children);
  8285. for (const childrenId in children) {
  8286. const child = children[childrenId];
  8287. this.fastTree(rowParents, child, parents);
  8288. }
  8289. return node;
  8290. },
  8291. makeTreeMap(rows, items) {
  8292. const itemParents = this.generateParents(items, 'rowId');
  8293. for (const row of rows) {
  8294. row._internal.items = itemParents[row.id] !== undefined ? Object.values(itemParents[row.id]) : [];
  8295. }
  8296. const rowParents = this.generateParents(rows);
  8297. const tree = { id: '', _internal: { children: [], parents: [], items: [] } };
  8298. return this.fastTree(rowParents, tree);
  8299. },
  8300. getFlatTreeMapById(treeMap, flatTreeMapById = {}) {
  8301. for (const child of treeMap._internal.children) {
  8302. flatTreeMapById[child.id] = child;
  8303. this.getFlatTreeMapById(child, flatTreeMapById);
  8304. }
  8305. return flatTreeMapById;
  8306. },
  8307. flattenTreeMap(treeMap, rows = []) {
  8308. for (const child of treeMap._internal.children) {
  8309. rows.push(child.id);
  8310. this.flattenTreeMap(child, rows);
  8311. }
  8312. return rows;
  8313. },
  8314. getRowsFromMap(flatTreeMap, rows) {
  8315. return flatTreeMap.map(node => rows[node.id]);
  8316. },
  8317. getRowsFromIds(ids, rows) {
  8318. const result = [];
  8319. for (const id of ids) {
  8320. result.push(rows[id]);
  8321. }
  8322. return result;
  8323. },
  8324. getRowsWithParentsExpanded(flatTreeMap, flatTreeMapById, rows) {
  8325. if (!flatTreeMap ||
  8326. !flatTreeMapById ||
  8327. !rows ||
  8328. flatTreeMap.length === 0 ||
  8329. flatTreeMapById.length === 0 ||
  8330. Object.keys(rows).length === 0) {
  8331. return [];
  8332. }
  8333. const rowsWithParentsExpanded = [];
  8334. next: for (const rowId of flatTreeMap) {
  8335. for (const parentId of flatTreeMapById[rowId]._internal.parents) {
  8336. const parent = rows[parentId];
  8337. if (!parent || !parent.expanded) {
  8338. continue next;
  8339. }
  8340. }
  8341. rowsWithParentsExpanded.push(rowId);
  8342. }
  8343. return rowsWithParentsExpanded;
  8344. },
  8345. getRowsHeight(rows) {
  8346. let height = 0;
  8347. for (const row of rows) {
  8348. if (row)
  8349. height += row.height;
  8350. }
  8351. return height;
  8352. },
  8353. /**
  8354. * Get visible rows - get rows that are inside current viewport (height)
  8355. *
  8356. * @param {array} rowsWithParentsExpanded rows that have parent expanded- they are visible
  8357. */
  8358. getVisibleRowsAndCompensation(rowsWithParentsExpanded) {
  8359. const visibleRows = [];
  8360. let currentRowsOffset = 0;
  8361. let rowOffset = 0;
  8362. const scrollTop = state.get('config.scroll.top');
  8363. const height = state.get('_internal.height');
  8364. let chartViewBottom = 0;
  8365. let compensation = 0;
  8366. for (const row of rowsWithParentsExpanded) {
  8367. if (row === undefined)
  8368. continue;
  8369. chartViewBottom = scrollTop + height;
  8370. if (currentRowsOffset + row.height >= scrollTop && currentRowsOffset <= chartViewBottom) {
  8371. row.top = rowOffset;
  8372. compensation = row.top + scrollTop - currentRowsOffset;
  8373. rowOffset += row.height;
  8374. visibleRows.push(row);
  8375. }
  8376. currentRowsOffset += row.height;
  8377. if (currentRowsOffset >= chartViewBottom) {
  8378. break;
  8379. }
  8380. }
  8381. return { visibleRows, compensation };
  8382. },
  8383. /**
  8384. * Normalize mouse wheel event to get proper scroll metrics
  8385. *
  8386. * @param {Event} event mouse wheel event
  8387. */
  8388. normalizeMouseWheelEvent(event) {
  8389. // @ts-ignore
  8390. let x = event.deltaX || 0;
  8391. // @ts-ignore
  8392. let y = event.deltaY || 0;
  8393. // @ts-ignore
  8394. let z = event.deltaZ || 0;
  8395. // @ts-ignore
  8396. const mode = event.deltaMode;
  8397. const lineHeight = state.get('config.list.rowHeight');
  8398. let scale = 1;
  8399. switch (mode) {
  8400. case 1:
  8401. if (lineHeight) {
  8402. scale = lineHeight;
  8403. }
  8404. break;
  8405. case 2:
  8406. // @ts-ignore
  8407. scale = window.height;
  8408. break;
  8409. }
  8410. x *= scale;
  8411. y *= scale;
  8412. z *= scale;
  8413. return { x, y, z, event };
  8414. },
  8415. normalizePointerEvent(event) {
  8416. const result = { x: 0, y: 0, pageX: 0, pageY: 0, clientX: 0, clientY: 0, screenX: 0, screenY: 0 };
  8417. switch (event.type) {
  8418. case 'wheel':
  8419. const wheel = this.normalizeMouseWheelEvent(event);
  8420. result.x = wheel.x;
  8421. result.y = wheel.y;
  8422. result.pageX = result.x;
  8423. result.pageY = result.y;
  8424. result.screenX = result.x;
  8425. result.screenY = result.y;
  8426. result.clientX = result.x;
  8427. result.clientY = result.y;
  8428. break;
  8429. case 'touchstart':
  8430. case 'touchmove':
  8431. case 'touchend':
  8432. case 'touchcancel':
  8433. result.x = event.changedTouches[0].screenX;
  8434. result.y = event.changedTouches[0].screenY;
  8435. result.pageX = event.changedTouches[0].pageX;
  8436. result.pageY = event.changedTouches[0].pageY;
  8437. result.screenX = event.changedTouches[0].screenX;
  8438. result.screenY = event.changedTouches[0].screenY;
  8439. result.clientX = event.changedTouches[0].clientX;
  8440. result.clientY = event.changedTouches[0].clientY;
  8441. break;
  8442. default:
  8443. result.x = event.x;
  8444. result.y = event.y;
  8445. result.pageX = event.pageX;
  8446. result.pageY = event.pageY;
  8447. result.screenX = event.screenX;
  8448. result.screenY = event.screenY;
  8449. result.clientX = event.clientX;
  8450. result.clientY = event.clientY;
  8451. break;
  8452. }
  8453. return result;
  8454. },
  8455. limitScrollLeft(totalViewDurationPx, chartWidth, scrollLeft) {
  8456. const width = totalViewDurationPx - chartWidth;
  8457. if (scrollLeft < 0) {
  8458. scrollLeft = 0;
  8459. }
  8460. else if (scrollLeft > width) {
  8461. scrollLeft = width;
  8462. }
  8463. return Math.round(scrollLeft);
  8464. },
  8465. limitScrollTop(rowsHeight, internalHeight, scrollTop) {
  8466. const height = rowsHeight - internalHeight;
  8467. if (scrollTop < 0) {
  8468. scrollTop = 0;
  8469. }
  8470. else if (scrollTop > height) {
  8471. scrollTop = height;
  8472. }
  8473. return Math.round(scrollTop);
  8474. },
  8475. time: new TimeApi(state),
  8476. /**
  8477. * Get scrollbar height - compute it from element
  8478. *
  8479. * @returns {number}
  8480. */
  8481. getScrollBarHeight(add = 0) {
  8482. const outer = document.createElement('div');
  8483. outer.style.visibility = 'hidden';
  8484. outer.style.height = '100px';
  8485. document.body.appendChild(outer);
  8486. const noScroll = outer.offsetHeight;
  8487. outer.style.msOverflowStyle = 'scrollbar';
  8488. outer.style.overflow = 'scroll';
  8489. const inner = document.createElement('div');
  8490. inner.style.height = '100%';
  8491. outer.appendChild(inner);
  8492. const withScroll = inner.offsetHeight;
  8493. outer.parentNode.removeChild(outer);
  8494. return noScroll - withScroll + add;
  8495. },
  8496. scrollToTime(toTime) {
  8497. const time = state.get('_internal.chart.time');
  8498. state.update('config.scroll', scroll => {
  8499. const chartWidth = state.get('_internal.chart.dimensions.width');
  8500. const halfTime = (chartWidth / 2) * time.timePerPixel;
  8501. const leftGlobal = toTime - halfTime - time.finalFrom;
  8502. scroll.left = this.limitScrollLeft(time.totalViewDurationPx, chartWidth, leftGlobal / time.timePerPixel);
  8503. return scroll;
  8504. });
  8505. },
  8506. /**
  8507. * Get grid blocks that are under specified rectangle
  8508. *
  8509. * @param {number} x beginging at chart-timeline bounding rect
  8510. * @param {number} y beginging at chart-timeline bounding rect
  8511. * @param {number} width
  8512. * @param {number} height
  8513. * @returns {array} array of {element, data}
  8514. */
  8515. getGridBlocksUnderRect(x, y, width, height) {
  8516. const main = state.get('_internal.elements.main');
  8517. if (!main)
  8518. return [];
  8519. },
  8520. getCompensationX() {
  8521. return state.get('config.scroll.compensation.x') || 0;
  8522. },
  8523. getCompensationY() {
  8524. return state.get('config.scroll.compensation.y') || 0;
  8525. },
  8526. getSVGIconSrc(svg) {
  8527. if (typeof iconsCache[svg] === 'string')
  8528. return iconsCache[svg];
  8529. iconsCache[svg] = 'data:image/svg+xml;base64,' + btoa(svg);
  8530. return iconsCache[svg];
  8531. },
  8532. /**
  8533. * Destroy things to release memory
  8534. */
  8535. destroy() {
  8536. $state = undefined;
  8537. for (const unsubscribe of unsubscribes) {
  8538. unsubscribe();
  8539. }
  8540. unsubscribes = [];
  8541. if (api.debug) {
  8542. // @ts-ignore
  8543. delete window.state;
  8544. }
  8545. }
  8546. };
  8547. if (api.debug) {
  8548. // @ts-ignore
  8549. window.state = state;
  8550. // @ts-ignore
  8551. window.api = api;
  8552. }
  8553. return api;
  8554. }
  8555. /**
  8556. * Gantt-Schedule-Timeline-Calendar
  8557. *
  8558. * @copyright Rafal Pospiech <https://neuronet.io>
  8559. * @author Rafal Pospiech <neuronet.io@gmail.com>
  8560. * @package gantt-schedule-timeline-calendar
  8561. * @license AGPL-3.0
  8562. */
  8563. function GSTC(options) {
  8564. const state = options.state;
  8565. const api = getInternalApi(state);
  8566. const _internal = {
  8567. components: {
  8568. Main
  8569. },
  8570. scrollBarHeight: api.getScrollBarHeight(2),
  8571. height: 0,
  8572. treeMap: {},
  8573. flatTreeMap: [],
  8574. flatTreeMapById: {},
  8575. list: {
  8576. expandedHeight: 0,
  8577. visibleRows: [],
  8578. rows: {},
  8579. width: 0
  8580. },
  8581. dimensions: {
  8582. width: 0,
  8583. height: 0
  8584. },
  8585. chart: {
  8586. dimensions: {
  8587. width: 0,
  8588. innerWidth: 0
  8589. },
  8590. visibleItems: [],
  8591. time: {
  8592. levels: [],
  8593. timePerPixel: 0,
  8594. firstTaskTime: 0,
  8595. lastTaskTime: 0,
  8596. totalViewDurationMs: 0,
  8597. totalViewDurationPx: 0,
  8598. leftGlobal: 0,
  8599. rightGlobal: 0,
  8600. leftPx: 0,
  8601. rightPx: 0,
  8602. leftInner: 0,
  8603. rightInner: 0,
  8604. maxWidth: {}
  8605. }
  8606. },
  8607. elements: {},
  8608. cache: {
  8609. calendar: {}
  8610. },
  8611. loaded: {}
  8612. };
  8613. if (typeof options.debug === 'boolean' && options.debug) {
  8614. // @ts-ignore
  8615. window.state = state;
  8616. }
  8617. state.update('', oldValue => {
  8618. return {
  8619. config: oldValue.config,
  8620. _internal
  8621. };
  8622. });
  8623. // @ts-ignore
  8624. const vido = Vido(state, api);
  8625. api.setVido(vido);
  8626. const app = vido.createApp({ component: Main, props: {}, element: options.element });
  8627. const internalApi = app.vidoInstance.api;
  8628. return { state, app, api: internalApi };
  8629. }
  8630. GSTC.api = publicApi;
  8631. export default GSTC;
  8632. //# sourceMappingURL=index.esm.js.map