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.

2738 lines
84 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
  1. /******/ (function(modules) { // webpackBootstrap
  2. /******/ // The module cache
  3. /******/ var installedModules = {};
  4. /******/
  5. /******/ // The require function
  6. /******/ function __webpack_require__(moduleId) {
  7. /******/
  8. /******/ // Check if module is in cache
  9. /******/ if(installedModules[moduleId]) {
  10. /******/ return installedModules[moduleId].exports;
  11. /******/ }
  12. /******/ // Create a new module (and put it into the cache)
  13. /******/ var module = installedModules[moduleId] = {
  14. /******/ i: moduleId,
  15. /******/ l: false,
  16. /******/ exports: {}
  17. /******/ };
  18. /******/
  19. /******/ // Execute the module function
  20. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  21. /******/
  22. /******/ // Flag the module as loaded
  23. /******/ module.l = true;
  24. /******/
  25. /******/ // Return the exports of the module
  26. /******/ return module.exports;
  27. /******/ }
  28. /******/
  29. /******/
  30. /******/ // expose the modules object (__webpack_modules__)
  31. /******/ __webpack_require__.m = modules;
  32. /******/
  33. /******/ // expose the module cache
  34. /******/ __webpack_require__.c = installedModules;
  35. /******/
  36. /******/ // define getter function for harmony exports
  37. /******/ __webpack_require__.d = function(exports, name, getter) {
  38. /******/ if(!__webpack_require__.o(exports, name)) {
  39. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  40. /******/ }
  41. /******/ };
  42. /******/
  43. /******/ // define __esModule on exports
  44. /******/ __webpack_require__.r = function(exports) {
  45. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  46. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  47. /******/ }
  48. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  49. /******/ };
  50. /******/
  51. /******/ // create a fake namespace object
  52. /******/ // mode & 1: value is a module id, require it
  53. /******/ // mode & 2: merge all properties of value into the ns
  54. /******/ // mode & 4: return value when already ns object
  55. /******/ // mode & 8|1: behave like require
  56. /******/ __webpack_require__.t = function(value, mode) {
  57. /******/ if(mode & 1) value = __webpack_require__(value);
  58. /******/ if(mode & 8) return value;
  59. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  60. /******/ var ns = Object.create(null);
  61. /******/ __webpack_require__.r(ns);
  62. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  63. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  64. /******/ return ns;
  65. /******/ };
  66. /******/
  67. /******/ // getDefaultExport function for compatibility with non-harmony modules
  68. /******/ __webpack_require__.n = function(module) {
  69. /******/ var getter = module && module.__esModule ?
  70. /******/ function getDefault() { return module['default']; } :
  71. /******/ function getModuleExports() { return module; };
  72. /******/ __webpack_require__.d(getter, 'a', getter);
  73. /******/ return getter;
  74. /******/ };
  75. /******/
  76. /******/ // Object.prototype.hasOwnProperty.call
  77. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  78. /******/
  79. /******/ // __webpack_public_path__
  80. /******/ __webpack_require__.p = "/";
  81. /******/
  82. /******/
  83. /******/ // Load entry module and return exports
  84. /******/ return __webpack_require__(__webpack_require__.s = 0);
  85. /******/ })
  86. /************************************************************************/
  87. /******/ ({
  88. /***/ "./demo.js":
  89. /*!*****************!*\
  90. !*** ./demo.js ***!
  91. \*****************/
  92. /*! no exports provided */
  93. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  94. "use strict";
  95. __webpack_require__.r(__webpack_exports__);
  96. /* harmony import */ var riot__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! riot */ "./node_modules/riot/riot.esm.js");
  97. /* harmony import */ var _src_TinyOnePage_riot__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/TinyOnePage.riot */ "./src/TinyOnePage.riot");
  98. riot__WEBPACK_IMPORTED_MODULE_0__["register"]('tiny-one-page', _src_TinyOnePage_riot__WEBPACK_IMPORTED_MODULE_1__["default"]); // adding component with animate object
  99. riot__WEBPACK_IMPORTED_MODULE_0__["mount"]('tiny-one-page');
  100. /***/ }),
  101. /***/ "./node_modules/riot/riot.esm.js":
  102. /*!***************************************!*\
  103. !*** ./node_modules/riot/riot.esm.js ***!
  104. \***************************************/
  105. /*! exports provided: __, component, install, mount, register, uninstall, unmount, unregister, version */
  106. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  107. "use strict";
  108. __webpack_require__.r(__webpack_exports__);
  109. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__", function() { return __; });
  110. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "component", function() { return component; });
  111. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "install", function() { return install; });
  112. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mount", function() { return mount; });
  113. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "register", function() { return register; });
  114. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "uninstall", function() { return uninstall; });
  115. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "unmount", function() { return unmount; });
  116. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "unregister", function() { return unregister; });
  117. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "version", function() { return version; });
  118. /* Riot v4.5.0, @license MIT */
  119. const COMPONENTS_IMPLEMENTATION_MAP = new Map(),
  120. DOM_COMPONENT_INSTANCE_PROPERTY = Symbol('riot-component'),
  121. PLUGINS_SET = new Set(),
  122. IS_DIRECTIVE = 'is',
  123. VALUE_ATTRIBUTE = 'value',
  124. ATTRIBUTES_KEY_SYMBOL = Symbol('attributes'),
  125. TEMPLATE_KEY_SYMBOL = Symbol('template');
  126. var globals = /*#__PURE__*/Object.freeze({
  127. COMPONENTS_IMPLEMENTATION_MAP: COMPONENTS_IMPLEMENTATION_MAP,
  128. DOM_COMPONENT_INSTANCE_PROPERTY: DOM_COMPONENT_INSTANCE_PROPERTY,
  129. PLUGINS_SET: PLUGINS_SET,
  130. IS_DIRECTIVE: IS_DIRECTIVE,
  131. VALUE_ATTRIBUTE: VALUE_ATTRIBUTE,
  132. ATTRIBUTES_KEY_SYMBOL: ATTRIBUTES_KEY_SYMBOL,
  133. TEMPLATE_KEY_SYMBOL: TEMPLATE_KEY_SYMBOL
  134. });
  135. /**
  136. * Remove the child nodes from any DOM node
  137. * @param {HTMLElement} node - target node
  138. * @returns {undefined}
  139. */
  140. function cleanNode(node) {
  141. clearChildren(node.childNodes);
  142. }
  143. /**
  144. * Clear multiple children in a node
  145. * @param {HTMLElement[]} children - direct children nodes
  146. * @returns {undefined}
  147. */
  148. function clearChildren(children) {
  149. Array.from(children).forEach(n => n.parentNode && n.parentNode.removeChild(n));
  150. }
  151. const EACH = 0;
  152. const IF = 1;
  153. const SIMPLE = 2;
  154. const TAG = 3;
  155. const SLOT = 4;
  156. var bindingTypes = {
  157. EACH,
  158. IF,
  159. SIMPLE,
  160. TAG,
  161. SLOT
  162. };
  163. /**
  164. * Create the template meta object in case of <template> fragments
  165. * @param {TemplateChunk} componentTemplate - template chunk object
  166. * @returns {Object} the meta property that will be passed to the mount function of the TemplateChunk
  167. */
  168. function createTemplateMeta(componentTemplate) {
  169. const fragment = componentTemplate.dom.cloneNode(true);
  170. return {
  171. avoidDOMInjection: true,
  172. fragment,
  173. children: Array.from(fragment.childNodes)
  174. };
  175. }
  176. /* get rid of the @ungap/essential-map polyfill */
  177. const {
  178. indexOf: iOF
  179. } = [];
  180. const append = (get, parent, children, start, end, before) => {
  181. const isSelect = 'selectedIndex' in parent;
  182. let noSelection = isSelect;
  183. while (start < end) {
  184. const child = get(children[start], 1);
  185. parent.insertBefore(child, before);
  186. if (isSelect && noSelection && child.selected) {
  187. noSelection = !noSelection;
  188. let {
  189. selectedIndex
  190. } = parent;
  191. parent.selectedIndex = selectedIndex < 0 ? start : iOF.call(parent.querySelectorAll('option'), child);
  192. }
  193. start++;
  194. }
  195. };
  196. const eqeq = (a, b) => a == b;
  197. const identity = O => O;
  198. const indexOf = (moreNodes, moreStart, moreEnd, lessNodes, lessStart, lessEnd, compare) => {
  199. const length = lessEnd - lessStart;
  200. /* istanbul ignore if */
  201. if (length < 1) return -1;
  202. while (moreEnd - moreStart >= length) {
  203. let m = moreStart;
  204. let l = lessStart;
  205. while (m < moreEnd && l < lessEnd && compare(moreNodes[m], lessNodes[l])) {
  206. m++;
  207. l++;
  208. }
  209. if (l === lessEnd) return moreStart;
  210. moreStart = m + 1;
  211. }
  212. return -1;
  213. };
  214. const isReversed = (futureNodes, futureEnd, currentNodes, currentStart, currentEnd, compare) => {
  215. while (currentStart < currentEnd && compare(currentNodes[currentStart], futureNodes[futureEnd - 1])) {
  216. currentStart++;
  217. futureEnd--;
  218. }
  219. return futureEnd === 0;
  220. };
  221. const next = (get, list, i, length, before) => i < length ? get(list[i], 0) : 0 < i ? get(list[i - 1], -0).nextSibling : before;
  222. const remove = (get, parent, children, start, end) => {
  223. if (end - start < 2) parent.removeChild(get(children[start], -1));else {
  224. const range = parent.ownerDocument.createRange();
  225. range.setStartBefore(get(children[start], -1));
  226. range.setEndAfter(get(children[end - 1], -1));
  227. range.deleteContents();
  228. }
  229. }; // - - - - - - - - - - - - - - - - - - -
  230. // diff related constants and utilities
  231. // - - - - - - - - - - - - - - - - - - -
  232. const DELETION = -1;
  233. const INSERTION = 1;
  234. const SKIP = 0;
  235. const SKIP_OND = 50;
  236. const HS = (futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges) => {
  237. let k = 0;
  238. /* istanbul ignore next */
  239. let minLen = futureChanges < currentChanges ? futureChanges : currentChanges;
  240. const link = Array(minLen++);
  241. const tresh = Array(minLen);
  242. tresh[0] = -1;
  243. for (let i = 1; i < minLen; i++) tresh[i] = currentEnd;
  244. const keymap = new Map();
  245. for (let i = currentStart; i < currentEnd; i++) keymap.set(currentNodes[i], i);
  246. for (let i = futureStart; i < futureEnd; i++) {
  247. const idxInOld = keymap.get(futureNodes[i]);
  248. if (idxInOld != null) {
  249. k = findK(tresh, minLen, idxInOld);
  250. /* istanbul ignore else */
  251. if (-1 < k) {
  252. tresh[k] = idxInOld;
  253. link[k] = {
  254. newi: i,
  255. oldi: idxInOld,
  256. prev: link[k - 1]
  257. };
  258. }
  259. }
  260. }
  261. k = --minLen;
  262. --currentEnd;
  263. while (tresh[k] > currentEnd) --k;
  264. minLen = currentChanges + futureChanges - k;
  265. const diff = Array(minLen);
  266. let ptr = link[k];
  267. --futureEnd;
  268. while (ptr) {
  269. const {
  270. newi,
  271. oldi
  272. } = ptr;
  273. while (futureEnd > newi) {
  274. diff[--minLen] = INSERTION;
  275. --futureEnd;
  276. }
  277. while (currentEnd > oldi) {
  278. diff[--minLen] = DELETION;
  279. --currentEnd;
  280. }
  281. diff[--minLen] = SKIP;
  282. --futureEnd;
  283. --currentEnd;
  284. ptr = ptr.prev;
  285. }
  286. while (futureEnd >= futureStart) {
  287. diff[--minLen] = INSERTION;
  288. --futureEnd;
  289. }
  290. while (currentEnd >= currentStart) {
  291. diff[--minLen] = DELETION;
  292. --currentEnd;
  293. }
  294. return diff;
  295. }; // this is pretty much the same petit-dom code without the delete map part
  296. // https://github.com/yelouafi/petit-dom/blob/bd6f5c919b5ae5297be01612c524c40be45f14a7/src/vdom.js#L556-L561
  297. const OND = (futureNodes, futureStart, rows, currentNodes, currentStart, cols, compare) => {
  298. const length = rows + cols;
  299. const v = [];
  300. let d, k, r, c, pv, cv, pd;
  301. outer: for (d = 0; d <= length; d++) {
  302. /* istanbul ignore if */
  303. if (d > SKIP_OND) return null;
  304. pd = d - 1;
  305. /* istanbul ignore next */
  306. pv = d ? v[d - 1] : [0, 0];
  307. cv = v[d] = [];
  308. for (k = -d; k <= d; k += 2) {
  309. if (k === -d || k !== d && pv[pd + k - 1] < pv[pd + k + 1]) {
  310. c = pv[pd + k + 1];
  311. } else {
  312. c = pv[pd + k - 1] + 1;
  313. }
  314. r = c - k;
  315. while (c < cols && r < rows && compare(currentNodes[currentStart + c], futureNodes[futureStart + r])) {
  316. c++;
  317. r++;
  318. }
  319. if (c === cols && r === rows) {
  320. break outer;
  321. }
  322. cv[d + k] = c;
  323. }
  324. }
  325. const diff = Array(d / 2 + length / 2);
  326. let diffIdx = diff.length - 1;
  327. for (d = v.length - 1; d >= 0; d--) {
  328. while (c > 0 && r > 0 && compare(currentNodes[currentStart + c - 1], futureNodes[futureStart + r - 1])) {
  329. // diagonal edge = equality
  330. diff[diffIdx--] = SKIP;
  331. c--;
  332. r--;
  333. }
  334. if (!d) break;
  335. pd = d - 1;
  336. /* istanbul ignore next */
  337. pv = d ? v[d - 1] : [0, 0];
  338. k = c - r;
  339. if (k === -d || k !== d && pv[pd + k - 1] < pv[pd + k + 1]) {
  340. // vertical edge = insertion
  341. r--;
  342. diff[diffIdx--] = INSERTION;
  343. } else {
  344. // horizontal edge = deletion
  345. c--;
  346. diff[diffIdx--] = DELETION;
  347. }
  348. }
  349. return diff;
  350. };
  351. const applyDiff = (diff, get, parentNode, futureNodes, futureStart, currentNodes, currentStart, currentLength, before) => {
  352. const live = new Map();
  353. const length = diff.length;
  354. let currentIndex = currentStart;
  355. let i = 0;
  356. while (i < length) {
  357. switch (diff[i++]) {
  358. case SKIP:
  359. futureStart++;
  360. currentIndex++;
  361. break;
  362. case INSERTION:
  363. // TODO: bulk appends for sequential nodes
  364. live.set(futureNodes[futureStart], 1);
  365. append(get, parentNode, futureNodes, futureStart++, futureStart, currentIndex < currentLength ? get(currentNodes[currentIndex], 0) : before);
  366. break;
  367. case DELETION:
  368. currentIndex++;
  369. break;
  370. }
  371. }
  372. i = 0;
  373. while (i < length) {
  374. switch (diff[i++]) {
  375. case SKIP:
  376. currentStart++;
  377. break;
  378. case DELETION:
  379. // TODO: bulk removes for sequential nodes
  380. if (live.has(currentNodes[currentStart])) currentStart++;else remove(get, parentNode, currentNodes, currentStart++, currentStart);
  381. break;
  382. }
  383. }
  384. };
  385. const findK = (ktr, length, j) => {
  386. let lo = 1;
  387. let hi = length;
  388. while (lo < hi) {
  389. const mid = (lo + hi) / 2 >>> 0;
  390. if (j < ktr[mid]) hi = mid;else lo = mid + 1;
  391. }
  392. return lo;
  393. };
  394. const smartDiff = (get, parentNode, futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges, currentLength, compare, before) => {
  395. applyDiff(OND(futureNodes, futureStart, futureChanges, currentNodes, currentStart, currentChanges, compare) || HS(futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges), get, parentNode, futureNodes, futureStart, currentNodes, currentStart, currentLength, before);
  396. };
  397. /*! (c) 2018 Andrea Giammarchi (ISC) */
  398. const domdiff = (parentNode, // where changes happen
  399. currentNodes, // Array of current items/nodes
  400. futureNodes, // Array of future items/nodes
  401. options // optional object with one of the following properties
  402. // before: domNode
  403. // compare(generic, generic) => true if same generic
  404. // node(generic) => Node
  405. ) => {
  406. if (!options) options = {};
  407. const compare = options.compare || eqeq;
  408. const get = options.node || identity;
  409. const before = options.before == null ? null : get(options.before, 0);
  410. const currentLength = currentNodes.length;
  411. let currentEnd = currentLength;
  412. let currentStart = 0;
  413. let futureEnd = futureNodes.length;
  414. let futureStart = 0; // common prefix
  415. while (currentStart < currentEnd && futureStart < futureEnd && compare(currentNodes[currentStart], futureNodes[futureStart])) {
  416. currentStart++;
  417. futureStart++;
  418. } // common suffix
  419. while (currentStart < currentEnd && futureStart < futureEnd && compare(currentNodes[currentEnd - 1], futureNodes[futureEnd - 1])) {
  420. currentEnd--;
  421. futureEnd--;
  422. }
  423. const currentSame = currentStart === currentEnd;
  424. const futureSame = futureStart === futureEnd; // same list
  425. if (currentSame && futureSame) return futureNodes; // only stuff to add
  426. if (currentSame && futureStart < futureEnd) {
  427. append(get, parentNode, futureNodes, futureStart, futureEnd, next(get, currentNodes, currentStart, currentLength, before));
  428. return futureNodes;
  429. } // only stuff to remove
  430. if (futureSame && currentStart < currentEnd) {
  431. remove(get, parentNode, currentNodes, currentStart, currentEnd);
  432. return futureNodes;
  433. }
  434. const currentChanges = currentEnd - currentStart;
  435. const futureChanges = futureEnd - futureStart;
  436. let i = -1; // 2 simple indels: the shortest sequence is a subsequence of the longest
  437. if (currentChanges < futureChanges) {
  438. i = indexOf(futureNodes, futureStart, futureEnd, currentNodes, currentStart, currentEnd, compare); // inner diff
  439. if (-1 < i) {
  440. append(get, parentNode, futureNodes, futureStart, i, get(currentNodes[currentStart], 0));
  441. append(get, parentNode, futureNodes, i + currentChanges, futureEnd, next(get, currentNodes, currentEnd, currentLength, before));
  442. return futureNodes;
  443. }
  444. }
  445. /* istanbul ignore else */
  446. else if (futureChanges < currentChanges) {
  447. i = indexOf(currentNodes, currentStart, currentEnd, futureNodes, futureStart, futureEnd, compare); // outer diff
  448. if (-1 < i) {
  449. remove(get, parentNode, currentNodes, currentStart, i);
  450. remove(get, parentNode, currentNodes, i + futureChanges, currentEnd);
  451. return futureNodes;
  452. }
  453. } // common case with one replacement for many nodes
  454. // or many nodes replaced for a single one
  455. /* istanbul ignore else */
  456. if (currentChanges < 2 || futureChanges < 2) {
  457. append(get, parentNode, futureNodes, futureStart, futureEnd, get(currentNodes[currentStart], 0));
  458. remove(get, parentNode, currentNodes, currentStart, currentEnd);
  459. return futureNodes;
  460. } // the half match diff part has been skipped in petit-dom
  461. // https://github.com/yelouafi/petit-dom/blob/bd6f5c919b5ae5297be01612c524c40be45f14a7/src/vdom.js#L391-L397
  462. // accordingly, I think it's safe to skip in here too
  463. // if one day it'll come out like the speediest thing ever to do
  464. // then I might add it in here too
  465. // Extra: before going too fancy, what about reversed lists ?
  466. // This should bail out pretty quickly if that's not the case.
  467. if (currentChanges === futureChanges && isReversed(futureNodes, futureEnd, currentNodes, currentStart, currentEnd, compare)) {
  468. append(get, parentNode, futureNodes, futureStart, futureEnd, next(get, currentNodes, currentEnd, currentLength, before));
  469. return futureNodes;
  470. } // last resort through a smart diff
  471. smartDiff(get, parentNode, futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges, currentLength, compare, before);
  472. return futureNodes;
  473. };
  474. /**
  475. * Check if a value is null or undefined
  476. * @param {*} value - anything
  477. * @returns {boolean} true only for the 'undefined' and 'null' types
  478. */
  479. function isNil(value) {
  480. return value === null || value === undefined;
  481. }
  482. /**
  483. * Check if an element is a template tag
  484. * @param {HTMLElement} el - element to check
  485. * @returns {boolean} true if it's a <template>
  486. */
  487. function isTemplate(el) {
  488. return !isNil(el.content);
  489. }
  490. const EachBinding = Object.seal({
  491. // dynamic binding properties
  492. childrenMap: null,
  493. node: null,
  494. root: null,
  495. condition: null,
  496. evaluate: null,
  497. template: null,
  498. isTemplateTag: false,
  499. nodes: [],
  500. getKey: null,
  501. indexName: null,
  502. itemName: null,
  503. afterPlaceholder: null,
  504. placeholder: null,
  505. // API methods
  506. mount(scope, parentScope) {
  507. return this.update(scope, parentScope);
  508. },
  509. update(scope, parentScope) {
  510. const {
  511. placeholder
  512. } = this;
  513. const collection = this.evaluate(scope);
  514. const items = collection ? Array.from(collection) : [];
  515. const parent = placeholder.parentNode; // prepare the diffing
  516. const {
  517. newChildrenMap,
  518. batches,
  519. futureNodes
  520. } = createPatch(items, scope, parentScope, this); // patch the DOM only if there are new nodes
  521. if (futureNodes.length) {
  522. domdiff(parent, this.nodes, futureNodes, {
  523. before: placeholder,
  524. node: patch(Array.from(this.childrenMap.values()), parentScope)
  525. });
  526. } else {
  527. // remove all redundant templates
  528. unmountRedundant(this.childrenMap);
  529. } // trigger the mounts and the updates
  530. batches.forEach(fn => fn()); // update the children map
  531. this.childrenMap = newChildrenMap;
  532. this.nodes = futureNodes;
  533. return this;
  534. },
  535. unmount(scope, parentScope) {
  536. unmountRedundant(this.childrenMap, parentScope);
  537. this.childrenMap = new Map();
  538. this.nodes = [];
  539. return this;
  540. }
  541. });
  542. /**
  543. * Patch the DOM while diffing
  544. * @param {TemplateChunk[]} redundant - redundant tepmplate chunks
  545. * @param {*} parentScope - scope of the parent template
  546. * @returns {Function} patch function used by domdiff
  547. */
  548. function patch(redundant, parentScope) {
  549. return (item, info) => {
  550. if (info < 0) {
  551. const {
  552. template,
  553. context
  554. } = redundant.pop(); // notice that we pass null as last argument because
  555. // the root node and its children will be removed by domdiff
  556. template.unmount(context, parentScope, null);
  557. }
  558. return item;
  559. };
  560. }
  561. /**
  562. * Unmount the remaining template instances
  563. * @param {Map} childrenMap - map containing the children template to unmount
  564. * @param {*} parentScope - scope of the parent template
  565. * @returns {TemplateChunk[]} collection containing the template chunks unmounted
  566. */
  567. function unmountRedundant(childrenMap, parentScope) {
  568. return Array.from(childrenMap.values()).map((_ref) => {
  569. let {
  570. template,
  571. context
  572. } = _ref;
  573. return template.unmount(context, parentScope, true);
  574. });
  575. }
  576. /**
  577. * Check whether a template must be filtered from a loop
  578. * @param {Function} condition - filter function
  579. * @param {Object} context - argument passed to the filter function
  580. * @returns {boolean} true if this item should be skipped
  581. */
  582. function mustFilterItem(condition, context) {
  583. return condition ? Boolean(condition(context)) === false : false;
  584. }
  585. /**
  586. * Extend the scope of the looped template
  587. * @param {Object} scope - current template scope
  588. * @param {string} options.itemName - key to identify the looped item in the new context
  589. * @param {string} options.indexName - key to identify the index of the looped item
  590. * @param {number} options.index - current index
  591. * @param {*} options.item - collection item looped
  592. * @returns {Object} enhanced scope object
  593. */
  594. function extendScope(scope, _ref2) {
  595. let {
  596. itemName,
  597. indexName,
  598. index,
  599. item
  600. } = _ref2;
  601. scope[itemName] = item;
  602. if (indexName) scope[indexName] = index;
  603. return scope;
  604. }
  605. /**
  606. * Loop the current template items
  607. * @param {Array} items - expression collection value
  608. * @param {*} scope - template scope
  609. * @param {*} parentScope - scope of the parent template
  610. * @param {EeachBinding} binding - each binding object instance
  611. * @returns {Object} data
  612. * @returns {Map} data.newChildrenMap - a Map containing the new children template structure
  613. * @returns {Array} data.batches - array containing the template lifecycle functions to trigger
  614. * @returns {Array} data.futureNodes - array containing the nodes we need to diff
  615. */
  616. function createPatch(items, scope, parentScope, binding) {
  617. const {
  618. condition,
  619. template,
  620. childrenMap,
  621. itemName,
  622. getKey,
  623. indexName,
  624. root,
  625. isTemplateTag
  626. } = binding;
  627. const newChildrenMap = new Map();
  628. const batches = [];
  629. const futureNodes = [];
  630. items.forEach((item, index) => {
  631. const context = extendScope(Object.create(scope), {
  632. itemName,
  633. indexName,
  634. index,
  635. item
  636. });
  637. const key = getKey ? getKey(context) : index;
  638. const oldItem = childrenMap.get(key);
  639. if (mustFilterItem(condition, context)) {
  640. return;
  641. }
  642. const componentTemplate = oldItem ? oldItem.template : template.clone();
  643. const el = oldItem ? componentTemplate.el : root.cloneNode();
  644. const mustMount = !oldItem;
  645. const meta = isTemplateTag && mustMount ? createTemplateMeta(componentTemplate) : {};
  646. if (mustMount) {
  647. batches.push(() => componentTemplate.mount(el, context, parentScope, meta));
  648. } else {
  649. componentTemplate.update(context, parentScope);
  650. } // create the collection of nodes to update or to add
  651. // in case of template tags we need to add all its children nodes
  652. if (isTemplateTag) {
  653. futureNodes.push(...(meta.children || componentTemplate.children));
  654. } else {
  655. futureNodes.push(el);
  656. } // delete the old item from the children map
  657. childrenMap.delete(key); // update the children map
  658. newChildrenMap.set(key, {
  659. template: componentTemplate,
  660. context,
  661. index
  662. });
  663. });
  664. return {
  665. newChildrenMap,
  666. batches,
  667. futureNodes
  668. };
  669. }
  670. function create(node, _ref3) {
  671. let {
  672. evaluate,
  673. condition,
  674. itemName,
  675. indexName,
  676. getKey,
  677. template
  678. } = _ref3;
  679. const placeholder = document.createTextNode('');
  680. const parent = node.parentNode;
  681. const root = node.cloneNode();
  682. parent.insertBefore(placeholder, node);
  683. parent.removeChild(node);
  684. return Object.assign({}, EachBinding, {
  685. childrenMap: new Map(),
  686. node,
  687. root,
  688. condition,
  689. evaluate,
  690. isTemplateTag: isTemplate(root),
  691. template: template.createDOM(node),
  692. getKey,
  693. indexName,
  694. itemName,
  695. placeholder
  696. });
  697. }
  698. /**
  699. * Binding responsible for the `if` directive
  700. */
  701. const IfBinding = Object.seal({
  702. // dynamic binding properties
  703. node: null,
  704. evaluate: null,
  705. parent: null,
  706. isTemplateTag: false,
  707. placeholder: null,
  708. template: null,
  709. // API methods
  710. mount(scope, parentScope) {
  711. this.parent.insertBefore(this.placeholder, this.node);
  712. this.parent.removeChild(this.node);
  713. return this.update(scope, parentScope);
  714. },
  715. update(scope, parentScope) {
  716. const value = !!this.evaluate(scope);
  717. const mustMount = !this.value && value;
  718. const mustUnmount = this.value && !value;
  719. const mount = () => {
  720. const pristine = this.node.cloneNode();
  721. this.parent.insertBefore(pristine, this.placeholder);
  722. this.template = this.template.clone();
  723. this.template.mount(pristine, scope, parentScope);
  724. };
  725. switch (true) {
  726. case mustMount:
  727. mount();
  728. break;
  729. case mustUnmount:
  730. this.unmount(scope);
  731. break;
  732. default:
  733. if (value) this.template.update(scope, parentScope);
  734. }
  735. this.value = value;
  736. return this;
  737. },
  738. unmount(scope, parentScope) {
  739. this.template.unmount(scope, parentScope, true);
  740. return this;
  741. }
  742. });
  743. function create$1(node, _ref4) {
  744. let {
  745. evaluate,
  746. template
  747. } = _ref4;
  748. return Object.assign({}, IfBinding, {
  749. node,
  750. evaluate,
  751. parent: node.parentNode,
  752. placeholder: document.createTextNode(''),
  753. template: template.createDOM(node)
  754. });
  755. }
  756. const ATTRIBUTE = 0;
  757. const EVENT = 1;
  758. const TEXT = 2;
  759. const VALUE = 3;
  760. var expressionTypes = {
  761. ATTRIBUTE,
  762. EVENT,
  763. TEXT,
  764. VALUE
  765. };
  766. /**
  767. * Check if a value is a Boolean
  768. * @param {*} value - anything
  769. * @returns {boolean} true only for the value is a boolean
  770. */
  771. function isBoolean(value) {
  772. return typeof value === 'boolean';
  773. }
  774. /**
  775. * Check if a value is an Object
  776. * @param {*} value - anything
  777. * @returns {boolean} true only for the value is an object
  778. */
  779. function isObject(value) {
  780. return typeof value === 'object';
  781. }
  782. const REMOVE_ATTRIBUTE = 'removeAttribute';
  783. const SET_ATTIBUTE = 'setAttribute';
  784. /**
  785. * Add all the attributes provided
  786. * @param {HTMLElement} node - target node
  787. * @param {Object} attributes - object containing the attributes names and values
  788. * @returns {undefined} sorry it's a void function :(
  789. */
  790. function setAllAttributes(node, attributes) {
  791. Object.entries(attributes).forEach((_ref5) => {
  792. let [name, value] = _ref5;
  793. return attributeExpression(node, {
  794. name
  795. }, value);
  796. });
  797. }
  798. /**
  799. * Remove all the attributes provided
  800. * @param {HTMLElement} node - target node
  801. * @param {Object} attributes - object containing all the attribute names
  802. * @returns {undefined} sorry it's a void function :(
  803. */
  804. function removeAllAttributes(node, attributes) {
  805. Object.keys(attributes).forEach(attribute => node.removeAttribute(attribute));
  806. }
  807. /**
  808. * This methods handles the DOM attributes updates
  809. * @param {HTMLElement} node - target node
  810. * @param {Object} expression - expression object
  811. * @param {string} expression.name - attribute name
  812. * @param {*} value - new expression value
  813. * @param {*} oldValue - the old expression cached value
  814. * @returns {undefined}
  815. */
  816. function attributeExpression(node, _ref6, value, oldValue) {
  817. let {
  818. name
  819. } = _ref6;
  820. // is it a spread operator? {...attributes}
  821. if (!name) {
  822. // is the value still truthy?
  823. if (value) {
  824. setAllAttributes(node, value);
  825. } else if (oldValue) {
  826. // otherwise remove all the old attributes
  827. removeAllAttributes(node, oldValue);
  828. }
  829. return;
  830. } // handle boolean attributes
  831. if (isBoolean(value) || isObject(value)) {
  832. node[name] = value;
  833. }
  834. node[getMethod(value)](name, normalizeValue(name, value));
  835. }
  836. /**
  837. * Get the attribute modifier method
  838. * @param {*} value - if truthy we return `setAttribute` othewise `removeAttribute`
  839. * @returns {string} the node attribute modifier method name
  840. */
  841. function getMethod(value) {
  842. return isNil(value) || value === false || value === '' || isObject(value) ? REMOVE_ATTRIBUTE : SET_ATTIBUTE;
  843. }
  844. /**
  845. * Get the value as string
  846. * @param {string} name - attribute name
  847. * @param {*} value - user input value
  848. * @returns {string} input value as string
  849. */
  850. function normalizeValue(name, value) {
  851. // be sure that expressions like selected={ true } will be always rendered as selected='selected'
  852. if (value === true) return name;
  853. return value;
  854. }
  855. const RE_EVENTS_PREFIX = /^on/;
  856. /**
  857. * Set a new event listener
  858. * @param {HTMLElement} node - target node
  859. * @param {Object} expression - expression object
  860. * @param {string} expression.name - event name
  861. * @param {*} value - new expression value
  862. * @param {*} oldValue - old expression value
  863. * @returns {value} the callback just received
  864. */
  865. function eventExpression(node, _ref7, value, oldValue) {
  866. let {
  867. name
  868. } = _ref7;
  869. const normalizedEventName = name.replace(RE_EVENTS_PREFIX, '');
  870. if (oldValue) {
  871. node.removeEventListener(normalizedEventName, oldValue);
  872. }
  873. if (value) {
  874. node.addEventListener(normalizedEventName, value, false);
  875. }
  876. return value;
  877. }
  878. /**
  879. * This methods handles a simple text expression update
  880. * @param {HTMLElement} node - target node
  881. * @param {Object} expression - expression object
  882. * @param {number} expression.childNodeIndex - index to find the text node to update
  883. * @param {*} value - new expression value
  884. * @returns {undefined}
  885. */
  886. function textExpression(node, _ref8, value) {
  887. let {
  888. childNodeIndex
  889. } = _ref8;
  890. const target = node.childNodes[childNodeIndex];
  891. const val = normalizeValue$1(value); // replace the target if it's a placeholder comment
  892. if (target.nodeType === Node.COMMENT_NODE) {
  893. const textNode = document.createTextNode(val);
  894. node.replaceChild(textNode, target);
  895. } else {
  896. target.data = normalizeValue$1(val);
  897. }
  898. }
  899. /**
  900. * Normalize the user value in order to render a empty string in case of falsy values
  901. * @param {*} value - user input value
  902. * @returns {string} hopefully a string
  903. */
  904. function normalizeValue$1(value) {
  905. return isNil(value) ? '' : value;
  906. }
  907. /**
  908. * This methods handles the input fileds value updates
  909. * @param {HTMLElement} node - target node
  910. * @param {Object} expression - expression object
  911. * @param {*} value - new expression value
  912. * @returns {undefined}
  913. */
  914. function valueExpression(node, expression, value) {
  915. node.value = value;
  916. }
  917. var expressions = {
  918. [ATTRIBUTE]: attributeExpression,
  919. [EVENT]: eventExpression,
  920. [TEXT]: textExpression,
  921. [VALUE]: valueExpression
  922. };
  923. const Expression = Object.seal({
  924. // Static props
  925. node: null,
  926. value: null,
  927. // API methods
  928. /**
  929. * Mount the expression evaluating its initial value
  930. * @param {*} scope - argument passed to the expression to evaluate its current values
  931. * @returns {Expression} self
  932. */
  933. mount(scope) {
  934. // hopefully a pure function
  935. this.value = this.evaluate(scope); // IO() DOM updates
  936. apply(this, this.value);
  937. return this;
  938. },
  939. /**
  940. * Update the expression if its value changed
  941. * @param {*} scope - argument passed to the expression to evaluate its current values
  942. * @returns {Expression} self
  943. */
  944. update(scope) {
  945. // pure function
  946. const value = this.evaluate(scope);
  947. if (this.value !== value) {
  948. // IO() DOM updates
  949. apply(this, value);
  950. this.value = value;
  951. }
  952. return this;
  953. },
  954. /**
  955. * Expression teardown method
  956. * @returns {Expression} self
  957. */
  958. unmount() {
  959. // unmount only the event handling expressions
  960. if (this.type === EVENT) apply(this, null);
  961. return this;
  962. }
  963. });
  964. /**
  965. * IO() function to handle the DOM updates
  966. * @param {Expression} expression - expression object
  967. * @param {*} value - current expression value
  968. * @returns {undefined}
  969. */
  970. function apply(expression, value) {
  971. return expressions[expression.type](expression.node, expression, value, expression.value);
  972. }
  973. function create$2(node, data) {
  974. return Object.assign({}, Expression, {}, data, {
  975. node
  976. });
  977. }
  978. /**
  979. * Create a flat object having as keys a list of methods that if dispatched will propagate
  980. * on the whole collection
  981. * @param {Array} collection - collection to iterate
  982. * @param {Array<string>} methods - methods to execute on each item of the collection
  983. * @param {*} context - context returned by the new methods created
  984. * @returns {Object} a new object to simplify the the nested methods dispatching
  985. */
  986. function flattenCollectionMethods(collection, methods, context) {
  987. return methods.reduce((acc, method) => {
  988. return Object.assign({}, acc, {
  989. [method]: scope => {
  990. return collection.map(item => item[method](scope)) && context;
  991. }
  992. });
  993. }, {});
  994. }
  995. function create$3(node, _ref9) {
  996. let {
  997. expressions
  998. } = _ref9;
  999. return Object.assign({}, flattenCollectionMethods(expressions.map(expression => create$2(node, expression)), ['mount', 'update', 'unmount']));
  1000. }
  1001. const SlotBinding = Object.seal({
  1002. // dynamic binding properties
  1003. node: null,
  1004. name: null,
  1005. template: null,
  1006. // API methods
  1007. mount(scope, parentScope) {
  1008. const templateData = scope.slots ? scope.slots.find((_ref10) => {
  1009. let {
  1010. id
  1011. } = _ref10;
  1012. return id === this.name;
  1013. }) : false;
  1014. const {
  1015. parentNode
  1016. } = this.node;
  1017. this.template = templateData && create$6(templateData.html, templateData.bindings).createDOM(parentNode);
  1018. if (this.template) {
  1019. this.template.mount(this.node, parentScope);
  1020. moveSlotInnerContent(this.node);
  1021. }
  1022. parentNode.removeChild(this.node);
  1023. return this;
  1024. },
  1025. update(scope, parentScope) {
  1026. if (this.template && parentScope) {
  1027. this.template.update(parentScope);
  1028. }
  1029. return this;
  1030. },
  1031. unmount(scope, parentScope, mustRemoveRoot) {
  1032. if (this.template) {
  1033. this.template.unmount(parentScope, null, mustRemoveRoot);
  1034. }
  1035. return this;
  1036. }
  1037. });
  1038. /**
  1039. * Move the inner content of the slots outside of them
  1040. * @param {HTMLNode} slot - slot node
  1041. * @returns {undefined} it's a void function
  1042. */
  1043. function moveSlotInnerContent(slot) {
  1044. if (slot.firstChild) {
  1045. slot.parentNode.insertBefore(slot.firstChild, slot);
  1046. moveSlotInnerContent(slot);
  1047. }
  1048. }
  1049. /**
  1050. * Create a single slot binding
  1051. * @param {HTMLElement} node - slot node
  1052. * @param {string} options.name - slot id
  1053. * @returns {Object} Slot binding object
  1054. */
  1055. function createSlot(node, _ref11) {
  1056. let {
  1057. name
  1058. } = _ref11;
  1059. return Object.assign({}, SlotBinding, {
  1060. node,
  1061. name
  1062. });
  1063. }
  1064. /**
  1065. * Create a new tag object if it was registered before, otherwise fallback to the simple
  1066. * template chunk
  1067. * @param {Function} component - component factory function
  1068. * @param {Array<Object>} slots - array containing the slots markup
  1069. * @param {Array} attributes - dynamic attributes that will be received by the tag element
  1070. * @returns {TagImplementation|TemplateChunk} a tag implementation or a template chunk as fallback
  1071. */
  1072. function getTag(component, slots, attributes) {
  1073. if (slots === void 0) {
  1074. slots = [];
  1075. }
  1076. if (attributes === void 0) {
  1077. attributes = [];
  1078. }
  1079. // if this tag was registered before we will return its implementation
  1080. if (component) {
  1081. return component({
  1082. slots,
  1083. attributes
  1084. });
  1085. } // otherwise we return a template chunk
  1086. return create$6(slotsToMarkup(slots), [...slotBindings(slots), {
  1087. // the attributes should be registered as binding
  1088. // if we fallback to a normal template chunk
  1089. expressions: attributes.map(attr => {
  1090. return Object.assign({
  1091. type: ATTRIBUTE
  1092. }, attr);
  1093. })
  1094. }]);
  1095. }
  1096. /**
  1097. * Merge all the slots bindings into a single array
  1098. * @param {Array<Object>} slots - slots collection
  1099. * @returns {Array<Bindings>} flatten bindings array
  1100. */
  1101. function slotBindings(slots) {
  1102. return slots.reduce((acc, _ref12) => {
  1103. let {
  1104. bindings
  1105. } = _ref12;
  1106. return acc.concat(bindings);
  1107. }, []);
  1108. }
  1109. /**
  1110. * Merge all the slots together in a single markup string
  1111. * @param {Array<Object>} slots - slots collection
  1112. * @returns {string} markup of all the slots in a single string
  1113. */
  1114. function slotsToMarkup(slots) {
  1115. return slots.reduce((acc, slot) => {
  1116. return acc + slot.html;
  1117. }, '');
  1118. }
  1119. const TagBinding = Object.seal({
  1120. // dynamic binding properties
  1121. node: null,
  1122. evaluate: null,
  1123. name: null,
  1124. slots: null,
  1125. tag: null,
  1126. attributes: null,
  1127. getComponent: null,
  1128. mount(scope) {
  1129. return this.update(scope);
  1130. },
  1131. update(scope, parentScope) {
  1132. const name = this.evaluate(scope); // simple update
  1133. if (name === this.name) {
  1134. this.tag.update(scope);
  1135. } else {
  1136. // unmount the old tag if it exists
  1137. this.unmount(scope, parentScope, true); // mount the new tag
  1138. this.name = name;
  1139. this.tag = getTag(this.getComponent(name), this.slots, this.attributes);
  1140. this.tag.mount(this.node, scope);
  1141. }
  1142. return this;
  1143. },
  1144. unmount(scope, parentScope, keepRootTag) {
  1145. if (this.tag) {
  1146. // keep the root tag
  1147. this.tag.unmount(keepRootTag);
  1148. }
  1149. return this;
  1150. }
  1151. });
  1152. function create$4(node, _ref13) {
  1153. let {
  1154. evaluate,
  1155. getComponent,
  1156. slots,
  1157. attributes
  1158. } = _ref13;
  1159. return Object.assign({}, TagBinding, {
  1160. node,
  1161. evaluate,
  1162. slots,
  1163. attributes,
  1164. getComponent
  1165. });
  1166. }
  1167. var bindings = {
  1168. [IF]: create$1,
  1169. [SIMPLE]: create$3,
  1170. [EACH]: create,
  1171. [TAG]: create$4,
  1172. [SLOT]: createSlot
  1173. };
  1174. /**
  1175. * Bind a new expression object to a DOM node
  1176. * @param {HTMLElement} root - DOM node where to bind the expression
  1177. * @param {Object} binding - binding data
  1178. * @returns {Expression} Expression object
  1179. */
  1180. function create$5(root, binding) {
  1181. const {
  1182. selector,
  1183. type,
  1184. redundantAttribute,
  1185. expressions
  1186. } = binding; // find the node to apply the bindings
  1187. const node = selector ? root.querySelector(selector) : root; // remove eventually additional attributes created only to select this node
  1188. if (redundantAttribute) node.removeAttribute(redundantAttribute); // init the binding
  1189. return (bindings[type] || bindings[SIMPLE])(node, Object.assign({}, binding, {
  1190. expressions: expressions || []
  1191. }));
  1192. }
  1193. /**
  1194. * Check if an element is part of an svg
  1195. * @param {HTMLElement} el - element to check
  1196. * @returns {boolean} true if we are in an svg context
  1197. */
  1198. function isSvg(el) {
  1199. const owner = el.ownerSVGElement;
  1200. return !!owner || owner === null;
  1201. } // in this case a simple innerHTML is enough
  1202. function createHTMLTree(html, root) {
  1203. const template = isTemplate(root) ? root : document.createElement('template');
  1204. template.innerHTML = html;
  1205. return template.content;
  1206. } // for svg nodes we need a bit more work
  1207. function createSVGTree(html, container) {
  1208. // create the SVGNode
  1209. const svgNode = container.ownerDocument.importNode(new window.DOMParser().parseFromString(`<svg xmlns="http://www.w3.org/2000/svg">${html}</svg>`, 'application/xml').documentElement, true);
  1210. return svgNode;
  1211. }
  1212. /**
  1213. * Create the DOM that will be injected
  1214. * @param {Object} root - DOM node to find out the context where the fragment will be created
  1215. * @param {string} html - DOM to create as string
  1216. * @returns {HTMLDocumentFragment|HTMLElement} a new html fragment
  1217. */
  1218. function createDOMTree(root, html) {
  1219. if (isSvg(root)) return createSVGTree(html, root);
  1220. return createHTMLTree(html, root);
  1221. }
  1222. /**
  1223. * Move all the child nodes from a source tag to another
  1224. * @param {HTMLElement} source - source node
  1225. * @param {HTMLElement} target - target node
  1226. * @returns {undefined} it's a void method ¯\_()_/¯
  1227. */
  1228. // Ignore this helper because it's needed only for svg tags
  1229. /* istanbul ignore next */
  1230. function moveChildren(source, target) {
  1231. if (source.firstChild) {
  1232. target.appendChild(source.firstChild);
  1233. moveChildren(source, target);
  1234. }
  1235. }
  1236. /**
  1237. * Inject the DOM tree into a target node
  1238. * @param {HTMLElement} el - target element
  1239. * @param {HTMLFragment|SVGElement} dom - dom tree to inject
  1240. * @returns {undefined}
  1241. */
  1242. function injectDOM(el, dom) {
  1243. switch (true) {
  1244. case isSvg(el):
  1245. moveChildren(dom, el);
  1246. break;
  1247. case isTemplate(el):
  1248. el.parentNode.replaceChild(dom, el);
  1249. break;
  1250. default:
  1251. el.appendChild(dom);
  1252. }
  1253. }
  1254. /**
  1255. * Create the Template DOM skeleton
  1256. * @param {HTMLElement} el - root node where the DOM will be injected
  1257. * @param {string} html - markup that will be injected into the root node
  1258. * @returns {HTMLFragment} fragment that will be injected into the root node
  1259. */
  1260. function createTemplateDOM(el, html) {
  1261. return html && (typeof html === 'string' ? createDOMTree(el, html) : html);
  1262. }
  1263. /**
  1264. * Template Chunk model
  1265. * @type {Object}
  1266. */
  1267. const TemplateChunk = Object.freeze({
  1268. // Static props
  1269. bindings: null,
  1270. bindingsData: null,
  1271. html: null,
  1272. isTemplateTag: false,
  1273. fragment: null,
  1274. children: null,
  1275. dom: null,
  1276. el: null,
  1277. /**
  1278. * Create the template DOM structure that will be cloned on each mount
  1279. * @param {HTMLElement} el - the root node
  1280. * @returns {TemplateChunk} self
  1281. */
  1282. createDOM(el) {
  1283. // make sure that the DOM gets created before cloning the template
  1284. this.dom = this.dom || createTemplateDOM(el, this.html);
  1285. return this;
  1286. },
  1287. // API methods
  1288. /**
  1289. * Attach the template to a DOM node
  1290. * @param {HTMLElement} el - target DOM node
  1291. * @param {*} scope - template data
  1292. * @param {*} parentScope - scope of the parent template tag
  1293. * @param {Object} meta - meta properties needed to handle the <template> tags in loops
  1294. * @returns {TemplateChunk} self
  1295. */
  1296. mount(el, scope, parentScope, meta) {
  1297. if (meta === void 0) {
  1298. meta = {};
  1299. }
  1300. if (!el) throw new Error('Please provide DOM node to mount properly your template');
  1301. if (this.el) this.unmount(scope); // <template> tags require a bit more work
  1302. // the template fragment might be already created via meta outside of this call
  1303. const {
  1304. fragment,
  1305. children,
  1306. avoidDOMInjection
  1307. } = meta; // <template> bindings of course can not have a root element
  1308. // so we check the parent node to set the query selector bindings
  1309. const {
  1310. parentNode
  1311. } = children ? children[0] : el;
  1312. this.isTemplateTag = isTemplate(el); // create the DOM if it wasn't created before
  1313. this.createDOM(el);
  1314. if (this.dom) {
  1315. // create the new template dom fragment if it want already passed in via meta
  1316. this.fragment = fragment || this.dom.cloneNode(true);
  1317. } // store root node
  1318. // notice that for template tags the root note will be the parent tag
  1319. this.el = this.isTemplateTag ? parentNode : el; // create the children array only for the <template> fragments
  1320. this.children = this.isTemplateTag ? children || Array.from(this.fragment.childNodes) : null; // inject the DOM into the el only if a fragment is available
  1321. if (!avoidDOMInjection && this.fragment) injectDOM(el, this.fragment); // create the bindings
  1322. this.bindings = this.bindingsData.map(binding => create$5(this.el, binding));
  1323. this.bindings.forEach(b => b.mount(scope, parentScope));
  1324. return this;
  1325. },
  1326. /**
  1327. * Update the template with fresh data
  1328. * @param {*} scope - template data
  1329. * @param {*} parentScope - scope of the parent template tag
  1330. * @returns {TemplateChunk} self
  1331. */
  1332. update(scope, parentScope) {
  1333. this.bindings.forEach(b => b.update(scope, parentScope));
  1334. return this;
  1335. },
  1336. /**
  1337. * Remove the template from the node where it was initially mounted
  1338. * @param {*} scope - template data
  1339. * @param {*} parentScope - scope of the parent template tag
  1340. * @param {boolean|null} mustRemoveRoot - if true remove the root element,
  1341. * if false or undefined clean the root tag content, if null don't touch the DOM
  1342. * @returns {TemplateChunk} self
  1343. */
  1344. unmount(scope, parentScope, mustRemoveRoot) {
  1345. if (this.el) {
  1346. this.bindings.forEach(b => b.unmount(scope, parentScope, mustRemoveRoot));
  1347. switch (true) {
  1348. // <template> tags should be treated a bit differently
  1349. // we need to clear their children only if it's explicitly required by the caller
  1350. // via mustRemoveRoot !== null
  1351. case this.isTemplateTag === true && mustRemoveRoot !== null:
  1352. clearChildren(this.children);
  1353. break;
  1354. // remove the root node only if the mustRemoveRoot === true
  1355. case mustRemoveRoot === true && this.el.parentNode !== null:
  1356. this.el.parentNode.removeChild(this.el);
  1357. break;
  1358. // otherwise we clean the node children
  1359. case mustRemoveRoot !== null:
  1360. cleanNode(this.el);
  1361. break;
  1362. }
  1363. this.el = null;
  1364. }
  1365. return this;
  1366. },
  1367. /**
  1368. * Clone the template chunk
  1369. * @returns {TemplateChunk} a clone of this object resetting the this.el property
  1370. */
  1371. clone() {
  1372. return Object.assign({}, this, {
  1373. el: null
  1374. });
  1375. }
  1376. });
  1377. /**
  1378. * Create a template chunk wiring also the bindings
  1379. * @param {string|HTMLElement} html - template string
  1380. * @param {Array} bindings - bindings collection
  1381. * @returns {TemplateChunk} a new TemplateChunk copy
  1382. */
  1383. function create$6(html, bindings) {
  1384. if (bindings === void 0) {
  1385. bindings = [];
  1386. }
  1387. return Object.assign({}, TemplateChunk, {
  1388. html,
  1389. bindingsData: bindings
  1390. });
  1391. }
  1392. /**
  1393. * Quick type checking
  1394. * @param {*} element - anything
  1395. * @param {string} type - type definition
  1396. * @returns {boolean} true if the type corresponds
  1397. */
  1398. function checkType(element, type) {
  1399. return typeof element === type;
  1400. }
  1401. /**
  1402. * Check that will be passed if its argument is a function
  1403. * @param {*} value - value to check
  1404. * @returns {boolean} - true if the value is a function
  1405. */
  1406. function isFunction(value) {
  1407. return checkType(value, 'function');
  1408. }
  1409. /* eslint-disable fp/no-mutating-methods */
  1410. /**
  1411. * Throw an error
  1412. * @param {string} error - error message
  1413. * @returns {undefined} it's a IO void function
  1414. */
  1415. function panic(error) {
  1416. throw new Error(error);
  1417. }
  1418. /**
  1419. * Call the first argument received only if it's a function otherwise return it as it is
  1420. * @param {*} source - anything
  1421. * @returns {*} anything
  1422. */
  1423. function callOrAssign(source) {
  1424. return isFunction(source) ? source.prototype && source.prototype.constructor ? new source() : source() : source;
  1425. }
  1426. /**
  1427. * Convert a string from camel case to dash-case
  1428. * @param {string} string - probably a component tag name
  1429. * @returns {string} component name normalized
  1430. */
  1431. function camelToDashCase(string) {
  1432. return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  1433. }
  1434. /**
  1435. * Convert a string containing dashes to camel case
  1436. * @param {string} string - input string
  1437. * @returns {string} my-string -> myString
  1438. */
  1439. function dashToCamelCase(string) {
  1440. return string.replace(/-(\w)/g, (_, c) => c.toUpperCase());
  1441. }
  1442. /**
  1443. * Define default properties if they don't exist on the source object
  1444. * @param {Object} source - object that will receive the default properties
  1445. * @param {Object} defaults - object containing additional optional keys
  1446. * @returns {Object} the original object received enhanced
  1447. */
  1448. function defineDefaults(source, defaults) {
  1449. Object.entries(defaults).forEach((_ref) => {
  1450. let [key, value] = _ref;
  1451. if (!source[key]) source[key] = value;
  1452. });
  1453. return source;
  1454. } // doese simply nothing
  1455. function noop() {
  1456. return this;
  1457. }
  1458. /**
  1459. * Autobind the methods of a source object to itself
  1460. * @param {Object} source - probably a riot tag instance
  1461. * @param {Array<string>} methods - list of the methods to autobind
  1462. * @returns {Object} the original object received
  1463. */
  1464. function autobindMethods(source, methods) {
  1465. methods.forEach(method => {
  1466. source[method] = source[method].bind(source);
  1467. });
  1468. return source;
  1469. }
  1470. /**
  1471. * Helper function to set an immutable property
  1472. * @param {Object} source - object where the new property will be set
  1473. * @param {string} key - object key where the new property will be stored
  1474. * @param {*} value - value of the new property
  1475. * @param {Object} options - set the propery overriding the default options
  1476. * @returns {Object} - the original object modified
  1477. */
  1478. function defineProperty(source, key, value, options) {
  1479. if (options === void 0) {
  1480. options = {};
  1481. }
  1482. Object.defineProperty(source, key, Object.assign({
  1483. value,
  1484. enumerable: false,
  1485. writable: false,
  1486. configurable: true
  1487. }, options));
  1488. return source;
  1489. }
  1490. /**
  1491. * Define multiple properties on a target object
  1492. * @param {Object} source - object where the new properties will be set
  1493. * @param {Object} properties - object containing as key pair the key + value properties
  1494. * @param {Object} options - set the propery overriding the default options
  1495. * @returns {Object} the original object modified
  1496. */
  1497. function defineProperties(source, properties, options) {
  1498. Object.entries(properties).forEach((_ref2) => {
  1499. let [key, value] = _ref2;
  1500. defineProperty(source, key, value, options);
  1501. });
  1502. return source;
  1503. }
  1504. /**
  1505. * Evaluate a list of attribute expressions
  1506. * @param {Array} attributes - attribute expressions generated by the riot compiler
  1507. * @returns {Object} key value pairs with the result of the computation
  1508. */
  1509. function evaluateAttributeExpressions(attributes) {
  1510. return attributes.reduce((acc, attribute) => {
  1511. const {
  1512. value,
  1513. type
  1514. } = attribute;
  1515. switch (true) {
  1516. // spread attribute
  1517. case !attribute.name && type === expressionTypes.ATTRIBUTE:
  1518. return Object.assign({}, acc, {}, value);
  1519. // value attribute
  1520. case type === expressionTypes.VALUE:
  1521. acc[VALUE_ATTRIBUTE] = attribute.value;
  1522. break;
  1523. // normal attributes
  1524. default:
  1525. acc[dashToCamelCase(attribute.name)] = attribute.value;
  1526. }
  1527. return acc;
  1528. }, {});
  1529. }
  1530. /**
  1531. * Converts any DOM node/s to a loopable array
  1532. * @param { HTMLElement|NodeList } els - single html element or a node list
  1533. * @returns { Array } always a loopable object
  1534. */
  1535. function domToArray(els) {
  1536. // can this object be already looped?
  1537. if (!Array.isArray(els)) {
  1538. // is it a node list?
  1539. if (/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(els)) && typeof els.length === 'number') return Array.from(els);else // if it's a single node
  1540. // it will be returned as "array" with one single entry
  1541. return [els];
  1542. } // this object could be looped out of the box
  1543. return els;
  1544. }
  1545. /**
  1546. * Normalize the return values, in case of a single value we avoid to return an array
  1547. * @param { Array } values - list of values we want to return
  1548. * @returns { Array|string|boolean } either the whole list of values or the single one found
  1549. * @private
  1550. */
  1551. const normalize = values => values.length === 1 ? values[0] : values;
  1552. /**
  1553. * Parse all the nodes received to get/remove/check their attributes
  1554. * @param { HTMLElement|NodeList|Array } els - DOM node/s to parse
  1555. * @param { string|Array } name - name or list of attributes
  1556. * @param { string } method - method that will be used to parse the attributes
  1557. * @returns { Array|string } result of the parsing in a list or a single value
  1558. * @private
  1559. */
  1560. function parseNodes(els, name, method) {
  1561. const names = typeof name === 'string' ? [name] : name;
  1562. return normalize(domToArray(els).map(el => {
  1563. return normalize(names.map(n => el[method](n)));
  1564. }));
  1565. }
  1566. /**
  1567. * Set any attribute on a single or a list of DOM nodes
  1568. * @param { HTMLElement|NodeList|Array } els - DOM node/s to parse
  1569. * @param { string|Object } name - either the name of the attribute to set
  1570. * or a list of properties as object key - value
  1571. * @param { string } value - the new value of the attribute (optional)
  1572. * @returns { HTMLElement|NodeList|Array } the original array of elements passed to this function
  1573. *
  1574. * @example
  1575. *
  1576. * import { set } from 'bianco.attr'
  1577. *
  1578. * const img = document.createElement('img')
  1579. *
  1580. * set(img, 'width', 100)
  1581. *
  1582. * // or also
  1583. * set(img, {
  1584. * width: 300,
  1585. * height: 300
  1586. * })
  1587. *
  1588. */
  1589. function set(els, name, value) {
  1590. const attrs = typeof name === 'object' ? name : {
  1591. [name]: value
  1592. };
  1593. const props = Object.keys(attrs);
  1594. domToArray(els).forEach(el => {
  1595. props.forEach(prop => el.setAttribute(prop, attrs[prop]));
  1596. });
  1597. return els;
  1598. }
  1599. /**
  1600. * Get any attribute from a single or a list of DOM nodes
  1601. * @param { HTMLElement|NodeList|Array } els - DOM node/s to parse
  1602. * @param { string|Array } name - name or list of attributes to get
  1603. * @returns { Array|string } list of the attributes found
  1604. *
  1605. * @example
  1606. *
  1607. * import { get } from 'bianco.attr'
  1608. *
  1609. * const img = document.createElement('img')
  1610. *
  1611. * get(img, 'width') // => '200'
  1612. *
  1613. * // or also
  1614. * get(img, ['width', 'height']) // => ['200', '300']
  1615. *
  1616. * // or also
  1617. * get([img1, img2], ['width', 'height']) // => [['200', '300'], ['500', '200']]
  1618. */
  1619. function get(els, name) {
  1620. return parseNodes(els, name, 'getAttribute');
  1621. }
  1622. /**
  1623. * Get all the element attributes as object
  1624. * @param {HTMLElement} element - DOM node we want to parse
  1625. * @returns {Object} all the attributes found as a key value pairs
  1626. */
  1627. function DOMattributesToObject(element) {
  1628. return Array.from(element.attributes).reduce((acc, attribute) => {
  1629. acc[dashToCamelCase(attribute.name)] = attribute.value;
  1630. return acc;
  1631. }, {});
  1632. }
  1633. /**
  1634. * Get the tag name of any DOM node
  1635. * @param {HTMLElement} element - DOM node we want to inspect
  1636. * @returns {string} name to identify this dom node in riot
  1637. */
  1638. function getName(element) {
  1639. return get(element, IS_DIRECTIVE) || element.tagName.toLowerCase();
  1640. }
  1641. /**
  1642. * Simple helper to find DOM nodes returning them as array like loopable object
  1643. * @param { string|DOMNodeList } selector - either the query or the DOM nodes to arraify
  1644. * @param { HTMLElement } ctx - context defining where the query will search for the DOM nodes
  1645. * @returns { Array } DOM nodes found as array
  1646. */
  1647. function $(selector, ctx) {
  1648. return domToArray(typeof selector === 'string' ? (ctx || document).querySelectorAll(selector) : selector);
  1649. }
  1650. const CSS_BY_NAME = new Map();
  1651. const STYLE_NODE_SELECTOR = 'style[riot]'; // memoized curried function
  1652. const getStyleNode = (style => {
  1653. return () => {
  1654. // lazy evaluation:
  1655. // if this function was already called before
  1656. // we return its cached result
  1657. if (style) return style; // create a new style element or use an existing one
  1658. // and cache it internally
  1659. style = $(STYLE_NODE_SELECTOR)[0] || document.createElement('style');
  1660. set(style, 'type', 'text/css');
  1661. /* istanbul ignore next */
  1662. if (!style.parentNode) document.head.appendChild(style);
  1663. return style;
  1664. };
  1665. })();
  1666. /**
  1667. * Object that will be used to inject and manage the css of every tag instance
  1668. */
  1669. var cssManager = {
  1670. CSS_BY_NAME,
  1671. /**
  1672. * Save a tag style to be later injected into DOM
  1673. * @param { string } name - if it's passed we will map the css to a tagname
  1674. * @param { string } css - css string
  1675. * @returns {Object} self
  1676. */
  1677. add(name, css) {
  1678. if (!CSS_BY_NAME.has(name)) {
  1679. CSS_BY_NAME.set(name, css);
  1680. this.inject();
  1681. }
  1682. return this;
  1683. },
  1684. /**
  1685. * Inject all previously saved tag styles into DOM
  1686. * innerHTML seems slow: http://jsperf.com/riot-insert-style
  1687. * @returns {Object} self
  1688. */
  1689. inject() {
  1690. getStyleNode().innerHTML = [...CSS_BY_NAME.values()].join('\n');
  1691. return this;
  1692. },
  1693. /**
  1694. * Remove a tag style from the DOM
  1695. * @param {string} name a registered tagname
  1696. * @returns {Object} self
  1697. */
  1698. remove(name) {
  1699. if (CSS_BY_NAME.has(name)) {
  1700. CSS_BY_NAME.delete(name);
  1701. this.inject();
  1702. }
  1703. return this;
  1704. }
  1705. };
  1706. /**
  1707. * Function to curry any javascript method
  1708. * @param {Function} fn - the target function we want to curry
  1709. * @param {...[args]} acc - initial arguments
  1710. * @returns {Function|*} it will return a function until the target function
  1711. * will receive all of its arguments
  1712. */
  1713. function curry(fn) {
  1714. for (var _len = arguments.length, acc = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  1715. acc[_key - 1] = arguments[_key];
  1716. }
  1717. return function () {
  1718. for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  1719. args[_key2] = arguments[_key2];
  1720. }
  1721. args = [...acc, ...args];
  1722. return args.length < fn.length ? curry(fn, ...args) : fn(...args);
  1723. };
  1724. }
  1725. const COMPONENT_CORE_HELPERS = Object.freeze({
  1726. // component helpers
  1727. $(selector) {
  1728. return $(selector, this.root)[0];
  1729. },
  1730. $$(selector) {
  1731. return $(selector, this.root);
  1732. }
  1733. });
  1734. const COMPONENT_LIFECYCLE_METHODS = Object.freeze({
  1735. shouldUpdate: noop,
  1736. onBeforeMount: noop,
  1737. onMounted: noop,
  1738. onBeforeUpdate: noop,
  1739. onUpdated: noop,
  1740. onBeforeUnmount: noop,
  1741. onUnmounted: noop
  1742. });
  1743. const MOCKED_TEMPLATE_INTERFACE = {
  1744. update: noop,
  1745. mount: noop,
  1746. unmount: noop,
  1747. clone: noop,
  1748. createDOM: noop
  1749. /**
  1750. * Factory function to create the component templates only once
  1751. * @param {Function} template - component template creation function
  1752. * @param {Object} components - object containing the nested components
  1753. * @returns {TemplateChunk} template chunk object
  1754. */
  1755. };
  1756. function componentTemplateFactory(template, components) {
  1757. return template(create$6, expressionTypes, bindingTypes, name => {
  1758. return components[name] || COMPONENTS_IMPLEMENTATION_MAP.get(name);
  1759. });
  1760. }
  1761. /**
  1762. * Create the component interface needed for the @riotjs/dom-bindings tag bindings
  1763. * @param {string} options.css - component css
  1764. * @param {Function} options.template - functon that will return the dom-bindings template function
  1765. * @param {Object} options.exports - component interface
  1766. * @param {string} options.name - component name
  1767. * @returns {Object} component like interface
  1768. */
  1769. function createComponent(_ref) {
  1770. let {
  1771. css,
  1772. template,
  1773. exports,
  1774. name
  1775. } = _ref;
  1776. const templateFn = template ? componentTemplateFactory(template, exports ? createSubcomponents(exports.components) : {}) : MOCKED_TEMPLATE_INTERFACE;
  1777. return (_ref2) => {
  1778. let {
  1779. slots,
  1780. attributes,
  1781. props
  1782. } = _ref2;
  1783. const componentAPI = callOrAssign(exports) || {};
  1784. const component = defineComponent({
  1785. css,
  1786. template: templateFn,
  1787. componentAPI,
  1788. name
  1789. })({
  1790. slots,
  1791. attributes,
  1792. props
  1793. }); // notice that for the components create via tag binding
  1794. // we need to invert the mount (state/parentScope) arguments
  1795. // the template bindings will only forward the parentScope updates
  1796. // and never deal with the component state
  1797. return {
  1798. mount(element, parentScope, state) {
  1799. return component.mount(element, state, parentScope);
  1800. },
  1801. update(parentScope, state) {
  1802. return component.update(state, parentScope);
  1803. },
  1804. unmount(preserveRoot) {
  1805. return component.unmount(preserveRoot);
  1806. }
  1807. };
  1808. };
  1809. }
  1810. /**
  1811. * Component definition function
  1812. * @param {Object} implementation - the componen implementation will be generated via compiler
  1813. * @param {Object} component - the component initial properties
  1814. * @returns {Object} a new component implementation object
  1815. */
  1816. function defineComponent(_ref3) {
  1817. let {
  1818. css,
  1819. template,
  1820. componentAPI,
  1821. name
  1822. } = _ref3;
  1823. // add the component css into the DOM
  1824. if (css && name) cssManager.add(name, css);
  1825. return curry(enhanceComponentAPI)(defineProperties( // set the component defaults without overriding the original component API
  1826. defineDefaults(componentAPI, Object.assign({}, COMPONENT_LIFECYCLE_METHODS, {
  1827. state: {}
  1828. })), Object.assign({
  1829. // defined during the component creation
  1830. slots: null,
  1831. root: null
  1832. }, COMPONENT_CORE_HELPERS, {
  1833. name,
  1834. css,
  1835. template
  1836. })));
  1837. }
  1838. /**
  1839. * Evaluate the component properties either from its real attributes or from its attribute expressions
  1840. * @param {HTMLElement} element - component root
  1841. * @param {Array} attributeExpressions - attribute values generated via createAttributeBindings
  1842. * @returns {Object} attributes key value pairs
  1843. */
  1844. function evaluateProps(element, attributeExpressions) {
  1845. if (attributeExpressions === void 0) {
  1846. attributeExpressions = [];
  1847. }
  1848. return Object.assign({}, DOMattributesToObject(element), {}, evaluateAttributeExpressions(attributeExpressions));
  1849. }
  1850. /**
  1851. * Create the bindings to update the component attributes
  1852. * @param {HTMLElement} node - node where we will bind the expressions
  1853. * @param {Array} attributes - list of attribute bindings
  1854. * @returns {TemplateChunk} - template bindings object
  1855. */
  1856. function createAttributeBindings(node, attributes) {
  1857. if (attributes === void 0) {
  1858. attributes = [];
  1859. }
  1860. const expressions = attributes.map(a => create$2(node, a));
  1861. const binding = {};
  1862. const updateValues = method => scope => {
  1863. expressions.forEach(e => e[method](scope));
  1864. return binding;
  1865. };
  1866. return Object.assign(binding, {
  1867. expressions,
  1868. mount: updateValues('mount'),
  1869. update: updateValues('update'),
  1870. unmount: updateValues('unmount')
  1871. });
  1872. }
  1873. /**
  1874. * Create the subcomponents that can be included inside a tag in runtime
  1875. * @param {Object} components - components imported in runtime
  1876. * @returns {Object} all the components transformed into Riot.Component factory functions
  1877. */
  1878. function createSubcomponents(components) {
  1879. if (components === void 0) {
  1880. components = {};
  1881. }
  1882. return Object.entries(callOrAssign(components)).reduce((acc, _ref4) => {
  1883. let [key, value] = _ref4;
  1884. acc[camelToDashCase(key)] = createComponent(value);
  1885. return acc;
  1886. }, {});
  1887. }
  1888. /**
  1889. * Run the component instance through all the plugins set by the user
  1890. * @param {Object} component - component instance
  1891. * @returns {Object} the component enhanced by the plugins
  1892. */
  1893. function runPlugins(component) {
  1894. return [...PLUGINS_SET].reduce((c, fn) => fn(c) || c, component);
  1895. }
  1896. /**
  1897. * Compute the component current state merging it with its previous state
  1898. * @param {Object} oldState - previous state object
  1899. * @param {Object} newState - new state givent to the `update` call
  1900. * @returns {Object} new object state
  1901. */
  1902. function computeState(oldState, newState) {
  1903. return Object.assign({}, oldState, {}, callOrAssign(newState));
  1904. }
  1905. /**
  1906. * Add eventually the "is" attribute to link this DOM node to its css
  1907. * @param {HTMLElement} element - target root node
  1908. * @param {string} name - name of the component mounted
  1909. * @returns {undefined} it's a void function
  1910. */
  1911. function addCssHook(element, name) {
  1912. if (getName(element) !== name) {
  1913. set(element, 'is', name);
  1914. }
  1915. }
  1916. /**
  1917. * Component creation factory function that will enhance the user provided API
  1918. * @param {Object} component - a component implementation previously defined
  1919. * @param {Array} options.slots - component slots generated via riot compiler
  1920. * @param {Array} options.attributes - attribute expressions generated via riot compiler
  1921. * @returns {Riot.Component} a riot component instance
  1922. */
  1923. function enhanceComponentAPI(component, _ref5) {
  1924. let {
  1925. slots,
  1926. attributes,
  1927. props
  1928. } = _ref5;
  1929. const initialProps = callOrAssign(props);
  1930. return autobindMethods(runPlugins(defineProperties(Object.create(component), {
  1931. mount(element, state, parentScope) {
  1932. if (state === void 0) {
  1933. state = {};
  1934. }
  1935. this[ATTRIBUTES_KEY_SYMBOL] = createAttributeBindings(element, attributes).mount(parentScope);
  1936. this.props = Object.freeze(Object.assign({}, initialProps, {}, evaluateProps(element, this[ATTRIBUTES_KEY_SYMBOL].expressions)));
  1937. this.state = computeState(this.state, state);
  1938. this[TEMPLATE_KEY_SYMBOL] = this.template.createDOM(element).clone(); // link this object to the DOM node
  1939. element[DOM_COMPONENT_INSTANCE_PROPERTY] = this; // add eventually the 'is' attribute
  1940. component.name && addCssHook(element, component.name); // define the root element
  1941. defineProperty(this, 'root', element); // define the slots array
  1942. defineProperty(this, 'slots', slots); // before mount lifecycle event
  1943. this.onBeforeMount(this.props, this.state); // mount the template
  1944. this[TEMPLATE_KEY_SYMBOL].mount(element, this, parentScope);
  1945. this.onMounted(this.props, this.state);
  1946. return this;
  1947. },
  1948. update(state, parentScope) {
  1949. if (state === void 0) {
  1950. state = {};
  1951. }
  1952. if (parentScope) {
  1953. this[ATTRIBUTES_KEY_SYMBOL].update(parentScope);
  1954. }
  1955. const newProps = evaluateProps(this.root, this[ATTRIBUTES_KEY_SYMBOL].expressions);
  1956. if (this.shouldUpdate(newProps, this.props) === false) return;
  1957. this.props = Object.freeze(Object.assign({}, initialProps, {}, newProps));
  1958. this.state = computeState(this.state, state);
  1959. this.onBeforeUpdate(this.props, this.state);
  1960. this[TEMPLATE_KEY_SYMBOL].update(this, parentScope);
  1961. this.onUpdated(this.props, this.state);
  1962. return this;
  1963. },
  1964. unmount(preserveRoot) {
  1965. this.onBeforeUnmount(this.props, this.state);
  1966. this[ATTRIBUTES_KEY_SYMBOL].unmount(); // if the preserveRoot is null the template html will be left untouched
  1967. // in that case the DOM cleanup will happen differently from a parent node
  1968. this[TEMPLATE_KEY_SYMBOL].unmount(this, {}, preserveRoot === null ? null : !preserveRoot);
  1969. this.onUnmounted(this.props, this.state);
  1970. return this;
  1971. }
  1972. })), Object.keys(component).filter(prop => isFunction(component[prop])));
  1973. }
  1974. /**
  1975. * Component initialization function starting from a DOM node
  1976. * @param {HTMLElement} element - element to upgrade
  1977. * @param {Object} initialProps - initial component properties
  1978. * @param {string} componentName - component id
  1979. * @returns {Object} a new component instance bound to a DOM node
  1980. */
  1981. function mountComponent(element, initialProps, componentName) {
  1982. const name = componentName || getName(element);
  1983. if (!COMPONENTS_IMPLEMENTATION_MAP.has(name)) panic(`The component named "${name}" was never registered`);
  1984. const component = COMPONENTS_IMPLEMENTATION_MAP.get(name)({
  1985. props: initialProps
  1986. });
  1987. return component.mount(element);
  1988. }
  1989. /**
  1990. * Similar to compose but performs from left-to-right function composition.<br/>
  1991. * {@link https://30secondsofcode.org/function#composeright see also}
  1992. * @param {...[function]} fns) - list of unary function
  1993. * @returns {*} result of the computation
  1994. */
  1995. /**
  1996. * Performs right-to-left function composition.<br/>
  1997. * Use Array.prototype.reduce() to perform right-to-left function composition.<br/>
  1998. * The last (rightmost) function can accept one or more arguments; the remaining functions must be unary.<br/>
  1999. * {@link https://30secondsofcode.org/function#compose original source code}
  2000. * @param {...[function]} fns) - list of unary function
  2001. * @returns {*} result of the computation
  2002. */
  2003. function compose() {
  2004. for (var _len2 = arguments.length, fns = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  2005. fns[_key2] = arguments[_key2];
  2006. }
  2007. return fns.reduce((f, g) => function () {
  2008. return f(g(...arguments));
  2009. });
  2010. }
  2011. const {
  2012. DOM_COMPONENT_INSTANCE_PROPERTY: DOM_COMPONENT_INSTANCE_PROPERTY$1,
  2013. COMPONENTS_IMPLEMENTATION_MAP: COMPONENTS_IMPLEMENTATION_MAP$1,
  2014. PLUGINS_SET: PLUGINS_SET$1
  2015. } = globals;
  2016. /**
  2017. * Riot public api
  2018. */
  2019. /**
  2020. * Register a custom tag by name
  2021. * @param {string} name - component name
  2022. * @param {Object} implementation - tag implementation
  2023. * @returns {Map} map containing all the components implementations
  2024. */
  2025. function register(name, _ref) {
  2026. let {
  2027. css,
  2028. template,
  2029. exports
  2030. } = _ref;
  2031. if (COMPONENTS_IMPLEMENTATION_MAP$1.has(name)) panic(`The component "${name}" was already registered`);
  2032. COMPONENTS_IMPLEMENTATION_MAP$1.set(name, createComponent({
  2033. name,
  2034. css,
  2035. template,
  2036. exports
  2037. }));
  2038. return COMPONENTS_IMPLEMENTATION_MAP$1;
  2039. }
  2040. /**
  2041. * Unregister a riot web component
  2042. * @param {string} name - component name
  2043. * @returns {Map} map containing all the components implementations
  2044. */
  2045. function unregister(name) {
  2046. if (!COMPONENTS_IMPLEMENTATION_MAP$1.has(name)) panic(`The component "${name}" was never registered`);
  2047. COMPONENTS_IMPLEMENTATION_MAP$1.delete(name);
  2048. cssManager.remove(name);
  2049. return COMPONENTS_IMPLEMENTATION_MAP$1;
  2050. }
  2051. /**
  2052. * Mounting function that will work only for the components that were globally registered
  2053. * @param {string|HTMLElement} selector - query for the selection or a DOM element
  2054. * @param {Object} initialProps - the initial component properties
  2055. * @param {string} name - optional component name
  2056. * @returns {Array} list of nodes upgraded
  2057. */
  2058. function mount(selector, initialProps, name) {
  2059. return $(selector).map(element => mountComponent(element, initialProps, name));
  2060. }
  2061. /**
  2062. * Sweet unmounting helper function for the DOM node mounted manually by the user
  2063. * @param {string|HTMLElement} selector - query for the selection or a DOM element
  2064. * @param {boolean|null} keepRootElement - if true keep the root element
  2065. * @returns {Array} list of nodes unmounted
  2066. */
  2067. function unmount(selector, keepRootElement) {
  2068. return $(selector).map(element => {
  2069. if (element[DOM_COMPONENT_INSTANCE_PROPERTY$1]) {
  2070. element[DOM_COMPONENT_INSTANCE_PROPERTY$1].unmount(keepRootElement);
  2071. }
  2072. return element;
  2073. });
  2074. }
  2075. /**
  2076. * Define a riot plugin
  2077. * @param {Function} plugin - function that will receive all the components created
  2078. * @returns {Set} the set containing all the plugins installed
  2079. */
  2080. function install(plugin) {
  2081. if (!isFunction(plugin)) panic('Plugins must be of type function');
  2082. if (PLUGINS_SET$1.has(plugin)) panic('This plugin was already install');
  2083. PLUGINS_SET$1.add(plugin);
  2084. return PLUGINS_SET$1;
  2085. }
  2086. /**
  2087. * Uninstall a riot plugin
  2088. * @param {Function} plugin - plugin previously installed
  2089. * @returns {Set} the set containing all the plugins installed
  2090. */
  2091. function uninstall(plugin) {
  2092. if (!PLUGINS_SET$1.has(plugin)) panic('This plugin was never installed');
  2093. PLUGINS_SET$1.delete(plugin);
  2094. return PLUGINS_SET$1;
  2095. }
  2096. /**
  2097. * Helpter method to create component without relying on the registered ones
  2098. * @param {Object} implementation - component implementation
  2099. * @returns {Function} function that will allow you to mount a riot component on a DOM node
  2100. */
  2101. function component(implementation) {
  2102. return (el, props) => compose(c => c.mount(el), c => c({
  2103. props
  2104. }), createComponent)(implementation);
  2105. }
  2106. /** @type {string} current riot version */
  2107. const version = 'v4.5.0'; // expose some internal stuff that might be used from external tools
  2108. const __ = {
  2109. cssManager,
  2110. defineComponent,
  2111. globals
  2112. };
  2113. /***/ }),
  2114. /***/ "./node_modules/smooth-scroll/dist/smooth-scroll.polyfills.min.js":
  2115. /*!************************************************************************!*\
  2116. !*** ./node_modules/smooth-scroll/dist/smooth-scroll.polyfills.min.js ***!
  2117. \************************************************************************/
  2118. /*! no static exports found */
  2119. /***/ (function(module, exports, __webpack_require__) {
  2120. /* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! smooth-scroll v16.1.0 | (c) 2019 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/smooth-scroll */
  2121. window.Element&&!Element.prototype.closest&&(Element.prototype.closest=function(e){var t,n=(this.document||this.ownerDocument).querySelectorAll(e),o=this;do{for(t=n.length;0<=--t&&n.item(t)!==o;);}while(t<0&&(o=o.parentElement));return o}),(function(){if("function"==typeof window.CustomEvent)return;function e(e,t){t=t||{bubbles:!1,cancelable:!1,detail:void 0};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),n}e.prototype=window.Event.prototype,window.CustomEvent=e})(),(function(){for(var r=0,e=["ms","moz","webkit","o"],t=0;t<e.length&&!window.requestAnimationFrame;++t)window.requestAnimationFrame=window[e[t]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[e[t]+"CancelAnimationFrame"]||window[e[t]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(e,t){var n=(new Date).getTime(),o=Math.max(0,16-(n-r)),a=window.setTimeout((function(){e(n+o)}),o);return r=n+o,a}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(e){clearTimeout(e)})})(),(function(e,t){ true?!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function(){return t(e)}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
  2122. __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)):undefined})("undefined"!=typeof global?global:"undefined"!=typeof window?window:this,(function(q){"use strict";var I={ignore:"[data-scroll-ignore]",header:null,topOnEmptyHash:!0,speed:500,speedAsDuration:!1,durationMax:null,durationMin:null,clip:!0,offset:0,easing:"easeInOutCubic",customEasing:null,updateURL:!0,popstate:!0,emitEvents:!0},F=function(){var n={};return Array.prototype.forEach.call(arguments,(function(e){for(var t in e){if(!e.hasOwnProperty(t))return;n[t]=e[t]}})),n},r=function(e){"#"===e.charAt(0)&&(e=e.substr(1));for(var t,n=String(e),o=n.length,a=-1,r="",i=n.charCodeAt(0);++a<o;){if(0===(t=n.charCodeAt(a)))throw new InvalidCharacterError("Invalid character: the input contains U+0000.");1<=t&&t<=31||127==t||0===a&&48<=t&&t<=57||1===a&&48<=t&&t<=57&&45===i?r+="\\"+t.toString(16)+" ":r+=128<=t||45===t||95===t||48<=t&&t<=57||65<=t&&t<=90||97<=t&&t<=122?n.charAt(a):"\\"+n.charAt(a)}return"#"+r},L=function(){return Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},x=function(e){return e?(t=e,parseInt(q.getComputedStyle(t).height,10)+e.offsetTop):0;var t},H=function(e,t,n,o){if(t.emitEvents&&"function"==typeof q.CustomEvent){var a=new CustomEvent(e,{bubbles:!0,detail:{anchor:n,toggle:o}});document.dispatchEvent(a)}};return function(o,e){var A,a,O,C,M={};M.cancelScroll=function(e){cancelAnimationFrame(C),C=null,e||H("scrollCancel",A)},M.animateScroll=function(i,c,e){M.cancelScroll();var s=F(A||I,e||{}),u="[object Number]"===Object.prototype.toString.call(i),t=u||!i.tagName?null:i;if(u||t){var l=q.pageYOffset;s.header&&!O&&(O=document.querySelector(s.header));var n,o,a,m,r,d,f,h,p=x(O),g=u?i:(function(e,t,n,o){var a=0;if(e.offsetParent)for(;a+=e.offsetTop,e=e.offsetParent;);return a=Math.max(a-t-n,0),o&&(a=Math.min(a,L()-q.innerHeight)),a})(t,p,parseInt("function"==typeof s.offset?s.offset(i,c):s.offset,10),s.clip),y=g-l,v=L(),w=0,S=(n=y,a=(o=s).speedAsDuration?o.speed:Math.abs(n/1e3*o.speed),o.durationMax&&a>o.durationMax?o.durationMax:o.durationMin&&a<o.durationMin?o.durationMin:parseInt(a,10)),E=function(e,t){var n,o,a,r=q.pageYOffset;if(e==t||r==t||(l<t&&q.innerHeight+r)>=v)return M.cancelScroll(!0),o=t,a=u,0===(n=i)&&document.body.focus(),a||(n.focus(),document.activeElement!==n&&(n.setAttribute("tabindex","-1"),n.focus(),n.style.outline="none"),q.scrollTo(0,o)),H("scrollStop",s,i,c),!(C=m=null)},b=function(e){var t,n,o;m||(m=e),w+=e-m,d=l+y*(n=r=1<(r=0===S?0:w/S)?1:r,"easeInQuad"===(t=s).easing&&(o=n*n),"easeOutQuad"===t.easing&&(o=n*(2-n)),"easeInOutQuad"===t.easing&&(o=n<.5?2*n*n:(4-2*n)*n-1),"easeInCubic"===t.easing&&(o=n*n*n),"easeOutCubic"===t.easing&&(o=--n*n*n+1),"easeInOutCubic"===t.easing&&(o=n<.5?4*n*n*n:(n-1)*(2*n-2)*(2*n-2)+1),"easeInQuart"===t.easing&&(o=n*n*n*n),"easeOutQuart"===t.easing&&(o=1- --n*n*n*n),"easeInOutQuart"===t.easing&&(o=n<.5?8*n*n*n*n:1-8*--n*n*n*n),"easeInQuint"===t.easing&&(o=n*n*n*n*n),"easeOutQuint"===t.easing&&(o=1+--n*n*n*n*n),"easeInOutQuint"===t.easing&&(o=n<.5?16*n*n*n*n*n:1+16*--n*n*n*n*n),t.customEasing&&(o=t.customEasing(n)),o||n),q.scrollTo(0,Math.floor(d)),E(d,g)||(C=q.requestAnimationFrame(b),m=e)};0===q.pageYOffset&&q.scrollTo(0,0),f=i,h=s,u||history.pushState&&h.updateURL&&history.pushState({smoothScroll:JSON.stringify(h),anchor:f.id},document.title,f===document.documentElement?"#top":"#"+f.id),"matchMedia"in q&&q.matchMedia("(prefers-reduced-motion)").matches?q.scrollTo(0,Math.floor(g)):(H("scrollStart",s,i,c),M.cancelScroll(!0),q.requestAnimationFrame(b))}};var t=function(e){if(!e.defaultPrevented&&!(0!==e.button||e.metaKey||e.ctrlKey||e.shiftKey)&&"closest"in e.target&&(a=e.target.closest(o))&&"a"===a.tagName.toLowerCase()&&!e.target.closest(A.ignore)&&a.hostname===q.location.hostname&&a.pathname===q.location.pathname&&/#/.test(a.href)){var t,n=r(a.hash);if("#"===n){if(!A.topOnEmptyHash)return;t=document.
  2123. /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
  2124. /***/ }),
  2125. /***/ "./node_modules/webpack/buildin/global.js":
  2126. /*!***********************************!*\
  2127. !*** (webpack)/buildin/global.js ***!
  2128. \***********************************/
  2129. /*! no static exports found */
  2130. /***/ (function(module, exports) {
  2131. var g;
  2132. // This works in non-strict mode
  2133. g = (function() {
  2134. return this;
  2135. })();
  2136. try {
  2137. // This works if eval is allowed (see CSP)
  2138. g = g || new Function("return this")();
  2139. } catch (e) {
  2140. // This works if the window reference is available
  2141. if (typeof window === "object") g = window;
  2142. }
  2143. // g can still be undefined, but nothing to do about it...
  2144. // We return undefined, instead of nothing here, so it's
  2145. // easier to handle this case. if(!global) { ...}
  2146. module.exports = g;
  2147. /***/ }),
  2148. /***/ "./src/TinyOnePage.riot":
  2149. /*!******************************!*\
  2150. !*** ./src/TinyOnePage.riot ***!
  2151. \******************************/
  2152. /*! exports provided: default */
  2153. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  2154. "use strict";
  2155. __webpack_require__.r(__webpack_exports__);
  2156. /* harmony import */ var smooth_scroll__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! smooth-scroll */ "./node_modules/smooth-scroll/dist/smooth-scroll.polyfills.min.js");
  2157. /* harmony import */ var smooth_scroll__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(smooth_scroll__WEBPACK_IMPORTED_MODULE_0__);
  2158. /* harmony default export */ __webpack_exports__["default"] = ({
  2159. 'css': null,
  2160. 'exports': {
  2161. /**
  2162. *
  2163. *
  2164. */
  2165. state:
  2166. {
  2167. options: {
  2168. offset: 0
  2169. }
  2170. },
  2171. /**
  2172. * getting innerHTML and remove
  2173. *
  2174. *
  2175. */
  2176. onBeforeMount()
  2177. {
  2178. this.content = this.root.innerHTML
  2179. this.root.innerHTML = ''
  2180. },
  2181. /**
  2182. *
  2183. *
  2184. *
  2185. */
  2186. onMounted()
  2187. {
  2188. // adding content to wrapper
  2189. const wrapper = this.$('.tiny-one-page__inner');
  2190. wrapper.innerHTML = this.content;
  2191. // adding scroll smooth, get all elements with hash
  2192. this.scroll = new smooth_scroll__WEBPACK_IMPORTED_MODULE_0___default.a('.tiny-one-page__inner a[href*="#"]', {
  2193. offset: () => {
  2194. return this.state.options.offset;
  2195. }
  2196. });
  2197. window.addEventListener('scroll', this.handleScroll.bind(this), false);
  2198. // if hash is set,
  2199. if (window.location.hash) {
  2200. this.$(".tiny-one-page__inner a[href$='" + window.location.hash + "']").click();
  2201. } else {
  2202. this.addClass(this.$$(".tiny-one-page__inner a")[0]);
  2203. }
  2204. },
  2205. /**
  2206. *
  2207. * @param {Object} element
  2208. * @return {Boolean}
  2209. */
  2210. isVisible(element, next)
  2211. {
  2212. let result = false;
  2213. console.log(window.outerHeight);
  2214. if (element.id === 'one') {
  2215. console.log('current offsetTop ' + element.offsetTop);
  2216. console.log('next offsetTop ' + next.offsetTop);
  2217. console.log('pageYOffset ' + window.pageYOffset);
  2218. }
  2219. if (((element.offsetTop <= window.pageYOffset) && (element.offsetTop + element.offsetHeight) > window.pageYOffset)) {
  2220. result = true;
  2221. }
  2222. if (result === true && next && window.pageYOffset > element.offsetTop && next.offsetTop > window.pageYOffset) {
  2223. result = false;
  2224. }
  2225. return result;
  2226. },
  2227. /**
  2228. * add css class to parent element
  2229. *
  2230. * @param {Object} element
  2231. */
  2232. addClass(element)
  2233. {
  2234. element
  2235. .parentElement
  2236. .classList
  2237. .add('current');
  2238. },
  2239. /**
  2240. * remove css class from parent element
  2241. *
  2242. * @param {Object} element
  2243. */
  2244. removeClass(element)
  2245. {
  2246. element
  2247. .parentElement
  2248. .classList
  2249. .remove('current');
  2250. },
  2251. /**
  2252. * handle scrolling,
  2253. *
  2254. * @param {Object} event
  2255. */
  2256. handleScroll(event)
  2257. {
  2258. // get elements
  2259. const elements = this.$$('.tiny-one-page__inner a');
  2260. // if found
  2261. let hasFound = false;
  2262. let result = undefined;
  2263. elements.forEach(function(element, index) {
  2264. let target = document.querySelector(element.hash);
  2265. // check if there is a next element
  2266. let next = undefined;
  2267. if (elements[index + 1]) {
  2268. next = document.querySelector(elements[index + 1].hash);
  2269. }
  2270. if (!result && ((target.offsetTop <= window.pageYOffset) && (target.offsetTop + target.offsetHeight) > window.pageYOffset)) {
  2271. result = target;
  2272. if (next && window.pageYOffset > target.offsetTop && next.offsetTop > window.pageYOffset) {
  2273. result = next;
  2274. }
  2275. }
  2276. this.removeClass(element);
  2277. }.bind(this));
  2278. this.addClass(this.$(".tiny-one-page__inner a[href$='" + result.id + "']"));
  2279. }
  2280. },
  2281. 'template': function(template, expressionTypes, bindingTypes, getComponent) {
  2282. return template(
  2283. '<div class="tiny-one-page"><div class="tiny-one-page__inner"></div></div>',
  2284. []
  2285. );
  2286. },
  2287. 'name': 'tiny-one-page'
  2288. });
  2289. /***/ }),
  2290. /***/ "./src/demo.scss":
  2291. /*!***********************!*\
  2292. !*** ./src/demo.scss ***!
  2293. \***********************/
  2294. /*! no static exports found */
  2295. /***/ (function(module, exports) {
  2296. // removed by extract-text-webpack-plugin
  2297. /***/ }),
  2298. /***/ 0:
  2299. /*!***************************************!*\
  2300. !*** multi ./demo.js ./src/demo.scss ***!
  2301. \***************************************/
  2302. /*! no static exports found */
  2303. /***/ (function(module, exports, __webpack_require__) {
  2304. __webpack_require__(/*! /home/herrhase/Workspace/tentakelfabrik/tiny-components/tiny-one-page/demo.js */"./demo.js");
  2305. module.exports = __webpack_require__(/*! /home/herrhase/Workspace/tentakelfabrik/tiny-components/tiny-one-page/src/demo.scss */"./src/demo.scss");
  2306. /***/ })
  2307. /******/ });