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.

4555 lines
136 KiB

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