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.

4574 lines
136 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /******/ (() => { // webpackBootstrap
  2. /******/ var __webpack_modules__ = ({
  3. /***/ "./resources/js/components/field-error.riot":
  4. /*!**************************************************!*\
  5. !*** ./resources/js/components/field-error.riot ***!
  6. \**************************************************/
  7. /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
  8. "use strict";
  9. __webpack_require__.r(__webpack_exports__);
  10. /* harmony export */ __webpack_require__.d(__webpack_exports__, {
  11. /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
  12. /* harmony export */ });
  13. /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
  14. 'css': null,
  15. 'exports': {
  16. state: {
  17. errors: [
  18. ],
  19. // css class for
  20. closest: '.field-group',
  21. },
  22. /**
  23. *
  24. *
  25. * @param {Object} props
  26. * @param {Object} state
  27. *
  28. */
  29. onBeforeMounted(props, state)
  30. {
  31. if (props.closest) {
  32. state.closest = props.closest
  33. }
  34. },
  35. /**
  36. *
  37. *
  38. * @param {Object} props
  39. * @param {Object} state
  40. *
  41. */
  42. onMounted(props, state)
  43. {
  44. // getting parent element for entire field
  45. const parent = this.root.closest(state.closest)
  46. // getting current element by name
  47. const element = parent.querySelector('[name="' + props.name + '"]')
  48. // getting form
  49. const form = element.closest('form')
  50. // element, form are exists and nofieldupdate is not set
  51. // each change of the element dispatch a event to form validation
  52. if (element && form && !props.nofieldupdate) {
  53. element.addEventListener('input', (event) => {
  54. this.dispatchCustomEvent(event, form, props.name)
  55. })
  56. }
  57. // add custom event to listen to form-validation
  58. this.root.addEventListener('form-validation', (event) => {
  59. this.onFormValidation(event, parent)
  60. })
  61. },
  62. /**
  63. * process form validation triggered by form
  64. *
  65. * @param {Event} event
  66. * @param {Element} parent
  67. *
  68. */
  69. onFormValidation(event, parent)
  70. {
  71. // if detail is a value, set to errors
  72. if (event.detail) {
  73. this.state.errors = event.detail
  74. parent.classList.add('field--error')
  75. parent.classList.remove('field--valid')
  76. } else {
  77. this.state.errors = []
  78. parent.classList.remove('field--error')
  79. parent.classList.add('field--valid')
  80. }
  81. this.update()
  82. },
  83. /**
  84. * create event to send to form validation
  85. *
  86. * @param {Event} event
  87. * @param {Element} form
  88. * @param {string} name
  89. *
  90. */
  91. dispatchCustomEvent(event, form, name)
  92. {
  93. const fieldUpdateEvent = new CustomEvent('field-update', {
  94. 'detail': {
  95. 'name': name,
  96. 'value': event.target.value
  97. }
  98. })
  99. form.dispatchEvent(fieldUpdateEvent)
  100. }
  101. },
  102. 'template': function(
  103. template,
  104. expressionTypes,
  105. bindingTypes,
  106. getComponent
  107. ) {
  108. return template(
  109. '<div expr0="expr0" class="field-error"></div>',
  110. [
  111. {
  112. 'type': bindingTypes.IF,
  113. 'evaluate': function(
  114. _scope
  115. ) {
  116. return _scope.state.errors.length > 0;
  117. },
  118. 'redundantAttribute': 'expr0',
  119. 'selector': '[expr0]',
  120. 'template': template(
  121. '<ul><li expr1="expr1"></li></ul>',
  122. [
  123. {
  124. 'type': bindingTypes.EACH,
  125. 'getKey': null,
  126. 'condition': null,
  127. 'template': template(
  128. ' ',
  129. [
  130. {
  131. 'expressions': [
  132. {
  133. 'type': expressionTypes.TEXT,
  134. 'childNodeIndex': 0,
  135. 'evaluate': function(
  136. _scope
  137. ) {
  138. return [
  139. _scope.error
  140. ].join(
  141. ''
  142. );
  143. }
  144. }
  145. ]
  146. }
  147. ]
  148. ),
  149. 'redundantAttribute': 'expr1',
  150. 'selector': '[expr1]',
  151. 'itemName': 'error',
  152. 'indexName': null,
  153. 'evaluate': function(
  154. _scope
  155. ) {
  156. return _scope.state.errors;
  157. }
  158. }
  159. ]
  160. )
  161. }
  162. ]
  163. );
  164. },
  165. 'name': 'field-error'
  166. });
  167. /***/ }),
  168. /***/ "./node_modules/@tentakelfabrik/tiny-validator/src/FormValidator.js":
  169. /*!**************************************************************************!*\
  170. !*** ./node_modules/@tentakelfabrik/tiny-validator/src/FormValidator.js ***!
  171. \**************************************************************************/
  172. /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
  173. "use strict";
  174. __webpack_require__.r(__webpack_exports__);
  175. /* harmony export */ __webpack_require__.d(__webpack_exports__, {
  176. /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
  177. /* harmony export */ });
  178. /* harmony import */ var validate_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! validate.js */ "./node_modules/validate.js/validate.js");
  179. /* harmony import */ var validate_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(validate_js__WEBPACK_IMPORTED_MODULE_0__);
  180. /* harmony import */ var form_serialize__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! form-serialize */ "./node_modules/form-serialize/index.js");
  181. /* harmony import */ var form_serialize__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(form_serialize__WEBPACK_IMPORTED_MODULE_1__);
  182. /**
  183. * Form Validator with RiotJS Components
  184. *
  185. *
  186. *
  187. *
  188. */
  189. class FormValidator
  190. {
  191. /**
  192. *
  193. * @param {[type]} formSelector [description]
  194. * @param {[type]} constraits [description]
  195. */
  196. constructor(formSelector, constraits)
  197. {
  198. // getting selector to find form-element
  199. this.formSelector = formSelector
  200. // constraits for validate.js
  201. this.constraits = constraits
  202. // get form and elements
  203. this.form = document.querySelector(this.formSelector)
  204. this.elements = this.form.querySelectorAll('field-error')
  205. // adding submit event
  206. this.form.addEventListener('submit', (event) => {
  207. this.onSubmit(event)
  208. })
  209. // adding event if a element is updated
  210. this.form.addEventListener('field-update', (event) => {
  211. this.onFieldUpdate(event)
  212. })
  213. }
  214. /**
  215. *
  216. * @param {[type]} event [description]
  217. * @return {[type]} [description]
  218. */
  219. onSubmit(event)
  220. {
  221. let errors = validate_js__WEBPACK_IMPORTED_MODULE_0___default()(form_serialize__WEBPACK_IMPORTED_MODULE_1___default()(event.target, {
  222. hash: true
  223. }), this.constraits, {
  224. fullMessages: false
  225. })
  226. if (errors) {
  227. event.preventDefault()
  228. // send each element a event
  229. this.elements.forEach((element) => {
  230. let elementErrors = false
  231. // check for errors by name
  232. if (errors[element.attributes.name.nodeValue]) {
  233. elementErrors = errors[element.attributes.name.nodeValue]
  234. }
  235. this.dispatchCustomEvent(elementErrors, element)
  236. })
  237. }
  238. }
  239. /**
  240. *
  241. *
  242. * @param {Event} event
  243. *
  244. */
  245. onFieldUpdate(event)
  246. {
  247. // workaround, make sure that value for single is undefined if it is empty
  248. if (event.detail.value == '') {
  249. event.detail.value = undefined
  250. }
  251. let errors = validate_js__WEBPACK_IMPORTED_MODULE_0___default().single(event.detail.value, this.constraits[event.detail.name])
  252. // search for element by name and dispatch event
  253. this.elements.forEach((element) => {
  254. if (element.attributes.name.nodeValue == event.detail.name) {
  255. this.dispatchCustomEvent(errors, element)
  256. }
  257. })
  258. }
  259. /**
  260. * dispatch event to single element
  261. *
  262. * @param {Array} errors
  263. * @param {Element} element
  264. *
  265. */
  266. dispatchCustomEvent(errors, element)
  267. {
  268. let detail = false
  269. if (errors) {
  270. detail = errors
  271. }
  272. const formValidationEvent = new CustomEvent('form-validation', {
  273. 'detail': detail
  274. })
  275. element.dispatchEvent(formValidationEvent)
  276. }
  277. }
  278. /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FormValidator);
  279. /***/ }),
  280. /***/ "./node_modules/form-serialize/index.js":
  281. /*!**********************************************!*\
  282. !*** ./node_modules/form-serialize/index.js ***!
  283. \**********************************************/
  284. /***/ ((module) => {
  285. // get successful control from form and assemble into object
  286. // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
  287. // types which indicate a submit action and are not successful controls
  288. // these will be ignored
  289. var k_r_submitter = /^(?:submit|button|image|reset|file)$/i;
  290. // node names which could be successful controls
  291. var k_r_success_contrls = /^(?:input|select|textarea|keygen)/i;
  292. // Matches bracket notation.
  293. var brackets = /(\[[^\[\]]*\])/g;
  294. // serializes form fields
  295. // @param form MUST be an HTMLForm element
  296. // @param options is an optional argument to configure the serialization. Default output
  297. // with no options specified is a url encoded string
  298. // - hash: [true | false] Configure the output type. If true, the output will
  299. // be a js object.
  300. // - serializer: [function] Optional serializer function to override the default one.
  301. // The function takes 3 arguments (result, key, value) and should return new result
  302. // hash and url encoded str serializers are provided with this module
  303. // - disabled: [true | false]. If true serialize disabled fields.
  304. // - empty: [true | false]. If true serialize empty fields
  305. function serialize(form, options) {
  306. if (typeof options != 'object') {
  307. options = { hash: !!options };
  308. }
  309. else if (options.hash === undefined) {
  310. options.hash = true;
  311. }
  312. var result = (options.hash) ? {} : '';
  313. var serializer = options.serializer || ((options.hash) ? hash_serializer : str_serialize);
  314. var elements = form && form.elements ? form.elements : [];
  315. //Object store each radio and set if it's empty or not
  316. var radio_store = Object.create(null);
  317. for (var i=0 ; i<elements.length ; ++i) {
  318. var element = elements[i];
  319. // ingore disabled fields
  320. if ((!options.disabled && element.disabled) || !element.name) {
  321. continue;
  322. }
  323. // ignore anyhting that is not considered a success field
  324. if (!k_r_success_contrls.test(element.nodeName) ||
  325. k_r_submitter.test(element.type)) {
  326. continue;
  327. }
  328. var key = element.name;
  329. var val = element.value;
  330. // we can't just use element.value for checkboxes cause some browsers lie to us
  331. // they say "on" for value when the box isn't checked
  332. if ((element.type === 'checkbox' || element.type === 'radio') && !element.checked) {
  333. val = undefined;
  334. }
  335. // If we want empty elements
  336. if (options.empty) {
  337. // for checkbox
  338. if (element.type === 'checkbox' && !element.checked) {
  339. val = '';
  340. }
  341. // for radio
  342. if (element.type === 'radio') {
  343. if (!radio_store[element.name] && !element.checked) {
  344. radio_store[element.name] = false;
  345. }
  346. else if (element.checked) {
  347. radio_store[element.name] = true;
  348. }
  349. }
  350. // if options empty is true, continue only if its radio
  351. if (val == undefined && element.type == 'radio') {
  352. continue;
  353. }
  354. }
  355. else {
  356. // value-less fields are ignored unless options.empty is true
  357. if (!val) {
  358. continue;
  359. }
  360. }
  361. // multi select boxes
  362. if (element.type === 'select-multiple') {
  363. val = [];
  364. var selectOptions = element.options;
  365. var isSelectedOptions = false;
  366. for (var j=0 ; j<selectOptions.length ; ++j) {
  367. var option = selectOptions[j];
  368. var allowedEmpty = options.empty && !option.value;
  369. var hasValue = (option.value || allowedEmpty);
  370. if (option.selected && hasValue) {
  371. isSelectedOptions = true;
  372. // If using a hash serializer be sure to add the
  373. // correct notation for an array in the multi-select
  374. // context. Here the name attribute on the select element
  375. // might be missing the trailing bracket pair. Both names
  376. // "foo" and "foo[]" should be arrays.
  377. if (options.hash && key.slice(key.length - 2) !== '[]') {
  378. result = serializer(result, key + '[]', option.value);
  379. }
  380. else {
  381. result = serializer(result, key, option.value);
  382. }
  383. }
  384. }
  385. // Serialize if no selected options and options.empty is true
  386. if (!isSelectedOptions && options.empty) {
  387. result = serializer(result, key, '');
  388. }
  389. continue;
  390. }
  391. result = serializer(result, key, val);
  392. }
  393. // Check for all empty radio buttons and serialize them with key=""
  394. if (options.empty) {
  395. for (var key in radio_store) {
  396. if (!radio_store[key]) {
  397. result = serializer(result, key, '');
  398. }
  399. }
  400. }
  401. return result;
  402. }
  403. function parse_keys(string) {
  404. var keys = [];
  405. var prefix = /^([^\[\]]*)/;
  406. var children = new RegExp(brackets);
  407. var match = prefix.exec(string);
  408. if (match[1]) {
  409. keys.push(match[1]);
  410. }
  411. while ((match = children.exec(string)) !== null) {
  412. keys.push(match[1]);
  413. }
  414. return keys;
  415. }
  416. function hash_assign(result, keys, value) {
  417. if (keys.length === 0) {
  418. result = value;
  419. return result;
  420. }
  421. var key = keys.shift();
  422. var between = key.match(/^\[(.+?)\]$/);
  423. if (key === '[]') {
  424. result = result || [];
  425. if (Array.isArray(result)) {
  426. result.push(hash_assign(null, keys, value));
  427. }
  428. else {
  429. // This might be the result of bad name attributes like "[][foo]",
  430. // in this case the original `result` object will already be
  431. // assigned to an object literal. Rather than coerce the object to
  432. // an array, or cause an exception the attribute "_values" is
  433. // assigned as an array.
  434. result._values = result._values || [];
  435. result._values.push(hash_assign(null, keys, value));
  436. }
  437. return result;
  438. }
  439. // Key is an attribute name and can be assigned directly.
  440. if (!between) {
  441. result[key] = hash_assign(result[key], keys, value);
  442. }
  443. else {
  444. var string = between[1];
  445. // +var converts the variable into a number
  446. // better than parseInt because it doesn't truncate away trailing
  447. // letters and actually fails if whole thing is not a number
  448. var index = +string;
  449. // If the characters between the brackets is not a number it is an
  450. // attribute name and can be assigned directly.
  451. if (isNaN(index)) {
  452. result = result || {};
  453. result[string] = hash_assign(result[string], keys, value);
  454. }
  455. else {
  456. result = result || [];
  457. result[index] = hash_assign(result[index], keys, value);
  458. }
  459. }
  460. return result;
  461. }
  462. // Object/hash encoding serializer.
  463. function hash_serializer(result, key, value) {
  464. var matches = key.match(brackets);
  465. // Has brackets? Use the recursive assignment function to walk the keys,
  466. // construct any missing objects in the result tree and make the assignment
  467. // at the end of the chain.
  468. if (matches) {
  469. var keys = parse_keys(key);
  470. hash_assign(result, keys, value);
  471. }
  472. else {
  473. // Non bracket notation can make assignments directly.
  474. var existing = result[key];
  475. // If the value has been assigned already (for instance when a radio and
  476. // a checkbox have the same name attribute) convert the previous value
  477. // into an array before pushing into it.
  478. //
  479. // NOTE: If this requirement were removed all hash creation and
  480. // assignment could go through `hash_assign`.
  481. if (existing) {
  482. if (!Array.isArray(existing)) {
  483. result[key] = [ existing ];
  484. }
  485. result[key].push(value);
  486. }
  487. else {
  488. result[key] = value;
  489. }
  490. }
  491. return result;
  492. }
  493. // urlform encoding serializer
  494. function str_serialize(result, key, value) {
  495. // encode newlines as \r\n cause the html spec says so
  496. value = value.replace(/(\r)?\n/g, '\r\n');
  497. value = encodeURIComponent(value);
  498. // spaces should be '+' rather than '%20'.
  499. value = value.replace(/%20/g, '+');
  500. return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value;
  501. }
  502. module.exports = serialize;
  503. /***/ }),
  504. /***/ "./node_modules/riot/riot.esm.js":
  505. /*!***************************************!*\
  506. !*** ./node_modules/riot/riot.esm.js ***!
  507. \***************************************/
  508. /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
  509. "use strict";
  510. __webpack_require__.r(__webpack_exports__);
  511. /* harmony export */ __webpack_require__.d(__webpack_exports__, {
  512. /* harmony export */ "__": () => (/* binding */ __),
  513. /* harmony export */ "component": () => (/* binding */ component),
  514. /* harmony export */ "install": () => (/* binding */ install),
  515. /* harmony export */ "mount": () => (/* binding */ mount),
  516. /* harmony export */ "pure": () => (/* binding */ pure),
  517. /* harmony export */ "register": () => (/* binding */ register),
  518. /* harmony export */ "uninstall": () => (/* binding */ uninstall),
  519. /* harmony export */ "unmount": () => (/* binding */ unmount),
  520. /* harmony export */ "unregister": () => (/* binding */ unregister),
  521. /* harmony export */ "version": () => (/* binding */ version),
  522. /* harmony export */ "withTypes": () => (/* binding */ withTypes)
  523. /* harmony export */ });
  524. /* Riot v6.0.3, @license MIT */
  525. /**
  526. * Convert a string from camel case to dash-case
  527. * @param {string} string - probably a component tag name
  528. * @returns {string} component name normalized
  529. */
  530. function camelToDashCase(string) {
  531. return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  532. }
  533. /**
  534. * Convert a string containing dashes to camel case
  535. * @param {string} string - input string
  536. * @returns {string} my-string -> myString
  537. */
  538. function dashToCamelCase(string) {
  539. return string.replace(/-(\w)/g, (_, c) => c.toUpperCase());
  540. }
  541. /**
  542. * Get all the element attributes as object
  543. * @param {HTMLElement} element - DOM node we want to parse
  544. * @returns {Object} all the attributes found as a key value pairs
  545. */
  546. function DOMattributesToObject(element) {
  547. return Array.from(element.attributes).reduce((acc, attribute) => {
  548. acc[dashToCamelCase(attribute.name)] = attribute.value;
  549. return acc;
  550. }, {});
  551. }
  552. /**
  553. * Move all the child nodes from a source tag to another
  554. * @param {HTMLElement} source - source node
  555. * @param {HTMLElement} target - target node
  556. * @returns {undefined} it's a void method ¯\_()_/¯
  557. */
  558. // Ignore this helper because it's needed only for svg tags
  559. function moveChildren(source, target) {
  560. if (source.firstChild) {
  561. target.appendChild(source.firstChild);
  562. moveChildren(source, target);
  563. }
  564. }
  565. /**
  566. * Remove the child nodes from any DOM node
  567. * @param {HTMLElement} node - target node
  568. * @returns {undefined}
  569. */
  570. function cleanNode(node) {
  571. clearChildren(node.childNodes);
  572. }
  573. /**
  574. * Clear multiple children in a node
  575. * @param {HTMLElement[]} children - direct children nodes
  576. * @returns {undefined}
  577. */
  578. function clearChildren(children) {
  579. Array.from(children).forEach(removeChild);
  580. }
  581. /**
  582. * Remove a node
  583. * @param {HTMLElement}node - node to remove
  584. * @returns {undefined}
  585. */
  586. const removeChild = node => node && node.parentNode && node.parentNode.removeChild(node);
  587. /**
  588. * Insert before a node
  589. * @param {HTMLElement} newNode - node to insert
  590. * @param {HTMLElement} refNode - ref child
  591. * @returns {undefined}
  592. */
  593. const insertBefore = (newNode, refNode) => refNode && refNode.parentNode && refNode.parentNode.insertBefore(newNode, refNode);
  594. /**
  595. * Replace a node
  596. * @param {HTMLElement} newNode - new node to add to the DOM
  597. * @param {HTMLElement} replaced - node to replace
  598. * @returns {undefined}
  599. */
  600. const replaceChild = (newNode, replaced) => replaced && replaced.parentNode && replaced.parentNode.replaceChild(newNode, replaced);
  601. // Riot.js constants that can be used accross more modules
  602. const COMPONENTS_IMPLEMENTATION_MAP$1 = new Map(),
  603. DOM_COMPONENT_INSTANCE_PROPERTY$1 = Symbol('riot-component'),
  604. PLUGINS_SET$1 = new Set(),
  605. IS_DIRECTIVE = 'is',
  606. VALUE_ATTRIBUTE = 'value',
  607. MOUNT_METHOD_KEY = 'mount',
  608. UPDATE_METHOD_KEY = 'update',
  609. UNMOUNT_METHOD_KEY = 'unmount',
  610. SHOULD_UPDATE_KEY = 'shouldUpdate',
  611. ON_BEFORE_MOUNT_KEY = 'onBeforeMount',
  612. ON_MOUNTED_KEY = 'onMounted',
  613. ON_BEFORE_UPDATE_KEY = 'onBeforeUpdate',
  614. ON_UPDATED_KEY = 'onUpdated',
  615. ON_BEFORE_UNMOUNT_KEY = 'onBeforeUnmount',
  616. ON_UNMOUNTED_KEY = 'onUnmounted',
  617. PROPS_KEY = 'props',
  618. STATE_KEY = 'state',
  619. SLOTS_KEY = 'slots',
  620. ROOT_KEY = 'root',
  621. IS_PURE_SYMBOL = Symbol('pure'),
  622. IS_COMPONENT_UPDATING = Symbol('is_updating'),
  623. PARENT_KEY_SYMBOL = Symbol('parent'),
  624. ATTRIBUTES_KEY_SYMBOL = Symbol('attributes'),
  625. TEMPLATE_KEY_SYMBOL = Symbol('template');
  626. var globals = /*#__PURE__*/Object.freeze({
  627. __proto__: null,
  628. COMPONENTS_IMPLEMENTATION_MAP: COMPONENTS_IMPLEMENTATION_MAP$1,
  629. DOM_COMPONENT_INSTANCE_PROPERTY: DOM_COMPONENT_INSTANCE_PROPERTY$1,
  630. PLUGINS_SET: PLUGINS_SET$1,
  631. IS_DIRECTIVE: IS_DIRECTIVE,
  632. VALUE_ATTRIBUTE: VALUE_ATTRIBUTE,
  633. MOUNT_METHOD_KEY: MOUNT_METHOD_KEY,
  634. UPDATE_METHOD_KEY: UPDATE_METHOD_KEY,
  635. UNMOUNT_METHOD_KEY: UNMOUNT_METHOD_KEY,
  636. SHOULD_UPDATE_KEY: SHOULD_UPDATE_KEY,
  637. ON_BEFORE_MOUNT_KEY: ON_BEFORE_MOUNT_KEY,
  638. ON_MOUNTED_KEY: ON_MOUNTED_KEY,
  639. ON_BEFORE_UPDATE_KEY: ON_BEFORE_UPDATE_KEY,
  640. ON_UPDATED_KEY: ON_UPDATED_KEY,
  641. ON_BEFORE_UNMOUNT_KEY: ON_BEFORE_UNMOUNT_KEY,
  642. ON_UNMOUNTED_KEY: ON_UNMOUNTED_KEY,
  643. PROPS_KEY: PROPS_KEY,
  644. STATE_KEY: STATE_KEY,
  645. SLOTS_KEY: SLOTS_KEY,
  646. ROOT_KEY: ROOT_KEY,
  647. IS_PURE_SYMBOL: IS_PURE_SYMBOL,
  648. IS_COMPONENT_UPDATING: IS_COMPONENT_UPDATING,
  649. PARENT_KEY_SYMBOL: PARENT_KEY_SYMBOL,
  650. ATTRIBUTES_KEY_SYMBOL: ATTRIBUTES_KEY_SYMBOL,
  651. TEMPLATE_KEY_SYMBOL: TEMPLATE_KEY_SYMBOL
  652. });
  653. const EACH = 0;
  654. const IF = 1;
  655. const SIMPLE = 2;
  656. const TAG = 3;
  657. const SLOT = 4;
  658. var bindingTypes = {
  659. EACH,
  660. IF,
  661. SIMPLE,
  662. TAG,
  663. SLOT
  664. };
  665. const ATTRIBUTE = 0;
  666. const EVENT = 1;
  667. const TEXT = 2;
  668. const VALUE = 3;
  669. var expressionTypes = {
  670. ATTRIBUTE,
  671. EVENT,
  672. TEXT,
  673. VALUE
  674. };
  675. const HEAD_SYMBOL = Symbol('head');
  676. const TAIL_SYMBOL = Symbol('tail');
  677. /**
  678. * Create the <template> fragments text nodes
  679. * @return {Object} {{head: Text, tail: Text}}
  680. */
  681. function createHeadTailPlaceholders() {
  682. const head = document.createTextNode('');
  683. const tail = document.createTextNode('');
  684. head[HEAD_SYMBOL] = true;
  685. tail[TAIL_SYMBOL] = true;
  686. return {
  687. head,
  688. tail
  689. };
  690. }
  691. /**
  692. * Create the template meta object in case of <template> fragments
  693. * @param {TemplateChunk} componentTemplate - template chunk object
  694. * @returns {Object} the meta property that will be passed to the mount function of the TemplateChunk
  695. */
  696. function createTemplateMeta(componentTemplate) {
  697. const fragment = componentTemplate.dom.cloneNode(true);
  698. const {
  699. head,
  700. tail
  701. } = createHeadTailPlaceholders();
  702. return {
  703. avoidDOMInjection: true,
  704. fragment,
  705. head,
  706. tail,
  707. children: [head, ...Array.from(fragment.childNodes), tail]
  708. };
  709. }
  710. /**
  711. * Helper function to set an immutable property
  712. * @param {Object} source - object where the new property will be set
  713. * @param {string} key - object key where the new property will be stored
  714. * @param {*} value - value of the new property
  715. * @param {Object} options - set the propery overriding the default options
  716. * @returns {Object} - the original object modified
  717. */
  718. function defineProperty(source, key, value, options) {
  719. if (options === void 0) {
  720. options = {};
  721. }
  722. /* eslint-disable fp/no-mutating-methods */
  723. Object.defineProperty(source, key, Object.assign({
  724. value,
  725. enumerable: false,
  726. writable: false,
  727. configurable: true
  728. }, options));
  729. /* eslint-enable fp/no-mutating-methods */
  730. return source;
  731. }
  732. /**
  733. * Define multiple properties on a target object
  734. * @param {Object} source - object where the new properties will be set
  735. * @param {Object} properties - object containing as key pair the key + value properties
  736. * @param {Object} options - set the propery overriding the default options
  737. * @returns {Object} the original object modified
  738. */
  739. function defineProperties(source, properties, options) {
  740. Object.entries(properties).forEach(_ref => {
  741. let [key, value] = _ref;
  742. defineProperty(source, key, value, options);
  743. });
  744. return source;
  745. }
  746. /**
  747. * Define default properties if they don't exist on the source object
  748. * @param {Object} source - object that will receive the default properties
  749. * @param {Object} defaults - object containing additional optional keys
  750. * @returns {Object} the original object received enhanced
  751. */
  752. function defineDefaults(source, defaults) {
  753. Object.entries(defaults).forEach(_ref2 => {
  754. let [key, value] = _ref2;
  755. if (!source[key]) source[key] = value;
  756. });
  757. return source;
  758. }
  759. /**
  760. * Get the current <template> fragment children located in between the head and tail comments
  761. * @param {Comment} head - head comment node
  762. * @param {Comment} tail - tail comment node
  763. * @return {Array[]} children list of the nodes found in this template fragment
  764. */
  765. function getFragmentChildren(_ref) {
  766. let {
  767. head,
  768. tail
  769. } = _ref;
  770. const nodes = walkNodes([head], head.nextSibling, n => n === tail, false);
  771. nodes.push(tail);
  772. return nodes;
  773. }
  774. /**
  775. * Recursive function to walk all the <template> children nodes
  776. * @param {Array[]} children - children nodes collection
  777. * @param {ChildNode} node - current node
  778. * @param {Function} check - exit function check
  779. * @param {boolean} isFilterActive - filter flag to skip nodes managed by other bindings
  780. * @returns {Array[]} children list of the nodes found in this template fragment
  781. */
  782. function walkNodes(children, node, check, isFilterActive) {
  783. const {
  784. nextSibling
  785. } = node; // filter tail and head nodes together with all the nodes in between
  786. // this is needed only to fix a really ugly edge case https://github.com/riot/riot/issues/2892
  787. if (!isFilterActive && !node[HEAD_SYMBOL] && !node[TAIL_SYMBOL]) {
  788. children.push(node);
  789. }
  790. if (!nextSibling || check(node)) return children;
  791. return walkNodes(children, nextSibling, check, // activate the filters to skip nodes between <template> fragments that will be managed by other bindings
  792. isFilterActive && !node[TAIL_SYMBOL] || nextSibling[HEAD_SYMBOL]);
  793. }
  794. /**
  795. * Quick type checking
  796. * @param {*} element - anything
  797. * @param {string} type - type definition
  798. * @returns {boolean} true if the type corresponds
  799. */
  800. function checkType(element, type) {
  801. return typeof element === type;
  802. }
  803. /**
  804. * Check if an element is part of an svg
  805. * @param {HTMLElement} el - element to check
  806. * @returns {boolean} true if we are in an svg context
  807. */
  808. function isSvg(el) {
  809. const owner = el.ownerSVGElement;
  810. return !!owner || owner === null;
  811. }
  812. /**
  813. * Check if an element is a template tag
  814. * @param {HTMLElement} el - element to check
  815. * @returns {boolean} true if it's a <template>
  816. */
  817. function isTemplate(el) {
  818. return el.tagName.toLowerCase() === 'template';
  819. }
  820. /**
  821. * Check that will be passed if its argument is a function
  822. * @param {*} value - value to check
  823. * @returns {boolean} - true if the value is a function
  824. */
  825. function isFunction(value) {
  826. return checkType(value, 'function');
  827. }
  828. /**
  829. * Check if a value is a Boolean
  830. * @param {*} value - anything
  831. * @returns {boolean} true only for the value is a boolean
  832. */
  833. function isBoolean(value) {
  834. return checkType(value, 'boolean');
  835. }
  836. /**
  837. * Check if a value is an Object
  838. * @param {*} value - anything
  839. * @returns {boolean} true only for the value is an object
  840. */
  841. function isObject(value) {
  842. return !isNil(value) && value.constructor === Object;
  843. }
  844. /**
  845. * Check if a value is null or undefined
  846. * @param {*} value - anything
  847. * @returns {boolean} true only for the 'undefined' and 'null' types
  848. */
  849. function isNil(value) {
  850. return value === null || value === undefined;
  851. }
  852. /**
  853. * ISC License
  854. *
  855. * Copyright (c) 2020, Andrea Giammarchi, @WebReflection
  856. *
  857. * Permission to use, copy, modify, and/or distribute this software for any
  858. * purpose with or without fee is hereby granted, provided that the above
  859. * copyright notice and this permission notice appear in all copies.
  860. *
  861. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  862. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  863. * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  864. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  865. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  866. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  867. * PERFORMANCE OF THIS SOFTWARE.
  868. */
  869. // fork of https://github.com/WebReflection/udomdiff version 1.1.0
  870. // due to https://github.com/WebReflection/udomdiff/pull/2
  871. /* eslint-disable */
  872. /**
  873. * @param {Node[]} a The list of current/live children
  874. * @param {Node[]} b The list of future children
  875. * @param {(entry: Node, action: number) => Node} get
  876. * The callback invoked per each entry related DOM operation.
  877. * @param {Node} [before] The optional node used as anchor to insert before.
  878. * @returns {Node[]} The same list of future children.
  879. */
  880. var udomdiff = ((a, b, get, before) => {
  881. const bLength = b.length;
  882. let aEnd = a.length;
  883. let bEnd = bLength;
  884. let aStart = 0;
  885. let bStart = 0;
  886. let map = null;
  887. while (aStart < aEnd || bStart < bEnd) {
  888. // append head, tail, or nodes in between: fast path
  889. if (aEnd === aStart) {
  890. // we could be in a situation where the rest of nodes that
  891. // need to be added are not at the end, and in such case
  892. // the node to `insertBefore`, if the index is more than 0
  893. // must be retrieved, otherwise it's gonna be the first item.
  894. const node = bEnd < bLength ? bStart ? get(b[bStart - 1], -0).nextSibling : get(b[bEnd - bStart], 0) : before;
  895. while (bStart < bEnd) insertBefore(get(b[bStart++], 1), node);
  896. } // remove head or tail: fast path
  897. else if (bEnd === bStart) {
  898. while (aStart < aEnd) {
  899. // remove the node only if it's unknown or not live
  900. if (!map || !map.has(a[aStart])) removeChild(get(a[aStart], -1));
  901. aStart++;
  902. }
  903. } // same node: fast path
  904. else if (a[aStart] === b[bStart]) {
  905. aStart++;
  906. bStart++;
  907. } // same tail: fast path
  908. else if (a[aEnd - 1] === b[bEnd - 1]) {
  909. aEnd--;
  910. bEnd--;
  911. } // The once here single last swap "fast path" has been removed in v1.1.0
  912. // https://github.com/WebReflection/udomdiff/blob/single-final-swap/esm/index.js#L69-L85
  913. // reverse swap: also fast path
  914. else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
  915. // this is a "shrink" operation that could happen in these cases:
  916. // [1, 2, 3, 4, 5]
  917. // [1, 4, 3, 2, 5]
  918. // or asymmetric too
  919. // [1, 2, 3, 4, 5]
  920. // [1, 2, 3, 5, 6, 4]
  921. const node = get(a[--aEnd], -1).nextSibling;
  922. insertBefore(get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
  923. insertBefore(get(b[--bEnd], 1), node); // mark the future index as identical (yeah, it's dirty, but cheap 👍)
  924. // The main reason to do this, is that when a[aEnd] will be reached,
  925. // the loop will likely be on the fast path, as identical to b[bEnd].
  926. // In the best case scenario, the next loop will skip the tail,
  927. // but in the worst one, this node will be considered as already
  928. // processed, bailing out pretty quickly from the map index check
  929. a[aEnd] = b[bEnd];
  930. } // map based fallback, "slow" path
  931. else {
  932. // the map requires an O(bEnd - bStart) operation once
  933. // to store all future nodes indexes for later purposes.
  934. // In the worst case scenario, this is a full O(N) cost,
  935. // and such scenario happens at least when all nodes are different,
  936. // but also if both first and last items of the lists are different
  937. if (!map) {
  938. map = new Map();
  939. let i = bStart;
  940. while (i < bEnd) map.set(b[i], i++);
  941. } // if it's a future node, hence it needs some handling
  942. if (map.has(a[aStart])) {
  943. // grab the index of such node, 'cause it might have been processed
  944. const index = map.get(a[aStart]); // if it's not already processed, look on demand for the next LCS
  945. if (bStart < index && index < bEnd) {
  946. let i = aStart; // counts the amount of nodes that are the same in the future
  947. let sequence = 1;
  948. while (++i < aEnd && i < bEnd && map.get(a[i]) === index + sequence) sequence++; // effort decision here: if the sequence is longer than replaces
  949. // needed to reach such sequence, which would brings again this loop
  950. // to the fast path, prepend the difference before a sequence,
  951. // and move only the future list index forward, so that aStart
  952. // and bStart will be aligned again, hence on the fast path.
  953. // An example considering aStart and bStart are both 0:
  954. // a: [1, 2, 3, 4]
  955. // b: [7, 1, 2, 3, 6]
  956. // this would place 7 before 1 and, from that time on, 1, 2, and 3
  957. // will be processed at zero cost
  958. if (sequence > index - bStart) {
  959. const node = get(a[aStart], 0);
  960. while (bStart < index) insertBefore(get(b[bStart++], 1), node);
  961. } // if the effort wasn't good enough, fallback to a replace,
  962. // moving both source and target indexes forward, hoping that some
  963. // similar node will be found later on, to go back to the fast path
  964. else {
  965. replaceChild(get(b[bStart++], 1), get(a[aStart++], -1));
  966. }
  967. } // otherwise move the source forward, 'cause there's nothing to do
  968. else aStart++;
  969. } // this node has no meaning in the future list, so it's more than safe
  970. // to remove it, and check the next live node out instead, meaning
  971. // that only the live list index should be forwarded
  972. else removeChild(get(a[aStart++], -1));
  973. }
  974. }
  975. return b;
  976. });
  977. const UNMOUNT_SCOPE = Symbol('unmount');
  978. const EachBinding = {
  979. // dynamic binding properties
  980. // childrenMap: null,
  981. // node: null,
  982. // root: null,
  983. // condition: null,
  984. // evaluate: null,
  985. // template: null,
  986. // isTemplateTag: false,
  987. nodes: [],
  988. // getKey: null,
  989. // indexName: null,
  990. // itemName: null,
  991. // afterPlaceholder: null,
  992. // placeholder: null,
  993. // API methods
  994. mount(scope, parentScope) {
  995. return this.update(scope, parentScope);
  996. },
  997. update(scope, parentScope) {
  998. const {
  999. placeholder,
  1000. nodes,
  1001. childrenMap
  1002. } = this;
  1003. const collection = scope === UNMOUNT_SCOPE ? null : this.evaluate(scope);
  1004. const items = collection ? Array.from(collection) : []; // prepare the diffing
  1005. const {
  1006. newChildrenMap,
  1007. batches,
  1008. futureNodes
  1009. } = createPatch(items, scope, parentScope, this); // patch the DOM only if there are new nodes
  1010. udomdiff(nodes, futureNodes, patch(Array.from(childrenMap.values()), parentScope), placeholder); // trigger the mounts and the updates
  1011. batches.forEach(fn => fn()); // update the children map
  1012. this.childrenMap = newChildrenMap;
  1013. this.nodes = futureNodes; // make sure that the loop edge nodes are marked
  1014. markEdgeNodes(this.nodes);
  1015. return this;
  1016. },
  1017. unmount(scope, parentScope) {
  1018. this.update(UNMOUNT_SCOPE, parentScope);
  1019. return this;
  1020. }
  1021. };
  1022. /**
  1023. * Patch the DOM while diffing
  1024. * @param {any[]} redundant - list of all the children (template, nodes, context) added via each
  1025. * @param {*} parentScope - scope of the parent template
  1026. * @returns {Function} patch function used by domdiff
  1027. */
  1028. function patch(redundant, parentScope) {
  1029. return (item, info) => {
  1030. if (info < 0) {
  1031. // get the last element added to the childrenMap saved previously
  1032. const element = redundant[redundant.length - 1];
  1033. if (element) {
  1034. // get the nodes and the template in stored in the last child of the childrenMap
  1035. const {
  1036. template,
  1037. nodes,
  1038. context
  1039. } = element; // remove the last node (notice <template> tags might have more children nodes)
  1040. nodes.pop(); // notice that we pass null as last argument because
  1041. // the root node and its children will be removed by domdiff
  1042. if (!nodes.length) {
  1043. // we have cleared all the children nodes and we can unmount this template
  1044. redundant.pop();
  1045. template.unmount(context, parentScope, null);
  1046. }
  1047. }
  1048. }
  1049. return item;
  1050. };
  1051. }
  1052. /**
  1053. * Check whether a template must be filtered from a loop
  1054. * @param {Function} condition - filter function
  1055. * @param {Object} context - argument passed to the filter function
  1056. * @returns {boolean} true if this item should be skipped
  1057. */
  1058. function mustFilterItem(condition, context) {
  1059. return condition ? !condition(context) : false;
  1060. }
  1061. /**
  1062. * Extend the scope of the looped template
  1063. * @param {Object} scope - current template scope
  1064. * @param {Object} options - options
  1065. * @param {string} options.itemName - key to identify the looped item in the new context
  1066. * @param {string} options.indexName - key to identify the index of the looped item
  1067. * @param {number} options.index - current index
  1068. * @param {*} options.item - collection item looped
  1069. * @returns {Object} enhanced scope object
  1070. */
  1071. function extendScope(scope, _ref) {
  1072. let {
  1073. itemName,
  1074. indexName,
  1075. index,
  1076. item
  1077. } = _ref;
  1078. defineProperty(scope, itemName, item);
  1079. if (indexName) defineProperty(scope, indexName, index);
  1080. return scope;
  1081. }
  1082. /**
  1083. * Mark the first and last nodes in order to ignore them in case we need to retrieve the <template> fragment nodes
  1084. * @param {Array[]} nodes - each binding nodes list
  1085. * @returns {undefined} void function
  1086. */
  1087. function markEdgeNodes(nodes) {
  1088. const first = nodes[0];
  1089. const last = nodes[nodes.length - 1];
  1090. if (first) first[HEAD_SYMBOL] = true;
  1091. if (last) last[TAIL_SYMBOL] = true;
  1092. }
  1093. /**
  1094. * Loop the current template items
  1095. * @param {Array} items - expression collection value
  1096. * @param {*} scope - template scope
  1097. * @param {*} parentScope - scope of the parent template
  1098. * @param {EachBinding} binding - each binding object instance
  1099. * @returns {Object} data
  1100. * @returns {Map} data.newChildrenMap - a Map containing the new children template structure
  1101. * @returns {Array} data.batches - array containing the template lifecycle functions to trigger
  1102. * @returns {Array} data.futureNodes - array containing the nodes we need to diff
  1103. */
  1104. function createPatch(items, scope, parentScope, binding) {
  1105. const {
  1106. condition,
  1107. template,
  1108. childrenMap,
  1109. itemName,
  1110. getKey,
  1111. indexName,
  1112. root,
  1113. isTemplateTag
  1114. } = binding;
  1115. const newChildrenMap = new Map();
  1116. const batches = [];
  1117. const futureNodes = [];
  1118. items.forEach((item, index) => {
  1119. const context = extendScope(Object.create(scope), {
  1120. itemName,
  1121. indexName,
  1122. index,
  1123. item
  1124. });
  1125. const key = getKey ? getKey(context) : index;
  1126. const oldItem = childrenMap.get(key);
  1127. const nodes = [];
  1128. if (mustFilterItem(condition, context)) {
  1129. return;
  1130. }
  1131. const mustMount = !oldItem;
  1132. const componentTemplate = oldItem ? oldItem.template : template.clone();
  1133. const el = componentTemplate.el || root.cloneNode();
  1134. const meta = isTemplateTag && mustMount ? createTemplateMeta(componentTemplate) : componentTemplate.meta;
  1135. if (mustMount) {
  1136. batches.push(() => componentTemplate.mount(el, context, parentScope, meta));
  1137. } else {
  1138. batches.push(() => componentTemplate.update(context, parentScope));
  1139. } // create the collection of nodes to update or to add
  1140. // in case of template tags we need to add all its children nodes
  1141. if (isTemplateTag) {
  1142. nodes.push(...(mustMount ? meta.children : getFragmentChildren(meta)));
  1143. } else {
  1144. nodes.push(el);
  1145. } // delete the old item from the children map
  1146. childrenMap.delete(key);
  1147. futureNodes.push(...nodes); // update the children map
  1148. newChildrenMap.set(key, {
  1149. nodes,
  1150. template: componentTemplate,
  1151. context,
  1152. index
  1153. });
  1154. });
  1155. return {
  1156. newChildrenMap,
  1157. batches,
  1158. futureNodes
  1159. };
  1160. }
  1161. function create$6(node, _ref2) {
  1162. let {
  1163. evaluate,
  1164. condition,
  1165. itemName,
  1166. indexName,
  1167. getKey,
  1168. template
  1169. } = _ref2;
  1170. const placeholder = document.createTextNode('');
  1171. const root = node.cloneNode();
  1172. insertBefore(placeholder, node);
  1173. removeChild(node);
  1174. return Object.assign({}, EachBinding, {
  1175. childrenMap: new Map(),
  1176. node,
  1177. root,
  1178. condition,
  1179. evaluate,
  1180. isTemplateTag: isTemplate(root),
  1181. template: template.createDOM(node),
  1182. getKey,
  1183. indexName,
  1184. itemName,
  1185. placeholder
  1186. });
  1187. }
  1188. /**
  1189. * Binding responsible for the `if` directive
  1190. */
  1191. const IfBinding = {
  1192. // dynamic binding properties
  1193. // node: null,
  1194. // evaluate: null,
  1195. // isTemplateTag: false,
  1196. // placeholder: null,
  1197. // template: null,
  1198. // API methods
  1199. mount(scope, parentScope) {
  1200. return this.update(scope, parentScope);
  1201. },
  1202. update(scope, parentScope) {
  1203. const value = !!this.evaluate(scope);
  1204. const mustMount = !this.value && value;
  1205. const mustUnmount = this.value && !value;
  1206. const mount = () => {
  1207. const pristine = this.node.cloneNode();
  1208. insertBefore(pristine, this.placeholder);
  1209. this.template = this.template.clone();
  1210. this.template.mount(pristine, scope, parentScope);
  1211. };
  1212. switch (true) {
  1213. case mustMount:
  1214. mount();
  1215. break;
  1216. case mustUnmount:
  1217. this.unmount(scope);
  1218. break;
  1219. default:
  1220. if (value) this.template.update(scope, parentScope);
  1221. }
  1222. this.value = value;
  1223. return this;
  1224. },
  1225. unmount(scope, parentScope) {
  1226. this.template.unmount(scope, parentScope, true);
  1227. return this;
  1228. }
  1229. };
  1230. function create$5(node, _ref) {
  1231. let {
  1232. evaluate,
  1233. template
  1234. } = _ref;
  1235. const placeholder = document.createTextNode('');
  1236. insertBefore(placeholder, node);
  1237. removeChild(node);
  1238. return Object.assign({}, IfBinding, {
  1239. node,
  1240. evaluate,
  1241. placeholder,
  1242. template: template.createDOM(node)
  1243. });
  1244. }
  1245. /**
  1246. * Throw an error with a descriptive message
  1247. * @param { string } message - error message
  1248. * @returns { undefined } hoppla.. at this point the program should stop working
  1249. */
  1250. function panic(message) {
  1251. throw new Error(message);
  1252. }
  1253. /**
  1254. * Returns the memoized (cached) function.
  1255. * // borrowed from https://www.30secondsofcode.org/js/s/memoize
  1256. * @param {Function} fn - function to memoize
  1257. * @returns {Function} memoize function
  1258. */
  1259. function memoize(fn) {
  1260. const cache = new Map();
  1261. const cached = val => {
  1262. return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
  1263. };
  1264. cached.cache = cache;
  1265. return cached;
  1266. }
  1267. /**
  1268. * Evaluate a list of attribute expressions
  1269. * @param {Array} attributes - attribute expressions generated by the riot compiler
  1270. * @returns {Object} key value pairs with the result of the computation
  1271. */
  1272. function evaluateAttributeExpressions(attributes) {
  1273. return attributes.reduce((acc, attribute) => {
  1274. const {
  1275. value,
  1276. type
  1277. } = attribute;
  1278. switch (true) {
  1279. // spread attribute
  1280. case !attribute.name && type === ATTRIBUTE:
  1281. return Object.assign({}, acc, value);
  1282. // value attribute
  1283. case type === VALUE:
  1284. acc.value = attribute.value;
  1285. break;
  1286. // normal attributes
  1287. default:
  1288. acc[dashToCamelCase(attribute.name)] = attribute.value;
  1289. }
  1290. return acc;
  1291. }, {});
  1292. }
  1293. const ElementProto = typeof Element === 'undefined' ? {} : Element.prototype;
  1294. const isNativeHtmlProperty = memoize(name => ElementProto.hasOwnProperty(name)); // eslint-disable-line
  1295. /**
  1296. * Add all the attributes provided
  1297. * @param {HTMLElement} node - target node
  1298. * @param {Object} attributes - object containing the attributes names and values
  1299. * @returns {undefined} sorry it's a void function :(
  1300. */
  1301. function setAllAttributes(node, attributes) {
  1302. Object.entries(attributes).forEach(_ref => {
  1303. let [name, value] = _ref;
  1304. return attributeExpression(node, {
  1305. name
  1306. }, value);
  1307. });
  1308. }
  1309. /**
  1310. * Remove all the attributes provided
  1311. * @param {HTMLElement} node - target node
  1312. * @param {Object} newAttributes - object containing all the new attribute names
  1313. * @param {Object} oldAttributes - object containing all the old attribute names
  1314. * @returns {undefined} sorry it's a void function :(
  1315. */
  1316. function removeAllAttributes(node, newAttributes, oldAttributes) {
  1317. const newKeys = newAttributes ? Object.keys(newAttributes) : [];
  1318. Object.keys(oldAttributes).filter(name => !newKeys.includes(name)).forEach(attribute => node.removeAttribute(attribute));
  1319. }
  1320. /**
  1321. * Check whether the attribute value can be rendered
  1322. * @param {*} value - expression value
  1323. * @returns {boolean} true if we can render this attribute value
  1324. */
  1325. function canRenderAttribute(value) {
  1326. return value === true || ['string', 'number'].includes(typeof value);
  1327. }
  1328. /**
  1329. * Check whether the attribute should be removed
  1330. * @param {*} value - expression value
  1331. * @returns {boolean} boolean - true if the attribute can be removed}
  1332. */
  1333. function shouldRemoveAttribute(value) {
  1334. return !value && value !== 0;
  1335. }
  1336. /**
  1337. * This methods handles the DOM attributes updates
  1338. * @param {HTMLElement} node - target node
  1339. * @param {Object} expression - expression object
  1340. * @param {string} expression.name - attribute name
  1341. * @param {*} value - new expression value
  1342. * @param {*} oldValue - the old expression cached value
  1343. * @returns {undefined}
  1344. */
  1345. function attributeExpression(node, _ref2, value, oldValue) {
  1346. let {
  1347. name
  1348. } = _ref2;
  1349. // is it a spread operator? {...attributes}
  1350. if (!name) {
  1351. if (oldValue) {
  1352. // remove all the old attributes
  1353. removeAllAttributes(node, value, oldValue);
  1354. } // is the value still truthy?
  1355. if (value) {
  1356. setAllAttributes(node, value);
  1357. }
  1358. return;
  1359. } // handle boolean attributes
  1360. if (!isNativeHtmlProperty(name) && (isBoolean(value) || isObject(value) || isFunction(value))) {
  1361. node[name] = value;
  1362. }
  1363. if (shouldRemoveAttribute(value)) {
  1364. node.removeAttribute(name);
  1365. } else if (canRenderAttribute(value)) {
  1366. node.setAttribute(name, normalizeValue(name, value));
  1367. }
  1368. }
  1369. /**
  1370. * Get the value as string
  1371. * @param {string} name - attribute name
  1372. * @param {*} value - user input value
  1373. * @returns {string} input value as string
  1374. */
  1375. function normalizeValue(name, value) {
  1376. // be sure that expressions like selected={ true } will be always rendered as selected='selected'
  1377. return value === true ? name : value;
  1378. }
  1379. const RE_EVENTS_PREFIX = /^on/;
  1380. const getCallbackAndOptions = value => Array.isArray(value) ? value : [value, false]; // see also https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
  1381. const EventListener = {
  1382. handleEvent(event) {
  1383. this[event.type](event);
  1384. }
  1385. };
  1386. const ListenersWeakMap = new WeakMap();
  1387. const createListener = node => {
  1388. const listener = Object.create(EventListener);
  1389. ListenersWeakMap.set(node, listener);
  1390. return listener;
  1391. };
  1392. /**
  1393. * Set a new event listener
  1394. * @param {HTMLElement} node - target node
  1395. * @param {Object} expression - expression object
  1396. * @param {string} expression.name - event name
  1397. * @param {*} value - new expression value
  1398. * @returns {value} the callback just received
  1399. */
  1400. function eventExpression(node, _ref, value) {
  1401. let {
  1402. name
  1403. } = _ref;
  1404. const normalizedEventName = name.replace(RE_EVENTS_PREFIX, '');
  1405. const eventListener = ListenersWeakMap.get(node) || createListener(node);
  1406. const [callback, options] = getCallbackAndOptions(value);
  1407. const handler = eventListener[normalizedEventName];
  1408. const mustRemoveEvent = handler && !callback;
  1409. const mustAddEvent = callback && !handler;
  1410. if (mustRemoveEvent) {
  1411. node.removeEventListener(normalizedEventName, eventListener);
  1412. }
  1413. if (mustAddEvent) {
  1414. node.addEventListener(normalizedEventName, eventListener, options);
  1415. }
  1416. eventListener[normalizedEventName] = callback;
  1417. }
  1418. /**
  1419. * Normalize the user value in order to render a empty string in case of falsy values
  1420. * @param {*} value - user input value
  1421. * @returns {string} hopefully a string
  1422. */
  1423. function normalizeStringValue(value) {
  1424. return isNil(value) ? '' : value;
  1425. }
  1426. /**
  1427. * Get the the target text node to update or create one from of a comment node
  1428. * @param {HTMLElement} node - any html element containing childNodes
  1429. * @param {number} childNodeIndex - index of the text node in the childNodes list
  1430. * @returns {Text} the text node to update
  1431. */
  1432. const getTextNode = (node, childNodeIndex) => {
  1433. const target = node.childNodes[childNodeIndex];
  1434. if (target.nodeType === Node.COMMENT_NODE) {
  1435. const textNode = document.createTextNode('');
  1436. node.replaceChild(textNode, target);
  1437. return textNode;
  1438. }
  1439. return target;
  1440. };
  1441. /**
  1442. * This methods handles a simple text expression update
  1443. * @param {HTMLElement} node - target node
  1444. * @param {Object} data - expression object
  1445. * @param {*} value - new expression value
  1446. * @returns {undefined}
  1447. */
  1448. function textExpression(node, data, value) {
  1449. node.data = normalizeStringValue(value);
  1450. }
  1451. /**
  1452. * This methods handles the input fileds value updates
  1453. * @param {HTMLElement} node - target node
  1454. * @param {Object} expression - expression object
  1455. * @param {*} value - new expression value
  1456. * @returns {undefined}
  1457. */
  1458. function valueExpression(node, expression, value) {
  1459. node.value = normalizeStringValue(value);
  1460. }
  1461. var expressions = {
  1462. [ATTRIBUTE]: attributeExpression,
  1463. [EVENT]: eventExpression,
  1464. [TEXT]: textExpression,
  1465. [VALUE]: valueExpression
  1466. };
  1467. const Expression = {
  1468. // Static props
  1469. // node: null,
  1470. // value: null,
  1471. // API methods
  1472. /**
  1473. * Mount the expression evaluating its initial value
  1474. * @param {*} scope - argument passed to the expression to evaluate its current values
  1475. * @returns {Expression} self
  1476. */
  1477. mount(scope) {
  1478. // hopefully a pure function
  1479. this.value = this.evaluate(scope); // IO() DOM updates
  1480. apply(this, this.value);
  1481. return this;
  1482. },
  1483. /**
  1484. * Update the expression if its value changed
  1485. * @param {*} scope - argument passed to the expression to evaluate its current values
  1486. * @returns {Expression} self
  1487. */
  1488. update(scope) {
  1489. // pure function
  1490. const value = this.evaluate(scope);
  1491. if (this.value !== value) {
  1492. // IO() DOM updates
  1493. apply(this, value);
  1494. this.value = value;
  1495. }
  1496. return this;
  1497. },
  1498. /**
  1499. * Expression teardown method
  1500. * @returns {Expression} self
  1501. */
  1502. unmount() {
  1503. // unmount only the event handling expressions
  1504. if (this.type === EVENT) apply(this, null);
  1505. return this;
  1506. }
  1507. };
  1508. /**
  1509. * IO() function to handle the DOM updates
  1510. * @param {Expression} expression - expression object
  1511. * @param {*} value - current expression value
  1512. * @returns {undefined}
  1513. */
  1514. function apply(expression, value) {
  1515. return expressions[expression.type](expression.node, expression, value, expression.value);
  1516. }
  1517. function create$4(node, data) {
  1518. return Object.assign({}, Expression, data, {
  1519. node: data.type === TEXT ? getTextNode(node, data.childNodeIndex) : node
  1520. });
  1521. }
  1522. /**
  1523. * Create a flat object having as keys a list of methods that if dispatched will propagate
  1524. * on the whole collection
  1525. * @param {Array} collection - collection to iterate
  1526. * @param {Array<string>} methods - methods to execute on each item of the collection
  1527. * @param {*} context - context returned by the new methods created
  1528. * @returns {Object} a new object to simplify the the nested methods dispatching
  1529. */
  1530. function flattenCollectionMethods(collection, methods, context) {
  1531. return methods.reduce((acc, method) => {
  1532. return Object.assign({}, acc, {
  1533. [method]: scope => {
  1534. return collection.map(item => item[method](scope)) && context;
  1535. }
  1536. });
  1537. }, {});
  1538. }
  1539. function create$3(node, _ref) {
  1540. let {
  1541. expressions
  1542. } = _ref;
  1543. return Object.assign({}, flattenCollectionMethods(expressions.map(expression => create$4(node, expression)), ['mount', 'update', 'unmount']));
  1544. }
  1545. function extendParentScope(attributes, scope, parentScope) {
  1546. if (!attributes || !attributes.length) return parentScope;
  1547. const expressions = attributes.map(attr => Object.assign({}, attr, {
  1548. value: attr.evaluate(scope)
  1549. }));
  1550. return Object.assign(Object.create(parentScope || null), evaluateAttributeExpressions(expressions));
  1551. } // this function is only meant to fix an edge case
  1552. // https://github.com/riot/riot/issues/2842
  1553. const getRealParent = (scope, parentScope) => scope[PARENT_KEY_SYMBOL] || parentScope;
  1554. const SlotBinding = {
  1555. // dynamic binding properties
  1556. // node: null,
  1557. // name: null,
  1558. attributes: [],
  1559. // template: null,
  1560. getTemplateScope(scope, parentScope) {
  1561. return extendParentScope(this.attributes, scope, parentScope);
  1562. },
  1563. // API methods
  1564. mount(scope, parentScope) {
  1565. const templateData = scope.slots ? scope.slots.find(_ref => {
  1566. let {
  1567. id
  1568. } = _ref;
  1569. return id === this.name;
  1570. }) : false;
  1571. const {
  1572. parentNode
  1573. } = this.node;
  1574. const realParent = getRealParent(scope, parentScope);
  1575. this.template = templateData && create(templateData.html, templateData.bindings).createDOM(parentNode);
  1576. if (this.template) {
  1577. cleanNode(this.node);
  1578. this.template.mount(this.node, this.getTemplateScope(scope, realParent), realParent);
  1579. this.template.children = Array.from(this.node.childNodes);
  1580. }
  1581. moveSlotInnerContent(this.node);
  1582. removeChild(this.node);
  1583. return this;
  1584. },
  1585. update(scope, parentScope) {
  1586. if (this.template) {
  1587. const realParent = getRealParent(scope, parentScope);
  1588. this.template.update(this.getTemplateScope(scope, realParent), realParent);
  1589. }
  1590. return this;
  1591. },
  1592. unmount(scope, parentScope, mustRemoveRoot) {
  1593. if (this.template) {
  1594. this.template.unmount(this.getTemplateScope(scope, parentScope), null, mustRemoveRoot);
  1595. }
  1596. return this;
  1597. }
  1598. };
  1599. /**
  1600. * Move the inner content of the slots outside of them
  1601. * @param {HTMLElement} slot - slot node
  1602. * @returns {undefined} it's a void method ¯\_()_/¯
  1603. */
  1604. function moveSlotInnerContent(slot) {
  1605. const child = slot && slot.firstChild;
  1606. if (!child) return;
  1607. insertBefore(child, slot);
  1608. moveSlotInnerContent(slot);
  1609. }
  1610. /**
  1611. * Create a single slot binding
  1612. * @param {HTMLElement} node - slot node
  1613. * @param {string} name - slot id
  1614. * @param {AttributeExpressionData[]} attributes - slot attributes
  1615. * @returns {Object} Slot binding object
  1616. */
  1617. function createSlot(node, _ref2) {
  1618. let {
  1619. name,
  1620. attributes
  1621. } = _ref2;
  1622. return Object.assign({}, SlotBinding, {
  1623. attributes,
  1624. node,
  1625. name
  1626. });
  1627. }
  1628. /**
  1629. * Create a new tag object if it was registered before, otherwise fallback to the simple
  1630. * template chunk
  1631. * @param {Function} component - component factory function
  1632. * @param {Array<Object>} slots - array containing the slots markup
  1633. * @param {Array} attributes - dynamic attributes that will be received by the tag element
  1634. * @returns {TagImplementation|TemplateChunk} a tag implementation or a template chunk as fallback
  1635. */
  1636. function getTag(component, slots, attributes) {
  1637. if (slots === void 0) {
  1638. slots = [];
  1639. }
  1640. if (attributes === void 0) {
  1641. attributes = [];
  1642. }
  1643. // if this tag was registered before we will return its implementation
  1644. if (component) {
  1645. return component({
  1646. slots,
  1647. attributes
  1648. });
  1649. } // otherwise we return a template chunk
  1650. return create(slotsToMarkup(slots), [...slotBindings(slots), {
  1651. // the attributes should be registered as binding
  1652. // if we fallback to a normal template chunk
  1653. expressions: attributes.map(attr => {
  1654. return Object.assign({
  1655. type: ATTRIBUTE
  1656. }, attr);
  1657. })
  1658. }]);
  1659. }
  1660. /**
  1661. * Merge all the slots bindings into a single array
  1662. * @param {Array<Object>} slots - slots collection
  1663. * @returns {Array<Bindings>} flatten bindings array
  1664. */
  1665. function slotBindings(slots) {
  1666. return slots.reduce((acc, _ref) => {
  1667. let {
  1668. bindings
  1669. } = _ref;
  1670. return acc.concat(bindings);
  1671. }, []);
  1672. }
  1673. /**
  1674. * Merge all the slots together in a single markup string
  1675. * @param {Array<Object>} slots - slots collection
  1676. * @returns {string} markup of all the slots in a single string
  1677. */
  1678. function slotsToMarkup(slots) {
  1679. return slots.reduce((acc, slot) => {
  1680. return acc + slot.html;
  1681. }, '');
  1682. }
  1683. const TagBinding = {
  1684. // dynamic binding properties
  1685. // node: null,
  1686. // evaluate: null,
  1687. // name: null,
  1688. // slots: null,
  1689. // tag: null,
  1690. // attributes: null,
  1691. // getComponent: null,
  1692. mount(scope) {
  1693. return this.update(scope);
  1694. },
  1695. update(scope, parentScope) {
  1696. const name = this.evaluate(scope); // simple update
  1697. if (name && name === this.name) {
  1698. this.tag.update(scope);
  1699. } else {
  1700. // unmount the old tag if it exists
  1701. this.unmount(scope, parentScope, true); // mount the new tag
  1702. this.name = name;
  1703. this.tag = getTag(this.getComponent(name), this.slots, this.attributes);
  1704. this.tag.mount(this.node, scope);
  1705. }
  1706. return this;
  1707. },
  1708. unmount(scope, parentScope, keepRootTag) {
  1709. if (this.tag) {
  1710. // keep the root tag
  1711. this.tag.unmount(keepRootTag);
  1712. }
  1713. return this;
  1714. }
  1715. };
  1716. function create$2(node, _ref2) {
  1717. let {
  1718. evaluate,
  1719. getComponent,
  1720. slots,
  1721. attributes
  1722. } = _ref2;
  1723. return Object.assign({}, TagBinding, {
  1724. node,
  1725. evaluate,
  1726. slots,
  1727. attributes,
  1728. getComponent
  1729. });
  1730. }
  1731. var bindings = {
  1732. [IF]: create$5,
  1733. [SIMPLE]: create$3,
  1734. [EACH]: create$6,
  1735. [TAG]: create$2,
  1736. [SLOT]: createSlot
  1737. };
  1738. /**
  1739. * Text expressions in a template tag will get childNodeIndex value normalized
  1740. * depending on the position of the <template> tag offset
  1741. * @param {Expression[]} expressions - riot expressions array
  1742. * @param {number} textExpressionsOffset - offset of the <template> tag
  1743. * @returns {Expression[]} expressions containing the text expressions normalized
  1744. */
  1745. function fixTextExpressionsOffset(expressions, textExpressionsOffset) {
  1746. return expressions.map(e => e.type === TEXT ? Object.assign({}, e, {
  1747. childNodeIndex: e.childNodeIndex + textExpressionsOffset
  1748. }) : e);
  1749. }
  1750. /**
  1751. * Bind a new expression object to a DOM node
  1752. * @param {HTMLElement} root - DOM node where to bind the expression
  1753. * @param {TagBindingData} binding - binding data
  1754. * @param {number|null} templateTagOffset - if it's defined we need to fix the text expressions childNodeIndex offset
  1755. * @returns {Binding} Binding object
  1756. */
  1757. function create$1(root, binding, templateTagOffset) {
  1758. const {
  1759. selector,
  1760. type,
  1761. redundantAttribute,
  1762. expressions
  1763. } = binding; // find the node to apply the bindings
  1764. const node = selector ? root.querySelector(selector) : root; // remove eventually additional attributes created only to select this node
  1765. if (redundantAttribute) node.removeAttribute(redundantAttribute);
  1766. const bindingExpressions = expressions || []; // init the binding
  1767. return (bindings[type] || bindings[SIMPLE])(node, Object.assign({}, binding, {
  1768. expressions: templateTagOffset && !selector ? fixTextExpressionsOffset(bindingExpressions, templateTagOffset) : bindingExpressions
  1769. }));
  1770. }
  1771. function createHTMLTree(html, root) {
  1772. const template = isTemplate(root) ? root : document.createElement('template');
  1773. template.innerHTML = html;
  1774. return template.content;
  1775. } // for svg nodes we need a bit more work
  1776. function createSVGTree(html, container) {
  1777. // create the SVGNode
  1778. const svgNode = container.ownerDocument.importNode(new window.DOMParser().parseFromString(`<svg xmlns="http://www.w3.org/2000/svg">${html}</svg>`, 'application/xml').documentElement, true);
  1779. return svgNode;
  1780. }
  1781. /**
  1782. * Create the DOM that will be injected
  1783. * @param {Object} root - DOM node to find out the context where the fragment will be created
  1784. * @param {string} html - DOM to create as string
  1785. * @returns {HTMLDocumentFragment|HTMLElement} a new html fragment
  1786. */
  1787. function createDOMTree(root, html) {
  1788. if (isSvg(root)) return createSVGTree(html, root);
  1789. return createHTMLTree(html, root);
  1790. }
  1791. /**
  1792. * Inject the DOM tree into a target node
  1793. * @param {HTMLElement} el - target element
  1794. * @param {DocumentFragment|SVGElement} dom - dom tree to inject
  1795. * @returns {undefined}
  1796. */
  1797. function injectDOM(el, dom) {
  1798. switch (true) {
  1799. case isSvg(el):
  1800. moveChildren(dom, el);
  1801. break;
  1802. case isTemplate(el):
  1803. el.parentNode.replaceChild(dom, el);
  1804. break;
  1805. default:
  1806. el.appendChild(dom);
  1807. }
  1808. }
  1809. /**
  1810. * Create the Template DOM skeleton
  1811. * @param {HTMLElement} el - root node where the DOM will be injected
  1812. * @param {string|HTMLElement} html - HTML markup or HTMLElement that will be injected into the root node
  1813. * @returns {?DocumentFragment} fragment that will be injected into the root node
  1814. */
  1815. function createTemplateDOM(el, html) {
  1816. return html && (typeof html === 'string' ? createDOMTree(el, html) : html);
  1817. }
  1818. /**
  1819. * Get the offset of the <template> tag
  1820. * @param {HTMLElement} parentNode - template tag parent node
  1821. * @param {HTMLElement} el - the template tag we want to render
  1822. * @param {Object} meta - meta properties needed to handle the <template> tags in loops
  1823. * @returns {number} offset of the <template> tag calculated from its siblings DOM nodes
  1824. */
  1825. function getTemplateTagOffset(parentNode, el, meta) {
  1826. const siblings = Array.from(parentNode.childNodes);
  1827. return Math.max(siblings.indexOf(el), siblings.indexOf(meta.head) + 1, 0);
  1828. }
  1829. /**
  1830. * Template Chunk model
  1831. * @type {Object}
  1832. */
  1833. const TemplateChunk = Object.freeze({
  1834. // Static props
  1835. // bindings: null,
  1836. // bindingsData: null,
  1837. // html: null,
  1838. // isTemplateTag: false,
  1839. // fragment: null,
  1840. // children: null,
  1841. // dom: null,
  1842. // el: null,
  1843. /**
  1844. * Create the template DOM structure that will be cloned on each mount
  1845. * @param {HTMLElement} el - the root node
  1846. * @returns {TemplateChunk} self
  1847. */
  1848. createDOM(el) {
  1849. // make sure that the DOM gets created before cloning the template
  1850. this.dom = this.dom || createTemplateDOM(el, this.html) || document.createDocumentFragment();
  1851. return this;
  1852. },
  1853. // API methods
  1854. /**
  1855. * Attach the template to a DOM node
  1856. * @param {HTMLElement} el - target DOM node
  1857. * @param {*} scope - template data
  1858. * @param {*} parentScope - scope of the parent template tag
  1859. * @param {Object} meta - meta properties needed to handle the <template> tags in loops
  1860. * @returns {TemplateChunk} self
  1861. */
  1862. mount(el, scope, parentScope, meta) {
  1863. if (meta === void 0) {
  1864. meta = {};
  1865. }
  1866. if (!el) throw new Error('Please provide DOM node to mount properly your template');
  1867. if (this.el) this.unmount(scope); // <template> tags require a bit more work
  1868. // the template fragment might be already created via meta outside of this call
  1869. const {
  1870. fragment,
  1871. children,
  1872. avoidDOMInjection
  1873. } = meta; // <template> bindings of course can not have a root element
  1874. // so we check the parent node to set the query selector bindings
  1875. const {
  1876. parentNode
  1877. } = children ? children[0] : el;
  1878. const isTemplateTag = isTemplate(el);
  1879. const templateTagOffset = isTemplateTag ? getTemplateTagOffset(parentNode, el, meta) : null; // create the DOM if it wasn't created before
  1880. this.createDOM(el); // create the DOM of this template cloning the original DOM structure stored in this instance
  1881. // notice that if a documentFragment was passed (via meta) we will use it instead
  1882. const cloneNode = fragment || this.dom.cloneNode(true); // store root node
  1883. // notice that for template tags the root note will be the parent tag
  1884. this.el = isTemplateTag ? parentNode : el; // create the children array only for the <template> fragments
  1885. this.children = isTemplateTag ? children || Array.from(cloneNode.childNodes) : null; // inject the DOM into the el only if a fragment is available
  1886. if (!avoidDOMInjection && cloneNode) injectDOM(el, cloneNode); // create the bindings
  1887. this.bindings = this.bindingsData.map(binding => create$1(this.el, binding, templateTagOffset));
  1888. this.bindings.forEach(b => b.mount(scope, parentScope)); // store the template meta properties
  1889. this.meta = meta;
  1890. return this;
  1891. },
  1892. /**
  1893. * Update the template with fresh data
  1894. * @param {*} scope - template data
  1895. * @param {*} parentScope - scope of the parent template tag
  1896. * @returns {TemplateChunk} self
  1897. */
  1898. update(scope, parentScope) {
  1899. this.bindings.forEach(b => b.update(scope, parentScope));
  1900. return this;
  1901. },
  1902. /**
  1903. * Remove the template from the node where it was initially mounted
  1904. * @param {*} scope - template data
  1905. * @param {*} parentScope - scope of the parent template tag
  1906. * @param {boolean|null} mustRemoveRoot - if true remove the root element,
  1907. * if false or undefined clean the root tag content, if null don't touch the DOM
  1908. * @returns {TemplateChunk} self
  1909. */
  1910. unmount(scope, parentScope, mustRemoveRoot) {
  1911. if (mustRemoveRoot === void 0) {
  1912. mustRemoveRoot = false;
  1913. }
  1914. const el = this.el;
  1915. if (!el) {
  1916. return this;
  1917. }
  1918. this.bindings.forEach(b => b.unmount(scope, parentScope, mustRemoveRoot));
  1919. switch (true) {
  1920. // pure components should handle the DOM unmount updates by themselves
  1921. // for mustRemoveRoot === null don't touch the DOM
  1922. case el[IS_PURE_SYMBOL] || mustRemoveRoot === null:
  1923. break;
  1924. // if children are declared, clear them
  1925. // applicable for <template> and <slot/> bindings
  1926. case Array.isArray(this.children):
  1927. clearChildren(this.children);
  1928. break;
  1929. // clean the node children only
  1930. case !mustRemoveRoot:
  1931. cleanNode(el);
  1932. break;
  1933. // remove the root node only if the mustRemoveRoot is truly
  1934. case !!mustRemoveRoot:
  1935. removeChild(el);
  1936. break;
  1937. }
  1938. this.el = null;
  1939. return this;
  1940. },
  1941. /**
  1942. * Clone the template chunk
  1943. * @returns {TemplateChunk} a clone of this object resetting the this.el property
  1944. */
  1945. clone() {
  1946. return Object.assign({}, this, {
  1947. meta: {},
  1948. el: null
  1949. });
  1950. }
  1951. });
  1952. /**
  1953. * Create a template chunk wiring also the bindings
  1954. * @param {string|HTMLElement} html - template string
  1955. * @param {BindingData[]} bindings - bindings collection
  1956. * @returns {TemplateChunk} a new TemplateChunk copy
  1957. */
  1958. function create(html, bindings) {
  1959. if (bindings === void 0) {
  1960. bindings = [];
  1961. }
  1962. return Object.assign({}, TemplateChunk, {
  1963. html,
  1964. bindingsData: bindings
  1965. });
  1966. }
  1967. /**
  1968. * Method used to bind expressions to a DOM node
  1969. * @param {string|HTMLElement} html - your static template html structure
  1970. * @param {Array} bindings - list of the expressions to bind to update the markup
  1971. * @returns {TemplateChunk} a new TemplateChunk object having the `update`,`mount`, `unmount` and `clone` methods
  1972. *
  1973. * @example
  1974. *
  1975. * riotDOMBindings
  1976. * .template(
  1977. * `<div expr0><!----></div><div><p expr1><!----><section expr2></section></p>`,
  1978. * [
  1979. * {
  1980. * selector: '[expr0]',
  1981. * redundantAttribute: 'expr0',
  1982. * expressions: [
  1983. * {
  1984. * type: expressionTypes.TEXT,
  1985. * childNodeIndex: 0,
  1986. * evaluate(scope) {
  1987. * return scope.time;
  1988. * },
  1989. * },
  1990. * ],
  1991. * },
  1992. * {
  1993. * selector: '[expr1]',
  1994. * redundantAttribute: 'expr1',
  1995. * expressions: [
  1996. * {
  1997. * type: expressionTypes.TEXT,
  1998. * childNodeIndex: 0,
  1999. * evaluate(scope) {
  2000. * return scope.name;
  2001. * },
  2002. * },
  2003. * {
  2004. * type: 'attribute',
  2005. * name: 'style',
  2006. * evaluate(scope) {
  2007. * return scope.style;
  2008. * },
  2009. * },
  2010. * ],
  2011. * },
  2012. * {
  2013. * selector: '[expr2]',
  2014. * redundantAttribute: 'expr2',
  2015. * type: bindingTypes.IF,
  2016. * evaluate(scope) {
  2017. * return scope.isVisible;
  2018. * },
  2019. * template: riotDOMBindings.template('hello there'),
  2020. * },
  2021. * ]
  2022. * )
  2023. */
  2024. var DOMBindings = /*#__PURE__*/Object.freeze({
  2025. __proto__: null,
  2026. template: create,
  2027. createBinding: create$1,
  2028. createExpression: create$4,
  2029. bindingTypes: bindingTypes,
  2030. expressionTypes: expressionTypes
  2031. });
  2032. function noop() {
  2033. return this;
  2034. }
  2035. /**
  2036. * Autobind the methods of a source object to itself
  2037. * @param {Object} source - probably a riot tag instance
  2038. * @param {Array<string>} methods - list of the methods to autobind
  2039. * @returns {Object} the original object received
  2040. */
  2041. function autobindMethods(source, methods) {
  2042. methods.forEach(method => {
  2043. source[method] = source[method].bind(source);
  2044. });
  2045. return source;
  2046. }
  2047. /**
  2048. * Call the first argument received only if it's a function otherwise return it as it is
  2049. * @param {*} source - anything
  2050. * @returns {*} anything
  2051. */
  2052. function callOrAssign(source) {
  2053. return isFunction(source) ? source.prototype && source.prototype.constructor ? new source() : source() : source;
  2054. }
  2055. /**
  2056. * Converts any DOM node/s to a loopable array
  2057. * @param { HTMLElement|NodeList } els - single html element or a node list
  2058. * @returns { Array } always a loopable object
  2059. */
  2060. function domToArray(els) {
  2061. // can this object be already looped?
  2062. if (!Array.isArray(els)) {
  2063. // is it a node list?
  2064. 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
  2065. // it will be returned as "array" with one single entry
  2066. return [els];
  2067. } // this object could be looped out of the box
  2068. return els;
  2069. }
  2070. /**
  2071. * Simple helper to find DOM nodes returning them as array like loopable object
  2072. * @param { string|DOMNodeList } selector - either the query or the DOM nodes to arraify
  2073. * @param { HTMLElement } ctx - context defining where the query will search for the DOM nodes
  2074. * @returns { Array } DOM nodes found as array
  2075. */
  2076. function $(selector, ctx) {
  2077. return domToArray(typeof selector === 'string' ? (ctx || document).querySelectorAll(selector) : selector);
  2078. }
  2079. /**
  2080. * Normalize the return values, in case of a single value we avoid to return an array
  2081. * @param { Array } values - list of values we want to return
  2082. * @returns { Array|string|boolean } either the whole list of values or the single one found
  2083. * @private
  2084. */
  2085. const normalize = values => values.length === 1 ? values[0] : values;
  2086. /**
  2087. * Parse all the nodes received to get/remove/check their attributes
  2088. * @param { HTMLElement|NodeList|Array } els - DOM node/s to parse
  2089. * @param { string|Array } name - name or list of attributes
  2090. * @param { string } method - method that will be used to parse the attributes
  2091. * @returns { Array|string } result of the parsing in a list or a single value
  2092. * @private
  2093. */
  2094. function parseNodes(els, name, method) {
  2095. const names = typeof name === 'string' ? [name] : name;
  2096. return normalize(domToArray(els).map(el => {
  2097. return normalize(names.map(n => el[method](n)));
  2098. }));
  2099. }
  2100. /**
  2101. * Set any attribute on a single or a list of DOM nodes
  2102. * @param { HTMLElement|NodeList|Array } els - DOM node/s to parse
  2103. * @param { string|Object } name - either the name of the attribute to set
  2104. * or a list of properties as object key - value
  2105. * @param { string } value - the new value of the attribute (optional)
  2106. * @returns { HTMLElement|NodeList|Array } the original array of elements passed to this function
  2107. *
  2108. * @example
  2109. *
  2110. * import { set } from 'bianco.attr'
  2111. *
  2112. * const img = document.createElement('img')
  2113. *
  2114. * set(img, 'width', 100)
  2115. *
  2116. * // or also
  2117. * set(img, {
  2118. * width: 300,
  2119. * height: 300
  2120. * })
  2121. *
  2122. */
  2123. function set(els, name, value) {
  2124. const attrs = typeof name === 'object' ? name : {
  2125. [name]: value
  2126. };
  2127. const props = Object.keys(attrs);
  2128. domToArray(els).forEach(el => {
  2129. props.forEach(prop => el.setAttribute(prop, attrs[prop]));
  2130. });
  2131. return els;
  2132. }
  2133. /**
  2134. * Get any attribute from a single or a list of DOM nodes
  2135. * @param { HTMLElement|NodeList|Array } els - DOM node/s to parse
  2136. * @param { string|Array } name - name or list of attributes to get
  2137. * @returns { Array|string } list of the attributes found
  2138. *
  2139. * @example
  2140. *
  2141. * import { get } from 'bianco.attr'
  2142. *
  2143. * const img = document.createElement('img')
  2144. *
  2145. * get(img, 'width') // => '200'
  2146. *
  2147. * // or also
  2148. * get(img, ['width', 'height']) // => ['200', '300']
  2149. *
  2150. * // or also
  2151. * get([img1, img2], ['width', 'height']) // => [['200', '300'], ['500', '200']]
  2152. */
  2153. function get(els, name) {
  2154. return parseNodes(els, name, 'getAttribute');
  2155. }
  2156. const CSS_BY_NAME = new Map();
  2157. const STYLE_NODE_SELECTOR = 'style[riot]'; // memoized curried function
  2158. const getStyleNode = (style => {
  2159. return () => {
  2160. // lazy evaluation:
  2161. // if this function was already called before
  2162. // we return its cached result
  2163. if (style) return style; // create a new style element or use an existing one
  2164. // and cache it internally
  2165. style = $(STYLE_NODE_SELECTOR)[0] || document.createElement('style');
  2166. set(style, 'type', 'text/css');
  2167. /* istanbul ignore next */
  2168. if (!style.parentNode) document.head.appendChild(style);
  2169. return style;
  2170. };
  2171. })();
  2172. /**
  2173. * Object that will be used to inject and manage the css of every tag instance
  2174. */
  2175. var cssManager = {
  2176. CSS_BY_NAME,
  2177. /**
  2178. * Save a tag style to be later injected into DOM
  2179. * @param { string } name - if it's passed we will map the css to a tagname
  2180. * @param { string } css - css string
  2181. * @returns {Object} self
  2182. */
  2183. add(name, css) {
  2184. if (!CSS_BY_NAME.has(name)) {
  2185. CSS_BY_NAME.set(name, css);
  2186. this.inject();
  2187. }
  2188. return this;
  2189. },
  2190. /**
  2191. * Inject all previously saved tag styles into DOM
  2192. * innerHTML seems slow: http://jsperf.com/riot-insert-style
  2193. * @returns {Object} self
  2194. */
  2195. inject() {
  2196. getStyleNode().innerHTML = [...CSS_BY_NAME.values()].join('\n');
  2197. return this;
  2198. },
  2199. /**
  2200. * Remove a tag style from the DOM
  2201. * @param {string} name a registered tagname
  2202. * @returns {Object} self
  2203. */
  2204. remove(name) {
  2205. if (CSS_BY_NAME.has(name)) {
  2206. CSS_BY_NAME.delete(name);
  2207. this.inject();
  2208. }
  2209. return this;
  2210. }
  2211. };
  2212. /**
  2213. * Function to curry any javascript method
  2214. * @param {Function} fn - the target function we want to curry
  2215. * @param {...[args]} acc - initial arguments
  2216. * @returns {Function|*} it will return a function until the target function
  2217. * will receive all of its arguments
  2218. */
  2219. function curry(fn) {
  2220. for (var _len = arguments.length, acc = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  2221. acc[_key - 1] = arguments[_key];
  2222. }
  2223. return function () {
  2224. for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  2225. args[_key2] = arguments[_key2];
  2226. }
  2227. args = [...acc, ...args];
  2228. return args.length < fn.length ? curry(fn, ...args) : fn(...args);
  2229. };
  2230. }
  2231. /**
  2232. * Get the tag name of any DOM node
  2233. * @param {HTMLElement} element - DOM node we want to inspect
  2234. * @returns {string} name to identify this dom node in riot
  2235. */
  2236. function getName(element) {
  2237. return get(element, IS_DIRECTIVE) || element.tagName.toLowerCase();
  2238. }
  2239. const COMPONENT_CORE_HELPERS = Object.freeze({
  2240. // component helpers
  2241. $(selector) {
  2242. return $(selector, this.root)[0];
  2243. },
  2244. $$(selector) {
  2245. return $(selector, this.root);
  2246. }
  2247. });
  2248. const PURE_COMPONENT_API = Object.freeze({
  2249. [MOUNT_METHOD_KEY]: noop,
  2250. [UPDATE_METHOD_KEY]: noop,
  2251. [UNMOUNT_METHOD_KEY]: noop
  2252. });
  2253. const COMPONENT_LIFECYCLE_METHODS = Object.freeze({
  2254. [SHOULD_UPDATE_KEY]: noop,
  2255. [ON_BEFORE_MOUNT_KEY]: noop,
  2256. [ON_MOUNTED_KEY]: noop,
  2257. [ON_BEFORE_UPDATE_KEY]: noop,
  2258. [ON_UPDATED_KEY]: noop,
  2259. [ON_BEFORE_UNMOUNT_KEY]: noop,
  2260. [ON_UNMOUNTED_KEY]: noop
  2261. });
  2262. const MOCKED_TEMPLATE_INTERFACE = Object.assign({}, PURE_COMPONENT_API, {
  2263. clone: noop,
  2264. createDOM: noop
  2265. });
  2266. /**
  2267. * Performance optimization for the recursive components
  2268. * @param {RiotComponentWrapper} componentWrapper - riot compiler generated object
  2269. * @returns {Object} component like interface
  2270. */
  2271. const memoizedCreateComponent = memoize(createComponent);
  2272. /**
  2273. * Evaluate the component properties either from its real attributes or from its initial user properties
  2274. * @param {HTMLElement} element - component root
  2275. * @param {Object} initialProps - initial props
  2276. * @returns {Object} component props key value pairs
  2277. */
  2278. function evaluateInitialProps(element, initialProps) {
  2279. if (initialProps === void 0) {
  2280. initialProps = {};
  2281. }
  2282. return Object.assign({}, DOMattributesToObject(element), callOrAssign(initialProps));
  2283. }
  2284. /**
  2285. * Bind a DOM node to its component object
  2286. * @param {HTMLElement} node - html node mounted
  2287. * @param {Object} component - Riot.js component object
  2288. * @returns {Object} the component object received as second argument
  2289. */
  2290. const bindDOMNodeToComponentObject = (node, component) => node[DOM_COMPONENT_INSTANCE_PROPERTY$1] = component;
  2291. /**
  2292. * Wrap the Riot.js core API methods using a mapping function
  2293. * @param {Function} mapFunction - lifting function
  2294. * @returns {Object} an object having the { mount, update, unmount } functions
  2295. */
  2296. function createCoreAPIMethods(mapFunction) {
  2297. return [MOUNT_METHOD_KEY, UPDATE_METHOD_KEY, UNMOUNT_METHOD_KEY].reduce((acc, method) => {
  2298. acc[method] = mapFunction(method);
  2299. return acc;
  2300. }, {});
  2301. }
  2302. /**
  2303. * Factory function to create the component templates only once
  2304. * @param {Function} template - component template creation function
  2305. * @param {RiotComponentWrapper} componentWrapper - riot compiler generated object
  2306. * @returns {TemplateChunk} template chunk object
  2307. */
  2308. function componentTemplateFactory(template, componentWrapper) {
  2309. const components = createSubcomponents(componentWrapper.exports ? componentWrapper.exports.components : {});
  2310. return template(create, expressionTypes, bindingTypes, name => {
  2311. // improve support for recursive components
  2312. if (name === componentWrapper.name) return memoizedCreateComponent(componentWrapper); // return the registered components
  2313. return components[name] || COMPONENTS_IMPLEMENTATION_MAP$1.get(name);
  2314. });
  2315. }
  2316. /**
  2317. * Create a pure component
  2318. * @param {Function} pureFactoryFunction - pure component factory function
  2319. * @param {Array} options.slots - component slots
  2320. * @param {Array} options.attributes - component attributes
  2321. * @param {Array} options.template - template factory function
  2322. * @param {Array} options.template - template factory function
  2323. * @param {any} options.props - initial component properties
  2324. * @returns {Object} pure component object
  2325. */
  2326. function createPureComponent(pureFactoryFunction, _ref) {
  2327. let {
  2328. slots,
  2329. attributes,
  2330. props,
  2331. css,
  2332. template
  2333. } = _ref;
  2334. if (template) panic('Pure components can not have html');
  2335. if (css) panic('Pure components do not have css');
  2336. const component = defineDefaults(pureFactoryFunction({
  2337. slots,
  2338. attributes,
  2339. props
  2340. }), PURE_COMPONENT_API);
  2341. return createCoreAPIMethods(method => function () {
  2342. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  2343. args[_key] = arguments[_key];
  2344. }
  2345. // intercept the mount calls to bind the DOM node to the pure object created
  2346. // see also https://github.com/riot/riot/issues/2806
  2347. if (method === MOUNT_METHOD_KEY) {
  2348. const [element] = args; // mark this node as pure element
  2349. defineProperty(element, IS_PURE_SYMBOL, true);
  2350. bindDOMNodeToComponentObject(element, component);
  2351. }
  2352. component[method](...args);
  2353. return component;
  2354. });
  2355. }
  2356. /**
  2357. * Create the component interface needed for the @riotjs/dom-bindings tag bindings
  2358. * @param {RiotComponentWrapper} componentWrapper - riot compiler generated object
  2359. * @param {string} componentWrapper.css - component css
  2360. * @param {Function} componentWrapper.template - function that will return the dom-bindings template function
  2361. * @param {Object} componentWrapper.exports - component interface
  2362. * @param {string} componentWrapper.name - component name
  2363. * @returns {Object} component like interface
  2364. */
  2365. function createComponent(componentWrapper) {
  2366. const {
  2367. css,
  2368. template,
  2369. exports,
  2370. name
  2371. } = componentWrapper;
  2372. const templateFn = template ? componentTemplateFactory(template, componentWrapper) : MOCKED_TEMPLATE_INTERFACE;
  2373. return _ref2 => {
  2374. let {
  2375. slots,
  2376. attributes,
  2377. props
  2378. } = _ref2;
  2379. // pure components rendering will be managed by the end user
  2380. if (exports && exports[IS_PURE_SYMBOL]) return createPureComponent(exports, {
  2381. slots,
  2382. attributes,
  2383. props,
  2384. css,
  2385. template
  2386. });
  2387. const componentAPI = callOrAssign(exports) || {};
  2388. const component = defineComponent({
  2389. css,
  2390. template: templateFn,
  2391. componentAPI,
  2392. name
  2393. })({
  2394. slots,
  2395. attributes,
  2396. props
  2397. }); // notice that for the components create via tag binding
  2398. // we need to invert the mount (state/parentScope) arguments
  2399. // the template bindings will only forward the parentScope updates
  2400. // and never deal with the component state
  2401. return {
  2402. mount(element, parentScope, state) {
  2403. return component.mount(element, state, parentScope);
  2404. },
  2405. update(parentScope, state) {
  2406. return component.update(state, parentScope);
  2407. },
  2408. unmount(preserveRoot) {
  2409. return component.unmount(preserveRoot);
  2410. }
  2411. };
  2412. };
  2413. }
  2414. /**
  2415. * Component definition function
  2416. * @param {Object} implementation - the componen implementation will be generated via compiler
  2417. * @param {Object} component - the component initial properties
  2418. * @returns {Object} a new component implementation object
  2419. */
  2420. function defineComponent(_ref3) {
  2421. let {
  2422. css,
  2423. template,
  2424. componentAPI,
  2425. name
  2426. } = _ref3;
  2427. // add the component css into the DOM
  2428. if (css && name) cssManager.add(name, css);
  2429. return curry(enhanceComponentAPI)(defineProperties( // set the component defaults without overriding the original component API
  2430. defineDefaults(componentAPI, Object.assign({}, COMPONENT_LIFECYCLE_METHODS, {
  2431. [PROPS_KEY]: {},
  2432. [STATE_KEY]: {}
  2433. })), Object.assign({
  2434. // defined during the component creation
  2435. [SLOTS_KEY]: null,
  2436. [ROOT_KEY]: null
  2437. }, COMPONENT_CORE_HELPERS, {
  2438. name,
  2439. css,
  2440. template
  2441. })));
  2442. }
  2443. /**
  2444. * Create the bindings to update the component attributes
  2445. * @param {HTMLElement} node - node where we will bind the expressions
  2446. * @param {Array} attributes - list of attribute bindings
  2447. * @returns {TemplateChunk} - template bindings object
  2448. */
  2449. function createAttributeBindings(node, attributes) {
  2450. if (attributes === void 0) {
  2451. attributes = [];
  2452. }
  2453. const expressions = attributes.map(a => create$4(node, a));
  2454. const binding = {};
  2455. return Object.assign(binding, Object.assign({
  2456. expressions
  2457. }, createCoreAPIMethods(method => scope => {
  2458. expressions.forEach(e => e[method](scope));
  2459. return binding;
  2460. })));
  2461. }
  2462. /**
  2463. * Create the subcomponents that can be included inside a tag in runtime
  2464. * @param {Object} components - components imported in runtime
  2465. * @returns {Object} all the components transformed into Riot.Component factory functions
  2466. */
  2467. function createSubcomponents(components) {
  2468. if (components === void 0) {
  2469. components = {};
  2470. }
  2471. return Object.entries(callOrAssign(components)).reduce((acc, _ref4) => {
  2472. let [key, value] = _ref4;
  2473. acc[camelToDashCase(key)] = createComponent(value);
  2474. return acc;
  2475. }, {});
  2476. }
  2477. /**
  2478. * Run the component instance through all the plugins set by the user
  2479. * @param {Object} component - component instance
  2480. * @returns {Object} the component enhanced by the plugins
  2481. */
  2482. function runPlugins(component) {
  2483. return [...PLUGINS_SET$1].reduce((c, fn) => fn(c) || c, component);
  2484. }
  2485. /**
  2486. * Compute the component current state merging it with its previous state
  2487. * @param {Object} oldState - previous state object
  2488. * @param {Object} newState - new state givent to the `update` call
  2489. * @returns {Object} new object state
  2490. */
  2491. function computeState(oldState, newState) {
  2492. return Object.assign({}, oldState, callOrAssign(newState));
  2493. }
  2494. /**
  2495. * Add eventually the "is" attribute to link this DOM node to its css
  2496. * @param {HTMLElement} element - target root node
  2497. * @param {string} name - name of the component mounted
  2498. * @returns {undefined} it's a void function
  2499. */
  2500. function addCssHook(element, name) {
  2501. if (getName(element) !== name) {
  2502. set(element, IS_DIRECTIVE, name);
  2503. }
  2504. }
  2505. /**
  2506. * Component creation factory function that will enhance the user provided API
  2507. * @param {Object} component - a component implementation previously defined
  2508. * @param {Array} options.slots - component slots generated via riot compiler
  2509. * @param {Array} options.attributes - attribute expressions generated via riot compiler
  2510. * @returns {Riot.Component} a riot component instance
  2511. */
  2512. function enhanceComponentAPI(component, _ref5) {
  2513. let {
  2514. slots,
  2515. attributes,
  2516. props
  2517. } = _ref5;
  2518. return autobindMethods(runPlugins(defineProperties(isObject(component) ? Object.create(component) : component, {
  2519. mount(element, state, parentScope) {
  2520. if (state === void 0) {
  2521. state = {};
  2522. }
  2523. // any element mounted passing through this function can't be a pure component
  2524. defineProperty(element, IS_PURE_SYMBOL, false);
  2525. this[PARENT_KEY_SYMBOL] = parentScope;
  2526. this[ATTRIBUTES_KEY_SYMBOL] = createAttributeBindings(element, attributes).mount(parentScope);
  2527. defineProperty(this, PROPS_KEY, Object.freeze(Object.assign({}, evaluateInitialProps(element, props), evaluateAttributeExpressions(this[ATTRIBUTES_KEY_SYMBOL].expressions))));
  2528. this[STATE_KEY] = computeState(this[STATE_KEY], state);
  2529. this[TEMPLATE_KEY_SYMBOL] = this.template.createDOM(element).clone(); // link this object to the DOM node
  2530. bindDOMNodeToComponentObject(element, this); // add eventually the 'is' attribute
  2531. component.name && addCssHook(element, component.name); // define the root element
  2532. defineProperty(this, ROOT_KEY, element); // define the slots array
  2533. defineProperty(this, SLOTS_KEY, slots); // before mount lifecycle event
  2534. this[ON_BEFORE_MOUNT_KEY](this[PROPS_KEY], this[STATE_KEY]); // mount the template
  2535. this[TEMPLATE_KEY_SYMBOL].mount(element, this, parentScope);
  2536. this[ON_MOUNTED_KEY](this[PROPS_KEY], this[STATE_KEY]);
  2537. return this;
  2538. },
  2539. update(state, parentScope) {
  2540. if (state === void 0) {
  2541. state = {};
  2542. }
  2543. if (parentScope) {
  2544. this[PARENT_KEY_SYMBOL] = parentScope;
  2545. this[ATTRIBUTES_KEY_SYMBOL].update(parentScope);
  2546. }
  2547. const newProps = evaluateAttributeExpressions(this[ATTRIBUTES_KEY_SYMBOL].expressions);
  2548. if (this[SHOULD_UPDATE_KEY](newProps, this[PROPS_KEY]) === false) return;
  2549. defineProperty(this, PROPS_KEY, Object.freeze(Object.assign({}, this[PROPS_KEY], newProps)));
  2550. this[STATE_KEY] = computeState(this[STATE_KEY], state);
  2551. this[ON_BEFORE_UPDATE_KEY](this[PROPS_KEY], this[STATE_KEY]); // avoiding recursive updates
  2552. // see also https://github.com/riot/riot/issues/2895
  2553. if (!this[IS_COMPONENT_UPDATING]) {
  2554. this[IS_COMPONENT_UPDATING] = true;
  2555. this[TEMPLATE_KEY_SYMBOL].update(this, this[PARENT_KEY_SYMBOL]);
  2556. }
  2557. this[ON_UPDATED_KEY](this[PROPS_KEY], this[STATE_KEY]);
  2558. this[IS_COMPONENT_UPDATING] = false;
  2559. return this;
  2560. },
  2561. unmount(preserveRoot) {
  2562. this[ON_BEFORE_UNMOUNT_KEY](this[PROPS_KEY], this[STATE_KEY]);
  2563. this[ATTRIBUTES_KEY_SYMBOL].unmount(); // if the preserveRoot is null the template html will be left untouched
  2564. // in that case the DOM cleanup will happen differently from a parent node
  2565. this[TEMPLATE_KEY_SYMBOL].unmount(this, this[PARENT_KEY_SYMBOL], preserveRoot === null ? null : !preserveRoot);
  2566. this[ON_UNMOUNTED_KEY](this[PROPS_KEY], this[STATE_KEY]);
  2567. return this;
  2568. }
  2569. })), Object.keys(component).filter(prop => isFunction(component[prop])));
  2570. }
  2571. /**
  2572. * Component initialization function starting from a DOM node
  2573. * @param {HTMLElement} element - element to upgrade
  2574. * @param {Object} initialProps - initial component properties
  2575. * @param {string} componentName - component id
  2576. * @returns {Object} a new component instance bound to a DOM node
  2577. */
  2578. function mountComponent(element, initialProps, componentName) {
  2579. const name = componentName || getName(element);
  2580. if (!COMPONENTS_IMPLEMENTATION_MAP$1.has(name)) panic(`The component named "${name}" was never registered`);
  2581. const component = COMPONENTS_IMPLEMENTATION_MAP$1.get(name)({
  2582. props: initialProps
  2583. });
  2584. return component.mount(element);
  2585. }
  2586. /**
  2587. * Similar to compose but performs from left-to-right function composition.<br/>
  2588. * {@link https://30secondsofcode.org/function#composeright see also}
  2589. * @param {...[function]} fns) - list of unary function
  2590. * @returns {*} result of the computation
  2591. */
  2592. /**
  2593. * Performs right-to-left function composition.<br/>
  2594. * Use Array.prototype.reduce() to perform right-to-left function composition.<br/>
  2595. * The last (rightmost) function can accept one or more arguments; the remaining functions must be unary.<br/>
  2596. * {@link https://30secondsofcode.org/function#compose original source code}
  2597. * @param {...[function]} fns) - list of unary function
  2598. * @returns {*} result of the computation
  2599. */
  2600. function compose() {
  2601. for (var _len2 = arguments.length, fns = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  2602. fns[_key2] = arguments[_key2];
  2603. }
  2604. return fns.reduce((f, g) => function () {
  2605. return f(g(...arguments));
  2606. });
  2607. }
  2608. const {
  2609. DOM_COMPONENT_INSTANCE_PROPERTY,
  2610. COMPONENTS_IMPLEMENTATION_MAP,
  2611. PLUGINS_SET
  2612. } = globals;
  2613. /**
  2614. * Riot public api
  2615. */
  2616. /**
  2617. * Register a custom tag by name
  2618. * @param {string} name - component name
  2619. * @param {Object} implementation - tag implementation
  2620. * @returns {Map} map containing all the components implementations
  2621. */
  2622. function register(name, _ref) {
  2623. let {
  2624. css,
  2625. template,
  2626. exports
  2627. } = _ref;
  2628. if (COMPONENTS_IMPLEMENTATION_MAP.has(name)) panic(`The component "${name}" was already registered`);
  2629. COMPONENTS_IMPLEMENTATION_MAP.set(name, createComponent({
  2630. name,
  2631. css,
  2632. template,
  2633. exports
  2634. }));
  2635. return COMPONENTS_IMPLEMENTATION_MAP;
  2636. }
  2637. /**
  2638. * Unregister a riot web component
  2639. * @param {string} name - component name
  2640. * @returns {Map} map containing all the components implementations
  2641. */
  2642. function unregister(name) {
  2643. if (!COMPONENTS_IMPLEMENTATION_MAP.has(name)) panic(`The component "${name}" was never registered`);
  2644. COMPONENTS_IMPLEMENTATION_MAP.delete(name);
  2645. cssManager.remove(name);
  2646. return COMPONENTS_IMPLEMENTATION_MAP;
  2647. }
  2648. /**
  2649. * Mounting function that will work only for the components that were globally registered
  2650. * @param {string|HTMLElement} selector - query for the selection or a DOM element
  2651. * @param {Object} initialProps - the initial component properties
  2652. * @param {string} name - optional component name
  2653. * @returns {Array} list of riot components
  2654. */
  2655. function mount(selector, initialProps, name) {
  2656. return $(selector).map(element => mountComponent(element, initialProps, name));
  2657. }
  2658. /**
  2659. * Sweet unmounting helper function for the DOM node mounted manually by the user
  2660. * @param {string|HTMLElement} selector - query for the selection or a DOM element
  2661. * @param {boolean|null} keepRootElement - if true keep the root element
  2662. * @returns {Array} list of nodes unmounted
  2663. */
  2664. function unmount(selector, keepRootElement) {
  2665. return $(selector).map(element => {
  2666. if (element[DOM_COMPONENT_INSTANCE_PROPERTY]) {
  2667. element[DOM_COMPONENT_INSTANCE_PROPERTY].unmount(keepRootElement);
  2668. }
  2669. return element;
  2670. });
  2671. }
  2672. /**
  2673. * Define a riot plugin
  2674. * @param {Function} plugin - function that will receive all the components created
  2675. * @returns {Set} the set containing all the plugins installed
  2676. */
  2677. function install(plugin) {
  2678. if (!isFunction(plugin)) panic('Plugins must be of type function');
  2679. if (PLUGINS_SET.has(plugin)) panic('This plugin was already installed');
  2680. PLUGINS_SET.add(plugin);
  2681. return PLUGINS_SET;
  2682. }
  2683. /**
  2684. * Uninstall a riot plugin
  2685. * @param {Function} plugin - plugin previously installed
  2686. * @returns {Set} the set containing all the plugins installed
  2687. */
  2688. function uninstall(plugin) {
  2689. if (!PLUGINS_SET.has(plugin)) panic('This plugin was never installed');
  2690. PLUGINS_SET.delete(plugin);
  2691. return PLUGINS_SET;
  2692. }
  2693. /**
  2694. * Helper method to create component without relying on the registered ones
  2695. * @param {Object} implementation - component implementation
  2696. * @returns {Function} function that will allow you to mount a riot component on a DOM node
  2697. */
  2698. function component(implementation) {
  2699. return function (el, props, _temp) {
  2700. let {
  2701. slots,
  2702. attributes,
  2703. parentScope
  2704. } = _temp === void 0 ? {} : _temp;
  2705. return compose(c => c.mount(el, parentScope), c => c({
  2706. props,
  2707. slots,
  2708. attributes
  2709. }), createComponent)(implementation);
  2710. };
  2711. }
  2712. /**
  2713. * Lift a riot component Interface into a pure riot object
  2714. * @param {Function} func - RiotPureComponent factory function
  2715. * @returns {Function} the lifted original function received as argument
  2716. */
  2717. function pure(func) {
  2718. if (!isFunction(func)) panic('riot.pure accepts only arguments of type "function"');
  2719. func[IS_PURE_SYMBOL] = true;
  2720. return func;
  2721. }
  2722. /**
  2723. * no-op function needed to add the proper types to your component via typescript
  2724. * @param {Function|Object} component - component default export
  2725. * @returns {Function|Object} returns exactly what it has received
  2726. */
  2727. const withTypes = component => component;
  2728. /** @type {string} current riot version */
  2729. const version = 'v6.0.3'; // expose some internal stuff that might be used from external tools
  2730. const __ = {
  2731. cssManager,
  2732. DOMBindings,
  2733. createComponent,
  2734. defineComponent,
  2735. globals
  2736. };
  2737. /***/ }),
  2738. /***/ "./node_modules/validate.js/validate.js":
  2739. /*!**********************************************!*\
  2740. !*** ./node_modules/validate.js/validate.js ***!
  2741. \**********************************************/
  2742. /***/ (function(module, exports, __webpack_require__) {
  2743. /* module decorator */ module = __webpack_require__.nmd(module);
  2744. /*!
  2745. * validate.js 0.13.1
  2746. *
  2747. * (c) 2013-2019 Nicklas Ansman, 2013 Wrapp
  2748. * Validate.js may be freely distributed under the MIT license.
  2749. * For all details and documentation:
  2750. * http://validatejs.org/
  2751. */
  2752. (function(exports, module, define) {
  2753. "use strict";
  2754. // The main function that calls the validators specified by the constraints.
  2755. // The options are the following:
  2756. // - format (string) - An option that controls how the returned value is formatted
  2757. // * flat - Returns a flat array of just the error messages
  2758. // * grouped - Returns the messages grouped by attribute (default)
  2759. // * detailed - Returns an array of the raw validation data
  2760. // - fullMessages (boolean) - If `true` (default) the attribute name is prepended to the error.
  2761. //
  2762. // Please note that the options are also passed to each validator.
  2763. var validate = function(attributes, constraints, options) {
  2764. options = v.extend({}, v.options, options);
  2765. var results = v.runValidations(attributes, constraints, options)
  2766. , attr
  2767. , validator;
  2768. if (results.some(function(r) { return v.isPromise(r.error); })) {
  2769. throw new Error("Use validate.async if you want support for promises");
  2770. }
  2771. return validate.processValidationResults(results, options);
  2772. };
  2773. var v = validate;
  2774. // Copies over attributes from one or more sources to a single destination.
  2775. // Very much similar to underscore's extend.
  2776. // The first argument is the target object and the remaining arguments will be
  2777. // used as sources.
  2778. v.extend = function(obj) {
  2779. [].slice.call(arguments, 1).forEach(function(source) {
  2780. for (var attr in source) {
  2781. obj[attr] = source[attr];
  2782. }
  2783. });
  2784. return obj;
  2785. };
  2786. v.extend(validate, {
  2787. // This is the version of the library as a semver.
  2788. // The toString function will allow it to be coerced into a string
  2789. version: {
  2790. major: 0,
  2791. minor: 13,
  2792. patch: 1,
  2793. metadata: null,
  2794. toString: function() {
  2795. var version = v.format("%{major}.%{minor}.%{patch}", v.version);
  2796. if (!v.isEmpty(v.version.metadata)) {
  2797. version += "+" + v.version.metadata;
  2798. }
  2799. return version;
  2800. }
  2801. },
  2802. // Below is the dependencies that are used in validate.js
  2803. // The constructor of the Promise implementation.
  2804. // If you are using Q.js, RSVP or any other A+ compatible implementation
  2805. // override this attribute to be the constructor of that promise.
  2806. // Since jQuery promises aren't A+ compatible they won't work.
  2807. Promise: typeof Promise !== "undefined" ? Promise : /* istanbul ignore next */ null,
  2808. EMPTY_STRING_REGEXP: /^\s*$/,
  2809. // Runs the validators specified by the constraints object.
  2810. // Will return an array of the format:
  2811. // [{attribute: "<attribute name>", error: "<validation result>"}, ...]
  2812. runValidations: function(attributes, constraints, options) {
  2813. var results = []
  2814. , attr
  2815. , validatorName
  2816. , value
  2817. , validators
  2818. , validator
  2819. , validatorOptions
  2820. , error;
  2821. if (v.isDomElement(attributes) || v.isJqueryElement(attributes)) {
  2822. attributes = v.collectFormValues(attributes);
  2823. }
  2824. // Loops through each constraints, finds the correct validator and run it.
  2825. for (attr in constraints) {
  2826. value = v.getDeepObjectValue(attributes, attr);
  2827. // This allows the constraints for an attribute to be a function.
  2828. // The function will be called with the value, attribute name, the complete dict of
  2829. // attributes as well as the options and constraints passed in.
  2830. // This is useful when you want to have different
  2831. // validations depending on the attribute value.
  2832. validators = v.result(constraints[attr], value, attributes, attr, options, constraints);
  2833. for (validatorName in validators) {
  2834. validator = v.validators[validatorName];
  2835. if (!validator) {
  2836. error = v.format("Unknown validator %{name}", {name: validatorName});
  2837. throw new Error(error);
  2838. }
  2839. validatorOptions = validators[validatorName];
  2840. // This allows the options to be a function. The function will be
  2841. // called with the value, attribute name, the complete dict of
  2842. // attributes as well as the options and constraints passed in.
  2843. // This is useful when you want to have different
  2844. // validations depending on the attribute value.
  2845. validatorOptions = v.result(validatorOptions, value, attributes, attr, options, constraints);
  2846. if (!validatorOptions) {
  2847. continue;
  2848. }
  2849. results.push({
  2850. attribute: attr,
  2851. value: value,
  2852. validator: validatorName,
  2853. globalOptions: options,
  2854. attributes: attributes,
  2855. options: validatorOptions,
  2856. error: validator.call(validator,
  2857. value,
  2858. validatorOptions,
  2859. attr,
  2860. attributes,
  2861. options)
  2862. });
  2863. }
  2864. }
  2865. return results;
  2866. },
  2867. // Takes the output from runValidations and converts it to the correct
  2868. // output format.
  2869. processValidationResults: function(errors, options) {
  2870. errors = v.pruneEmptyErrors(errors, options);
  2871. errors = v.expandMultipleErrors(errors, options);
  2872. errors = v.convertErrorMessages(errors, options);
  2873. var format = options.format || "grouped";
  2874. if (typeof v.formatters[format] === 'function') {
  2875. errors = v.formatters[format](errors);
  2876. } else {
  2877. throw new Error(v.format("Unknown format %{format}", options));
  2878. }
  2879. return v.isEmpty(errors) ? undefined : errors;
  2880. },
  2881. // Runs the validations with support for promises.
  2882. // This function will return a promise that is settled when all the
  2883. // validation promises have been completed.
  2884. // It can be called even if no validations returned a promise.
  2885. async: function(attributes, constraints, options) {
  2886. options = v.extend({}, v.async.options, options);
  2887. var WrapErrors = options.wrapErrors || function(errors) {
  2888. return errors;
  2889. };
  2890. // Removes unknown attributes
  2891. if (options.cleanAttributes !== false) {
  2892. attributes = v.cleanAttributes(attributes, constraints);
  2893. }
  2894. var results = v.runValidations(attributes, constraints, options);
  2895. return new v.Promise(function(resolve, reject) {
  2896. v.waitForResults(results).then(function() {
  2897. var errors = v.processValidationResults(results, options);
  2898. if (errors) {
  2899. reject(new WrapErrors(errors, options, attributes, constraints));
  2900. } else {
  2901. resolve(attributes);
  2902. }
  2903. }, function(err) {
  2904. reject(err);
  2905. });
  2906. });
  2907. },
  2908. single: function(value, constraints, options) {
  2909. options = v.extend({}, v.single.options, options, {
  2910. format: "flat",
  2911. fullMessages: false
  2912. });
  2913. return v({single: value}, {single: constraints}, options);
  2914. },
  2915. // Returns a promise that is resolved when all promises in the results array
  2916. // are settled. The promise returned from this function is always resolved,
  2917. // never rejected.
  2918. // This function modifies the input argument, it replaces the promises
  2919. // with the value returned from the promise.
  2920. waitForResults: function(results) {
  2921. // Create a sequence of all the results starting with a resolved promise.
  2922. return results.reduce(function(memo, result) {
  2923. // If this result isn't a promise skip it in the sequence.
  2924. if (!v.isPromise(result.error)) {
  2925. return memo;
  2926. }
  2927. return memo.then(function() {
  2928. return result.error.then(function(error) {
  2929. result.error = error || null;
  2930. });
  2931. });
  2932. }, new v.Promise(function(r) { r(); })); // A resolved promise
  2933. },
  2934. // If the given argument is a call: function the and: function return the value
  2935. // otherwise just return the value. Additional arguments will be passed as
  2936. // arguments to the function.
  2937. // Example:
  2938. // ```
  2939. // result('foo') // 'foo'
  2940. // result(Math.max, 1, 2) // 2
  2941. // ```
  2942. result: function(value) {
  2943. var args = [].slice.call(arguments, 1);
  2944. if (typeof value === 'function') {
  2945. value = value.apply(null, args);
  2946. }
  2947. return value;
  2948. },
  2949. // Checks if the value is a number. This function does not consider NaN a
  2950. // number like many other `isNumber` functions do.
  2951. isNumber: function(value) {
  2952. return typeof value === 'number' && !isNaN(value);
  2953. },
  2954. // Returns false if the object is not a function
  2955. isFunction: function(value) {
  2956. return typeof value === 'function';
  2957. },
  2958. // A simple check to verify that the value is an integer. Uses `isNumber`
  2959. // and a simple modulo check.
  2960. isInteger: function(value) {
  2961. return v.isNumber(value) && value % 1 === 0;
  2962. },
  2963. // Checks if the value is a boolean
  2964. isBoolean: function(value) {
  2965. return typeof value === 'boolean';
  2966. },
  2967. // Uses the `Object` function to check if the given argument is an object.
  2968. isObject: function(obj) {
  2969. return obj === Object(obj);
  2970. },
  2971. // Simply checks if the object is an instance of a date
  2972. isDate: function(obj) {
  2973. return obj instanceof Date;
  2974. },
  2975. // Returns false if the object is `null` of `undefined`
  2976. isDefined: function(obj) {
  2977. return obj !== null && obj !== undefined;
  2978. },
  2979. // Checks if the given argument is a promise. Anything with a `then`
  2980. // function is considered a promise.
  2981. isPromise: function(p) {
  2982. return !!p && v.isFunction(p.then);
  2983. },
  2984. isJqueryElement: function(o) {
  2985. return o && v.isString(o.jquery);
  2986. },
  2987. isDomElement: function(o) {
  2988. if (!o) {
  2989. return false;
  2990. }
  2991. if (!o.querySelectorAll || !o.querySelector) {
  2992. return false;
  2993. }
  2994. if (v.isObject(document) && o === document) {
  2995. return true;
  2996. }
  2997. // http://stackoverflow.com/a/384380/699304
  2998. /* istanbul ignore else */
  2999. if (typeof HTMLElement === "object") {
  3000. return o instanceof HTMLElement;
  3001. } else {
  3002. return o &&
  3003. typeof o === "object" &&
  3004. o !== null &&
  3005. o.nodeType === 1 &&
  3006. typeof o.nodeName === "string";
  3007. }
  3008. },
  3009. isEmpty: function(value) {
  3010. var attr;
  3011. // Null and undefined are empty
  3012. if (!v.isDefined(value)) {
  3013. return true;
  3014. }
  3015. // functions are non empty
  3016. if (v.isFunction(value)) {
  3017. return false;
  3018. }
  3019. // Whitespace only strings are empty
  3020. if (v.isString(value)) {
  3021. return v.EMPTY_STRING_REGEXP.test(value);
  3022. }
  3023. // For arrays we use the length property
  3024. if (v.isArray(value)) {
  3025. return value.length === 0;
  3026. }
  3027. // Dates have no attributes but aren't empty
  3028. if (v.isDate(value)) {
  3029. return false;
  3030. }
  3031. // If we find at least one property we consider it non empty
  3032. if (v.isObject(value)) {
  3033. for (attr in value) {
  3034. return false;
  3035. }
  3036. return true;
  3037. }
  3038. return false;
  3039. },
  3040. // Formats the specified strings with the given values like so:
  3041. // ```
  3042. // format("Foo: %{foo}", {foo: "bar"}) // "Foo bar"
  3043. // ```
  3044. // If you want to write %{...} without having it replaced simply
  3045. // prefix it with % like this `Foo: %%{foo}` and it will be returned
  3046. // as `"Foo: %{foo}"`
  3047. format: v.extend(function(str, vals) {
  3048. if (!v.isString(str)) {
  3049. return str;
  3050. }
  3051. return str.replace(v.format.FORMAT_REGEXP, function(m0, m1, m2) {
  3052. if (m1 === '%') {
  3053. return "%{" + m2 + "}";
  3054. } else {
  3055. return String(vals[m2]);
  3056. }
  3057. });
  3058. }, {
  3059. // Finds %{key} style patterns in the given string
  3060. FORMAT_REGEXP: /(%?)%\{([^\}]+)\}/g
  3061. }),
  3062. // "Prettifies" the given string.
  3063. // Prettifying means replacing [.\_-] with spaces as well as splitting
  3064. // camel case words.
  3065. prettify: function(str) {
  3066. if (v.isNumber(str)) {
  3067. // If there are more than 2 decimals round it to two
  3068. if ((str * 100) % 1 === 0) {
  3069. return "" + str;
  3070. } else {
  3071. return parseFloat(Math.round(str * 100) / 100).toFixed(2);
  3072. }
  3073. }
  3074. if (v.isArray(str)) {
  3075. return str.map(function(s) { return v.prettify(s); }).join(", ");
  3076. }
  3077. if (v.isObject(str)) {
  3078. if (!v.isDefined(str.toString)) {
  3079. return JSON.stringify(str);
  3080. }
  3081. return str.toString();
  3082. }
  3083. // Ensure the string is actually a string
  3084. str = "" + str;
  3085. return str
  3086. // Splits keys separated by periods
  3087. .replace(/([^\s])\.([^\s])/g, '$1 $2')
  3088. // Removes backslashes
  3089. .replace(/\\+/g, '')
  3090. // Replaces - and - with space
  3091. .replace(/[_-]/g, ' ')
  3092. // Splits camel cased words
  3093. .replace(/([a-z])([A-Z])/g, function(m0, m1, m2) {
  3094. return "" + m1 + " " + m2.toLowerCase();
  3095. })
  3096. .toLowerCase();
  3097. },
  3098. stringifyValue: function(value, options) {
  3099. var prettify = options && options.prettify || v.prettify;
  3100. return prettify(value);
  3101. },
  3102. isString: function(value) {
  3103. return typeof value === 'string';
  3104. },
  3105. isArray: function(value) {
  3106. return {}.toString.call(value) === '[object Array]';
  3107. },
  3108. // Checks if the object is a hash, which is equivalent to an object that
  3109. // is neither an array nor a function.
  3110. isHash: function(value) {
  3111. return v.isObject(value) && !v.isArray(value) && !v.isFunction(value);
  3112. },
  3113. contains: function(obj, value) {
  3114. if (!v.isDefined(obj)) {
  3115. return false;
  3116. }
  3117. if (v.isArray(obj)) {
  3118. return obj.indexOf(value) !== -1;
  3119. }
  3120. return value in obj;
  3121. },
  3122. unique: function(array) {
  3123. if (!v.isArray(array)) {
  3124. return array;
  3125. }
  3126. return array.filter(function(el, index, array) {
  3127. return array.indexOf(el) == index;
  3128. });
  3129. },
  3130. forEachKeyInKeypath: function(object, keypath, callback) {
  3131. if (!v.isString(keypath)) {
  3132. return undefined;
  3133. }
  3134. var key = ""
  3135. , i
  3136. , escape = false;
  3137. for (i = 0; i < keypath.length; ++i) {
  3138. switch (keypath[i]) {
  3139. case '.':
  3140. if (escape) {
  3141. escape = false;
  3142. key += '.';
  3143. } else {
  3144. object = callback(object, key, false);
  3145. key = "";
  3146. }
  3147. break;
  3148. case '\\':
  3149. if (escape) {
  3150. escape = false;
  3151. key += '\\';
  3152. } else {
  3153. escape = true;
  3154. }
  3155. break;
  3156. default:
  3157. escape = false;
  3158. key += keypath[i];
  3159. break;
  3160. }
  3161. }
  3162. return callback(object, key, true);
  3163. },
  3164. getDeepObjectValue: function(obj, keypath) {
  3165. if (!v.isObject(obj)) {
  3166. return undefined;
  3167. }
  3168. return v.forEachKeyInKeypath(obj, keypath, function(obj, key) {
  3169. if (v.isObject(obj)) {
  3170. return obj[key];
  3171. }
  3172. });
  3173. },
  3174. // This returns an object with all the values of the form.
  3175. // It uses the input name as key and the value as value
  3176. // So for example this:
  3177. // <input type="text" name="email" value="foo@bar.com" />
  3178. // would return:
  3179. // {email: "foo@bar.com"}
  3180. collectFormValues: function(form, options) {
  3181. var values = {}
  3182. , i
  3183. , j
  3184. , input
  3185. , inputs
  3186. , option
  3187. , value;
  3188. if (v.isJqueryElement(form)) {
  3189. form = form[0];
  3190. }
  3191. if (!form) {
  3192. return values;
  3193. }
  3194. options = options || {};
  3195. inputs = form.querySelectorAll("input[name], textarea[name]");
  3196. for (i = 0; i < inputs.length; ++i) {
  3197. input = inputs.item(i);
  3198. if (v.isDefined(input.getAttribute("data-ignored"))) {
  3199. continue;
  3200. }
  3201. var name = input.name.replace(/\./g, "\\\\.");
  3202. value = v.sanitizeFormValue(input.value, options);
  3203. if (input.type === "number") {
  3204. value = value ? +value : null;
  3205. } else if (input.type === "checkbox") {
  3206. if (input.attributes.value) {
  3207. if (!input.checked) {
  3208. value = values[name] || null;
  3209. }
  3210. } else {
  3211. value = input.checked;
  3212. }
  3213. } else if (input.type === "radio") {
  3214. if (!input.checked) {
  3215. value = values[name] || null;
  3216. }
  3217. }
  3218. values[name] = value;
  3219. }
  3220. inputs = form.querySelectorAll("select[name]");
  3221. for (i = 0; i < inputs.length; ++i) {
  3222. input = inputs.item(i);
  3223. if (v.isDefined(input.getAttribute("data-ignored"))) {
  3224. continue;
  3225. }
  3226. if (input.multiple) {
  3227. value = [];
  3228. for (j in input.options) {
  3229. option = input.options[j];
  3230. if (option && option.selected) {
  3231. value.push(v.sanitizeFormValue(option.value, options));
  3232. }
  3233. }
  3234. } else {
  3235. var _val = typeof input.options[input.selectedIndex] !== 'undefined' ? input.options[input.selectedIndex].value : /* istanbul ignore next */ '';
  3236. value = v.sanitizeFormValue(_val, options);
  3237. }
  3238. values[input.name] = value;
  3239. }
  3240. return values;
  3241. },
  3242. sanitizeFormValue: function(value, options) {
  3243. if (options.trim && v.isString(value)) {
  3244. value = value.trim();
  3245. }
  3246. if (options.nullify !== false && value === "") {
  3247. return null;
  3248. }
  3249. return value;
  3250. },
  3251. capitalize: function(str) {
  3252. if (!v.isString(str)) {
  3253. return str;
  3254. }
  3255. return str[0].toUpperCase() + str.slice(1);
  3256. },
  3257. // Remove all errors who's error attribute is empty (null or undefined)
  3258. pruneEmptyErrors: function(errors) {
  3259. return errors.filter(function(error) {
  3260. return !v.isEmpty(error.error);
  3261. });
  3262. },
  3263. // In
  3264. // [{error: ["err1", "err2"], ...}]
  3265. // Out
  3266. // [{error: "err1", ...}, {error: "err2", ...}]
  3267. //
  3268. // All attributes in an error with multiple messages are duplicated
  3269. // when expanding the errors.
  3270. expandMultipleErrors: function(errors) {
  3271. var ret = [];
  3272. errors.forEach(function(error) {
  3273. // Removes errors without a message
  3274. if (v.isArray(error.error)) {
  3275. error.error.forEach(function(msg) {
  3276. ret.push(v.extend({}, error, {error: msg}));
  3277. });
  3278. } else {
  3279. ret.push(error);
  3280. }
  3281. });
  3282. return ret;
  3283. },
  3284. // Converts the error mesages by prepending the attribute name unless the
  3285. // message is prefixed by ^
  3286. convertErrorMessages: function(errors, options) {
  3287. options = options || {};
  3288. var ret = []
  3289. , prettify = options.prettify || v.prettify;
  3290. errors.forEach(function(errorInfo) {
  3291. var error = v.result(errorInfo.error,
  3292. errorInfo.value,
  3293. errorInfo.attribute,
  3294. errorInfo.options,
  3295. errorInfo.attributes,
  3296. errorInfo.globalOptions);
  3297. if (!v.isString(error)) {
  3298. ret.push(errorInfo);
  3299. return;
  3300. }
  3301. if (error[0] === '^') {
  3302. error = error.slice(1);
  3303. } else if (options.fullMessages !== false) {
  3304. error = v.capitalize(prettify(errorInfo.attribute)) + " " + error;
  3305. }
  3306. error = error.replace(/\\\^/g, "^");
  3307. error = v.format(error, {
  3308. value: v.stringifyValue(errorInfo.value, options)
  3309. });
  3310. ret.push(v.extend({}, errorInfo, {error: error}));
  3311. });
  3312. return ret;
  3313. },
  3314. // In:
  3315. // [{attribute: "<attributeName>", ...}]
  3316. // Out:
  3317. // {"<attributeName>": [{attribute: "<attributeName>", ...}]}
  3318. groupErrorsByAttribute: function(errors) {
  3319. var ret = {};
  3320. errors.forEach(function(error) {
  3321. var list = ret[error.attribute];
  3322. if (list) {
  3323. list.push(error);
  3324. } else {
  3325. ret[error.attribute] = [error];
  3326. }
  3327. });
  3328. return ret;
  3329. },
  3330. // In:
  3331. // [{error: "<message 1>", ...}, {error: "<message 2>", ...}]
  3332. // Out:
  3333. // ["<message 1>", "<message 2>"]
  3334. flattenErrorsToArray: function(errors) {
  3335. return errors
  3336. .map(function(error) { return error.error; })
  3337. .filter(function(value, index, self) {
  3338. return self.indexOf(value) === index;
  3339. });
  3340. },
  3341. cleanAttributes: function(attributes, whitelist) {
  3342. function whitelistCreator(obj, key, last) {
  3343. if (v.isObject(obj[key])) {
  3344. return obj[key];
  3345. }
  3346. return (obj[key] = last ? true : {});
  3347. }
  3348. function buildObjectWhitelist(whitelist) {
  3349. var ow = {}
  3350. , lastObject
  3351. , attr;
  3352. for (attr in whitelist) {
  3353. if (!whitelist[attr]) {
  3354. continue;
  3355. }
  3356. v.forEachKeyInKeypath(ow, attr, whitelistCreator);
  3357. }
  3358. return ow;
  3359. }
  3360. function cleanRecursive(attributes, whitelist) {
  3361. if (!v.isObject(attributes)) {
  3362. return attributes;
  3363. }
  3364. var ret = v.extend({}, attributes)
  3365. , w
  3366. , attribute;
  3367. for (attribute in attributes) {
  3368. w = whitelist[attribute];
  3369. if (v.isObject(w)) {
  3370. ret[attribute] = cleanRecursive(ret[attribute], w);
  3371. } else if (!w) {
  3372. delete ret[attribute];
  3373. }
  3374. }
  3375. return ret;
  3376. }
  3377. if (!v.isObject(whitelist) || !v.isObject(attributes)) {
  3378. return {};
  3379. }
  3380. whitelist = buildObjectWhitelist(whitelist);
  3381. return cleanRecursive(attributes, whitelist);
  3382. },
  3383. exposeModule: function(validate, root, exports, module, define) {
  3384. if (exports) {
  3385. if (module && module.exports) {
  3386. exports = module.exports = validate;
  3387. }
  3388. exports.validate = validate;
  3389. } else {
  3390. root.validate = validate;
  3391. if (validate.isFunction(define) && define.amd) {
  3392. define([], function () { return validate; });
  3393. }
  3394. }
  3395. },
  3396. warn: function(msg) {
  3397. if (typeof console !== "undefined" && console.warn) {
  3398. console.warn("[validate.js] " + msg);
  3399. }
  3400. },
  3401. error: function(msg) {
  3402. if (typeof console !== "undefined" && console.error) {
  3403. console.error("[validate.js] " + msg);
  3404. }
  3405. }
  3406. });
  3407. validate.validators = {
  3408. // Presence validates that the value isn't empty
  3409. presence: function(value, options) {
  3410. options = v.extend({}, this.options, options);
  3411. if (options.allowEmpty !== false ? !v.isDefined(value) : v.isEmpty(value)) {
  3412. return options.message || this.message || "can't be blank";
  3413. }
  3414. },
  3415. length: function(value, options, attribute) {
  3416. // Empty values are allowed
  3417. if (!v.isDefined(value)) {
  3418. return;
  3419. }
  3420. options = v.extend({}, this.options, options);
  3421. var is = options.is
  3422. , maximum = options.maximum
  3423. , minimum = options.minimum
  3424. , tokenizer = options.tokenizer || function(val) { return val; }
  3425. , err
  3426. , errors = [];
  3427. value = tokenizer(value);
  3428. var length = value.length;
  3429. if(!v.isNumber(length)) {
  3430. return options.message || this.notValid || "has an incorrect length";
  3431. }
  3432. // Is checks
  3433. if (v.isNumber(is) && length !== is) {
  3434. err = options.wrongLength ||
  3435. this.wrongLength ||
  3436. "is the wrong length (should be %{count} characters)";
  3437. errors.push(v.format(err, {count: is}));
  3438. }
  3439. if (v.isNumber(minimum) && length < minimum) {
  3440. err = options.tooShort ||
  3441. this.tooShort ||
  3442. "is too short (minimum is %{count} characters)";
  3443. errors.push(v.format(err, {count: minimum}));
  3444. }
  3445. if (v.isNumber(maximum) && length > maximum) {
  3446. err = options.tooLong ||
  3447. this.tooLong ||
  3448. "is too long (maximum is %{count} characters)";
  3449. errors.push(v.format(err, {count: maximum}));
  3450. }
  3451. if (errors.length > 0) {
  3452. return options.message || errors;
  3453. }
  3454. },
  3455. numericality: function(value, options, attribute, attributes, globalOptions) {
  3456. // Empty values are fine
  3457. if (!v.isDefined(value)) {
  3458. return;
  3459. }
  3460. options = v.extend({}, this.options, options);
  3461. var errors = []
  3462. , name
  3463. , count
  3464. , checks = {
  3465. greaterThan: function(v, c) { return v > c; },
  3466. greaterThanOrEqualTo: function(v, c) { return v >= c; },
  3467. equalTo: function(v, c) { return v === c; },
  3468. lessThan: function(v, c) { return v < c; },
  3469. lessThanOrEqualTo: function(v, c) { return v <= c; },
  3470. divisibleBy: function(v, c) { return v % c === 0; }
  3471. }
  3472. , prettify = options.prettify ||
  3473. (globalOptions && globalOptions.prettify) ||
  3474. v.prettify;
  3475. // Strict will check that it is a valid looking number
  3476. if (v.isString(value) && options.strict) {
  3477. var pattern = "^-?(0|[1-9]\\d*)";
  3478. if (!options.onlyInteger) {
  3479. pattern += "(\\.\\d+)?";
  3480. }
  3481. pattern += "$";
  3482. if (!(new RegExp(pattern).test(value))) {
  3483. return options.message ||
  3484. options.notValid ||
  3485. this.notValid ||
  3486. this.message ||
  3487. "must be a valid number";
  3488. }
  3489. }
  3490. // Coerce the value to a number unless we're being strict.
  3491. if (options.noStrings !== true && v.isString(value) && !v.isEmpty(value)) {
  3492. value = +value;
  3493. }
  3494. // If it's not a number we shouldn't continue since it will compare it.
  3495. if (!v.isNumber(value)) {
  3496. return options.message ||
  3497. options.notValid ||
  3498. this.notValid ||
  3499. this.message ||
  3500. "is not a number";
  3501. }
  3502. // Same logic as above, sort of. Don't bother with comparisons if this
  3503. // doesn't pass.
  3504. if (options.onlyInteger && !v.isInteger(value)) {
  3505. return options.message ||
  3506. options.notInteger ||
  3507. this.notInteger ||
  3508. this.message ||
  3509. "must be an integer";
  3510. }
  3511. for (name in checks) {
  3512. count = options[name];
  3513. if (v.isNumber(count) && !checks[name](value, count)) {
  3514. // This picks the default message if specified
  3515. // For example the greaterThan check uses the message from
  3516. // this.notGreaterThan so we capitalize the name and prepend "not"
  3517. var key = "not" + v.capitalize(name);
  3518. var msg = options[key] ||
  3519. this[key] ||
  3520. this.message ||
  3521. "must be %{type} %{count}";
  3522. errors.push(v.format(msg, {
  3523. count: count,
  3524. type: prettify(name)
  3525. }));
  3526. }
  3527. }
  3528. if (options.odd && value % 2 !== 1) {
  3529. errors.push(options.notOdd ||
  3530. this.notOdd ||
  3531. this.message ||
  3532. "must be odd");
  3533. }
  3534. if (options.even && value % 2 !== 0) {
  3535. errors.push(options.notEven ||
  3536. this.notEven ||
  3537. this.message ||
  3538. "must be even");
  3539. }
  3540. if (errors.length) {
  3541. return options.message || errors;
  3542. }
  3543. },
  3544. datetime: v.extend(function(value, options) {
  3545. if (!v.isFunction(this.parse) || !v.isFunction(this.format)) {
  3546. throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator");
  3547. }
  3548. // Empty values are fine
  3549. if (!v.isDefined(value)) {
  3550. return;
  3551. }
  3552. options = v.extend({}, this.options, options);
  3553. var err
  3554. , errors = []
  3555. , earliest = options.earliest ? this.parse(options.earliest, options) : NaN
  3556. , latest = options.latest ? this.parse(options.latest, options) : NaN;
  3557. value = this.parse(value, options);
  3558. // 86400000 is the number of milliseconds in a day, this is used to remove
  3559. // the time from the date
  3560. if (isNaN(value) || options.dateOnly && value % 86400000 !== 0) {
  3561. err = options.notValid ||
  3562. options.message ||
  3563. this.notValid ||
  3564. "must be a valid date";
  3565. return v.format(err, {value: arguments[0]});
  3566. }
  3567. if (!isNaN(earliest) && value < earliest) {
  3568. err = options.tooEarly ||
  3569. options.message ||
  3570. this.tooEarly ||
  3571. "must be no earlier than %{date}";
  3572. err = v.format(err, {
  3573. value: this.format(value, options),
  3574. date: this.format(earliest, options)
  3575. });
  3576. errors.push(err);
  3577. }
  3578. if (!isNaN(latest) && value > latest) {
  3579. err = options.tooLate ||
  3580. options.message ||
  3581. this.tooLate ||
  3582. "must be no later than %{date}";
  3583. err = v.format(err, {
  3584. date: this.format(latest, options),
  3585. value: this.format(value, options)
  3586. });
  3587. errors.push(err);
  3588. }
  3589. if (errors.length) {
  3590. return v.unique(errors);
  3591. }
  3592. }, {
  3593. parse: null,
  3594. format: null
  3595. }),
  3596. date: function(value, options) {
  3597. options = v.extend({}, options, {dateOnly: true});
  3598. return v.validators.datetime.call(v.validators.datetime, value, options);
  3599. },
  3600. format: function(value, options) {
  3601. if (v.isString(options) || (options instanceof RegExp)) {
  3602. options = {pattern: options};
  3603. }
  3604. options = v.extend({}, this.options, options);
  3605. var message = options.message || this.message || "is invalid"
  3606. , pattern = options.pattern
  3607. , match;
  3608. // Empty values are allowed
  3609. if (!v.isDefined(value)) {
  3610. return;
  3611. }
  3612. if (!v.isString(value)) {
  3613. return message;
  3614. }
  3615. if (v.isString(pattern)) {
  3616. pattern = new RegExp(options.pattern, options.flags);
  3617. }
  3618. match = pattern.exec(value);
  3619. if (!match || match[0].length != value.length) {
  3620. return message;
  3621. }
  3622. },
  3623. inclusion: function(value, options) {
  3624. // Empty values are fine
  3625. if (!v.isDefined(value)) {
  3626. return;
  3627. }
  3628. if (v.isArray(options)) {
  3629. options = {within: options};
  3630. }
  3631. options = v.extend({}, this.options, options);
  3632. if (v.contains(options.within, value)) {
  3633. return;
  3634. }
  3635. var message = options.message ||
  3636. this.message ||
  3637. "^%{value} is not included in the list";
  3638. return v.format(message, {value: value});
  3639. },
  3640. exclusion: function(value, options) {
  3641. // Empty values are fine
  3642. if (!v.isDefined(value)) {
  3643. return;
  3644. }
  3645. if (v.isArray(options)) {
  3646. options = {within: options};
  3647. }
  3648. options = v.extend({}, this.options, options);
  3649. if (!v.contains(options.within, value)) {
  3650. return;
  3651. }
  3652. var message = options.message || this.message || "^%{value} is restricted";
  3653. if (v.isString(options.within[value])) {
  3654. value = options.within[value];
  3655. }
  3656. return v.format(message, {value: value});
  3657. },
  3658. email: v.extend(function(value, options) {
  3659. options = v.extend({}, this.options, options);
  3660. var message = options.message || this.message || "is not a valid email";
  3661. // Empty values are fine
  3662. if (!v.isDefined(value)) {
  3663. return;
  3664. }
  3665. if (!v.isString(value)) {
  3666. return message;
  3667. }
  3668. if (!this.PATTERN.exec(value)) {
  3669. return message;
  3670. }
  3671. }, {
  3672. PATTERN: /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i
  3673. }),
  3674. equality: function(value, options, attribute, attributes, globalOptions) {
  3675. if (!v.isDefined(value)) {
  3676. return;
  3677. }
  3678. if (v.isString(options)) {
  3679. options = {attribute: options};
  3680. }
  3681. options = v.extend({}, this.options, options);
  3682. var message = options.message ||
  3683. this.message ||
  3684. "is not equal to %{attribute}";
  3685. if (v.isEmpty(options.attribute) || !v.isString(options.attribute)) {
  3686. throw new Error("The attribute must be a non empty string");
  3687. }
  3688. var otherValue = v.getDeepObjectValue(attributes, options.attribute)
  3689. , comparator = options.comparator || function(v1, v2) {
  3690. return v1 === v2;
  3691. }
  3692. , prettify = options.prettify ||
  3693. (globalOptions && globalOptions.prettify) ||
  3694. v.prettify;
  3695. if (!comparator(value, otherValue, options, attribute, attributes)) {
  3696. return v.format(message, {attribute: prettify(options.attribute)});
  3697. }
  3698. },
  3699. // A URL validator that is used to validate URLs with the ability to
  3700. // restrict schemes and some domains.
  3701. url: function(value, options) {
  3702. if (!v.isDefined(value)) {
  3703. return;
  3704. }
  3705. options = v.extend({}, this.options, options);
  3706. var message = options.message || this.message || "is not a valid url"
  3707. , schemes = options.schemes || this.schemes || ['http', 'https']
  3708. , allowLocal = options.allowLocal || this.allowLocal || false
  3709. , allowDataUrl = options.allowDataUrl || this.allowDataUrl || false;
  3710. if (!v.isString(value)) {
  3711. return message;
  3712. }
  3713. // https://gist.github.com/dperini/729294
  3714. var regex =
  3715. "^" +
  3716. // protocol identifier
  3717. "(?:(?:" + schemes.join("|") + ")://)" +
  3718. // user:pass authentication
  3719. "(?:\\S+(?::\\S*)?@)?" +
  3720. "(?:";
  3721. var tld = "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";
  3722. if (allowLocal) {
  3723. tld += "?";
  3724. } else {
  3725. regex +=
  3726. // IP address exclusion
  3727. // private & local networks
  3728. "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
  3729. "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
  3730. "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";
  3731. }
  3732. regex +=
  3733. // IP address dotted notation octets
  3734. // excludes loopback network 0.0.0.0
  3735. // excludes reserved space >= 224.0.0.0
  3736. // excludes network & broacast addresses
  3737. // (first & last IP address of each class)
  3738. "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
  3739. "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
  3740. "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
  3741. "|" +
  3742. // host name
  3743. "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
  3744. // domain name
  3745. "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
  3746. tld +
  3747. ")" +
  3748. // port number
  3749. "(?::\\d{2,5})?" +
  3750. // resource path
  3751. "(?:[/?#]\\S*)?" +
  3752. "$";
  3753. if (allowDataUrl) {
  3754. // RFC 2397
  3755. var mediaType = "\\w+\\/[-+.\\w]+(?:;[\\w=]+)*";
  3756. var urlchar = "[A-Za-z0-9-_.!~\\*'();\\/?:@&=+$,%]*";
  3757. var dataurl = "data:(?:"+mediaType+")?(?:;base64)?,"+urlchar;
  3758. regex = "(?:"+regex+")|(?:^"+dataurl+"$)";
  3759. }
  3760. var PATTERN = new RegExp(regex, 'i');
  3761. if (!PATTERN.exec(value)) {
  3762. return message;
  3763. }
  3764. },
  3765. type: v.extend(function(value, originalOptions, attribute, attributes, globalOptions) {
  3766. if (v.isString(originalOptions)) {
  3767. originalOptions = {type: originalOptions};
  3768. }
  3769. if (!v.isDefined(value)) {
  3770. return;
  3771. }
  3772. var options = v.extend({}, this.options, originalOptions);
  3773. var type = options.type;
  3774. if (!v.isDefined(type)) {
  3775. throw new Error("No type was specified");
  3776. }
  3777. var check;
  3778. if (v.isFunction(type)) {
  3779. check = type;
  3780. } else {
  3781. check = this.types[type];
  3782. }
  3783. if (!v.isFunction(check)) {
  3784. throw new Error("validate.validators.type.types." + type + " must be a function.");
  3785. }
  3786. if (!check(value, options, attribute, attributes, globalOptions)) {
  3787. var message = originalOptions.message ||
  3788. this.messages[type] ||
  3789. this.message ||
  3790. options.message ||
  3791. (v.isFunction(type) ? "must be of the correct type" : "must be of type %{type}");
  3792. if (v.isFunction(message)) {
  3793. message = message(value, originalOptions, attribute, attributes, globalOptions);
  3794. }
  3795. return v.format(message, {attribute: v.prettify(attribute), type: type});
  3796. }
  3797. }, {
  3798. types: {
  3799. object: function(value) {
  3800. return v.isObject(value) && !v.isArray(value);
  3801. },
  3802. array: v.isArray,
  3803. integer: v.isInteger,
  3804. number: v.isNumber,
  3805. string: v.isString,
  3806. date: v.isDate,
  3807. boolean: v.isBoolean
  3808. },
  3809. messages: {}
  3810. })
  3811. };
  3812. validate.formatters = {
  3813. detailed: function(errors) {return errors;},
  3814. flat: v.flattenErrorsToArray,
  3815. grouped: function(errors) {
  3816. var attr;
  3817. errors = v.groupErrorsByAttribute(errors);
  3818. for (attr in errors) {
  3819. errors[attr] = v.flattenErrorsToArray(errors[attr]);
  3820. }
  3821. return errors;
  3822. },
  3823. constraint: function(errors) {
  3824. var attr;
  3825. errors = v.groupErrorsByAttribute(errors);
  3826. for (attr in errors) {
  3827. errors[attr] = errors[attr].map(function(result) {
  3828. return result.validator;
  3829. }).sort();
  3830. }
  3831. return errors;
  3832. }
  3833. };
  3834. validate.exposeModule(validate, this, exports, module, __webpack_require__.amdD);
  3835. }).call(this,
  3836. true ? /* istanbul ignore next */ exports : 0,
  3837. true ? /* istanbul ignore next */ module : 0,
  3838. __webpack_require__.amdD);
  3839. /***/ })
  3840. /******/ });
  3841. /************************************************************************/
  3842. /******/ // The module cache
  3843. /******/ var __webpack_module_cache__ = {};
  3844. /******/
  3845. /******/ // The require function
  3846. /******/ function __webpack_require__(moduleId) {
  3847. /******/ // Check if module is in cache
  3848. /******/ var cachedModule = __webpack_module_cache__[moduleId];
  3849. /******/ if (cachedModule !== undefined) {
  3850. /******/ return cachedModule.exports;
  3851. /******/ }
  3852. /******/ // Create a new module (and put it into the cache)
  3853. /******/ var module = __webpack_module_cache__[moduleId] = {
  3854. /******/ id: moduleId,
  3855. /******/ loaded: false,
  3856. /******/ exports: {}
  3857. /******/ };
  3858. /******/
  3859. /******/ // Execute the module function
  3860. /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  3861. /******/
  3862. /******/ // Flag the module as loaded
  3863. /******/ module.loaded = true;
  3864. /******/
  3865. /******/ // Return the exports of the module
  3866. /******/ return module.exports;
  3867. /******/ }
  3868. /******/
  3869. /************************************************************************/
  3870. /******/ /* webpack/runtime/amd define */
  3871. /******/ (() => {
  3872. /******/ __webpack_require__.amdD = function () {
  3873. /******/ throw new Error('define cannot be used indirect');
  3874. /******/ };
  3875. /******/ })();
  3876. /******/
  3877. /******/ /* webpack/runtime/compat get default export */
  3878. /******/ (() => {
  3879. /******/ // getDefaultExport function for compatibility with non-harmony modules
  3880. /******/ __webpack_require__.n = (module) => {
  3881. /******/ var getter = module && module.__esModule ?
  3882. /******/ () => (module['default']) :
  3883. /******/ () => (module);
  3884. /******/ __webpack_require__.d(getter, { a: getter });
  3885. /******/ return getter;
  3886. /******/ };
  3887. /******/ })();
  3888. /******/
  3889. /******/ /* webpack/runtime/define property getters */
  3890. /******/ (() => {
  3891. /******/ // define getter functions for harmony exports
  3892. /******/ __webpack_require__.d = (exports, definition) => {
  3893. /******/ for(var key in definition) {
  3894. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  3895. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  3896. /******/ }
  3897. /******/ }
  3898. /******/ };
  3899. /******/ })();
  3900. /******/
  3901. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  3902. /******/ (() => {
  3903. /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
  3904. /******/ })();
  3905. /******/
  3906. /******/ /* webpack/runtime/make namespace object */
  3907. /******/ (() => {
  3908. /******/ // define __esModule on exports
  3909. /******/ __webpack_require__.r = (exports) => {
  3910. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  3911. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  3912. /******/ }
  3913. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  3914. /******/ };
  3915. /******/ })();
  3916. /******/
  3917. /******/ /* webpack/runtime/node module decorator */
  3918. /******/ (() => {
  3919. /******/ __webpack_require__.nmd = (module) => {
  3920. /******/ module.paths = [];
  3921. /******/ if (!module.children) module.children = [];
  3922. /******/ return module;
  3923. /******/ };
  3924. /******/ })();
  3925. /******/
  3926. /************************************************************************/
  3927. var __webpack_exports__ = {};
  3928. // This entry need to be wrapped in an IIFE because it need to be in strict mode.
  3929. (() => {
  3930. "use strict";
  3931. /*!***************************************!*\
  3932. !*** ./resources/js/create-bucket.js ***!
  3933. \***************************************/
  3934. __webpack_require__.r(__webpack_exports__);
  3935. /* harmony import */ var riot__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! riot */ "./node_modules/riot/riot.esm.js");
  3936. /* harmony import */ var _components_field_error_riot__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./components/field-error.riot */ "./resources/js/components/field-error.riot");
  3937. /* harmony import */ var _node_modules_tentakelfabrik_tiny_validator_src_FormValidator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./../../node_modules/@tentakelfabrik/tiny-validator/src/FormValidator */ "./node_modules/@tentakelfabrik/tiny-validator/src/FormValidator.js");
  3938. riot__WEBPACK_IMPORTED_MODULE_2__.register('field-error', _components_field_error_riot__WEBPACK_IMPORTED_MODULE_0__["default"]);
  3939. riot__WEBPACK_IMPORTED_MODULE_2__.mount('field-error');
  3940. var formValidation = new _node_modules_tentakelfabrik_tiny_validator_src_FormValidator__WEBPACK_IMPORTED_MODULE_1__["default"]('form', {
  3941. 'title': {
  3942. 'presence': true,
  3943. 'length': {
  3944. 'maximum': 255
  3945. }
  3946. },
  3947. 'description': {
  3948. 'length': {
  3949. 'maximum': 255
  3950. }
  3951. },
  3952. 'type': {
  3953. 'presence': true
  3954. },
  3955. 'visiblity': {
  3956. 'presence': true
  3957. }
  3958. });
  3959. })();
  3960. /******/ })()
  3961. ;