@websy/websy-designs 1.10.6 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ const bCrypt = require('bcrypt-nodejs')
2
+ const md5 = require('md5')
3
+
4
+ class AuthHelper {
5
+ constructor (dbHelper, options) {
6
+ this.dbHelper = dbHelper
7
+ const DEFAULTS = {
8
+ loginType: 'email'
9
+ }
10
+ this.options = Object.assign({}, DEFAULTS, options)
11
+ // console.log(this.options)
12
+ }
13
+ isLoggedIn (req, res, next) {
14
+ next()
15
+ }
16
+ checkPermissions (req, res, next) {
17
+ next()
18
+ }
19
+ }
20
+
21
+ module.exports = AuthHelper
@@ -221,6 +221,9 @@ class PGHelper {
221
221
  let updates = []
222
222
  for (let key in data) {
223
223
  if (this.updateIgnores.indexOf(key) === -1) {
224
+ if (typeof data[key] === 'string') {
225
+ data[key] = data[key].replace(/''/gm, `'`).replace(/'/gm, `''`).replace(/\\\\/gm, '\\')
226
+ }
224
227
  updates.push(`${key} = ${(data[key] === null ? data[key] : `'${data[key]}'`)}`)
225
228
  }
226
229
  }
@@ -269,9 +272,21 @@ class PGHelper {
269
272
  }
270
273
  else {
271
274
  delete row[(this.options.entityConfig[entity] && this.options.entityConfig[entity].idColumn) || 'id']
275
+ let sqlValues = Object.values(row).map(d => {
276
+ if (d === null) {
277
+ return d
278
+ }
279
+ else {
280
+ if (typeof d === 'string') {
281
+ d = d.replace(/'/g, `''`)
282
+ }
283
+ return `'${d}'`
284
+ // (d === null ? `${d}` : `'${d.replace(/'/)}'`)
285
+ }
286
+ }).join(',')
272
287
  sql += `
273
288
  INSERT INTO ${entity} (${Object.keys(row).join(',')})
274
- VALUES (${Object.values(row).map(d => (d === null ? `${d}` : `'${d}'`)).join(',')})
289
+ VALUES (${sqlValues})
275
290
  RETURNING ${(this.options.entityConfig[entity] && this.options.entityConfig[entity].idColumn) || 'id'};
276
291
  `
277
292
  }
@@ -12,6 +12,11 @@ module.exports = {
12
12
  const rect = el.getBoundingClientRect()
13
13
  const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
14
14
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop
15
- return { top: rect.top + scrollTop, left: rect.left + scrollLeft, width: rect.width, height: rect.height }
15
+ return {
16
+ top: rect.top + scrollTop,
17
+ left: rect.left + scrollLeft,
18
+ width: rect.width,
19
+ height: rect.height
20
+ }
16
21
  }
17
22
  }
@@ -24,10 +24,12 @@ module.exports = function (options) {
24
24
  process.env.wdRoot = __dirname
25
25
  let version = options.version || 'v1'
26
26
  process.env.WD_VERSION = version
27
- app.use(bodyParser.json({limit: '5mb'}))
28
- app.use(bodyParser.urlencoded({limit: '5mb', extended: true}))
29
- app.use(bodyParser.raw({limit: '5mb'}))
27
+ console.log('max body size is', options.maxBodySize)
28
+ app.use(bodyParser.json({limit: options.maxBodySize || '5mb'}))
29
+ app.use(bodyParser.urlencoded({limit: options.maxBodySize || '5mb', extended: true}))
30
+ app.use(bodyParser.raw({limit: options.maxBodySize || '5mb'}))
30
31
  const AuthHelper = require(`./helpers/${version}/authHelper`)
32
+ const NoAuthHelper = require(`./helpers/${version}/noAuthHelper`)
31
33
  const allowCrossDomain = (req, res, next) => {
32
34
  // console.log(req.url);
33
35
  // const allowedOrigins = ['https://www.google.com', 'http://localhost:4000', 'https://localhost:4000', 'http://ec2-3-92-185-52.compute-1.amazonaws.com', 'https://ec2-3-92-185-52.compute-1.amazonaws.com']
@@ -128,6 +130,9 @@ module.exports = function (options) {
128
130
  }
129
131
  app.use('/auth', require(`./routes/${version}/auth`)(dbHelper, options.dbEngine, app, options.strategy))
130
132
  }
133
+ else {
134
+ app.authHelper = new NoAuthHelper()
135
+ }
131
136
  const protectedRoutes = function (req, res, next) {
132
137
  let secureRoutes = true
133
138
  if (process.env.SECURE_ROUTES) {
@@ -1909,6 +1909,7 @@ class WebsyDropdown {
1909
1909
  const headerPos = WebsyUtils.getElementPos(headerEl)
1910
1910
  const contentPos = WebsyUtils.getElementPos(contentEl)
1911
1911
  if (this.options.style === 'plain' && headerPos.width > 0 && headerPos.height > 0) {
1912
+ contentEl.style.left = 'unset'
1912
1913
  contentEl.style.right = `calc(100vw - ${headerPos.right}px)`
1913
1914
  contentEl.style.width = `${Math.max(this.options.minWidth, headerEl.clientWidth)}px`
1914
1915
  if (headerPos.bottom + contentPos.height > window.innerHeight) {
@@ -1921,6 +1922,7 @@ class WebsyDropdown {
1921
1922
  }
1922
1923
  else if (this.options.style === 'plain' && headerPos.width === 0 && headerPos.height === 0) {
1923
1924
  const targetPos = WebsyUtils.getElementPos(event.target)
1925
+ contentEl.style.left = 'unset'
1924
1926
  contentEl.style.right = `calc(100vw - ${targetPos.right}px)`
1925
1927
  contentEl.style.width = `${Math.max(this.options.minWidth, targetPos.width)}px`
1926
1928
  }
@@ -2137,6 +2139,7 @@ class WebsyForm {
2137
2139
  const defaults = {
2138
2140
  submit: { text: 'Save', classes: [] },
2139
2141
  useRecaptcha: false,
2142
+ recaptchaAction: 'submit',
2140
2143
  clearAfterSave: false,
2141
2144
  fields: [],
2142
2145
  mode: 'add',
@@ -2199,6 +2202,23 @@ class WebsyForm {
2199
2202
  resolve(false)
2200
2203
  }
2201
2204
  }
2205
+ else if (this.options.useRecaptchaV3 === true) {
2206
+ grecaptcha.ready(() => {
2207
+ grecaptcha.execute(ENVIRONMENT.RECAPTCHA_KEY, { action: this.options.recaptchaAction }).then(token => {
2208
+ this.apiService.add('google/checkrecaptcha', {grecaptcharesponse: token}).then(response => {
2209
+ if (response.success && response.success === true) {
2210
+ resolve(true)
2211
+ grecaptcha.reset(`${this.elementId}_recaptcha`, {sitekey: ENVIRONMENT.RECAPTCHA_KEY})
2212
+ }
2213
+ else {
2214
+ resolve(false)
2215
+ }
2216
+ })
2217
+ }, err => {
2218
+ console.log(err)
2219
+ })
2220
+ })
2221
+ }
2202
2222
  else {
2203
2223
  resolve(true)
2204
2224
  }
@@ -2468,7 +2488,7 @@ class WebsyForm {
2468
2488
  if (f.component) {
2469
2489
  componentsToProcess.push(f)
2470
2490
  html += `
2471
- ${i > 0 ? '-->' : ''}<div id='${this.elementId}_${f.field}_inputContainer' style='${f.style || ''}' class='websy-input-container ${f.classes ? f.classes.join(' ') : ''}'>
2491
+ ${i > 0 ? '-->' : ''}<div id='${this.elementId}_${f.field}_inputContainer' style='${f.style || ''}' class='websy-input-container ${f.classes ? f.classes.join(' ') : ''} ${f.component === 'MediaUpload' ? 'media-upload' : ''}'>
2472
2492
  ${f.label ? `<label for="${f.field}">${f.label}</label>` : ''}${f.required === true ? '<span class="websy-form-required-value">*</span>' : ''}
2473
2493
  <div id='${this.elementId}_input_${f.field}_component' class='form-component'></div>
2474
2494
  <span id='${this.elementId}_${f.field}_error' class='websy-form-validation-error'></span>
@@ -2488,7 +2508,7 @@ class WebsyForm {
2488
2508
  name="${f.field}"
2489
2509
  ${(f.attributes || []).join(' ')}
2490
2510
  class="websy-input websy-textarea"
2491
- ></textarea>
2511
+ >${f.value || ''}</textarea>
2492
2512
  <span id='${this.elementId}_${f.field}_error' class='websy-form-validation-error'></span>
2493
2513
  </div><!--
2494
2514
  `
@@ -2507,7 +2527,7 @@ class WebsyForm {
2507
2527
  ${(f.attributes || []).join(' ')}
2508
2528
  name="${f.field}"
2509
2529
  placeholder="${f.placeholder || ''}"
2510
- value="${f.value || ''}"
2530
+ value="${f.type === 'date' ? '' : f.value || ''}"
2511
2531
  valueAsDate="${f.type === 'date' ? f.value : ''}"
2512
2532
  oninvalidx="this.setCustomValidity('${f.invalidMessage || 'Please fill in this field.'}')"
2513
2533
  />
@@ -2536,7 +2556,7 @@ class WebsyForm {
2536
2556
  `
2537
2557
  el.innerHTML = html
2538
2558
  this.processComponents(componentsToProcess, () => {
2539
- if (this.options.useRecaptcha === true && typeof grecaptcha !== 'undefined') {
2559
+ if ((this.options.useRecaptcha === true || this.options.useRecaptchaV3 === true) && typeof grecaptcha !== 'undefined') {
2540
2560
  this.recaptchaReady()
2541
2561
  }
2542
2562
  })
@@ -2555,6 +2575,9 @@ class WebsyForm {
2555
2575
  if (this.fieldMap[field].type === 'checkbox') {
2556
2576
  el.checked = value
2557
2577
  }
2578
+ if (this.fieldMap[field].type === 'date') {
2579
+ el.valueAsDate = value
2580
+ }
2558
2581
  }
2559
2582
  else {
2560
2583
  console.error(`Input for ${field} does not exist in form.`)
@@ -2626,6 +2649,9 @@ class WebsyForm {
2626
2649
  if (recaptchErrEl) {
2627
2650
  recaptchErrEl.classList.remove('websy-hidden')
2628
2651
  }
2652
+ if (this.options.submitErr) {
2653
+ this.options.submitErr()
2654
+ }
2629
2655
  }
2630
2656
  })
2631
2657
  }
@@ -2696,10 +2722,12 @@ class MultiForm {
2696
2722
  constructor (elementId, options) {
2697
2723
  this.elementId = elementId
2698
2724
  const DEFAULTS = {
2699
- addButton: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="256" y1="112" x2="256" y2="400" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="400" y1="256" x2="112" y2="256" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2700
- deleteButton: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="368" y1="368" x2="144" y2="144" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="368" y1="144" x2="144" y2="368" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2725
+ addIcon: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="256" y1="112" x2="256" y2="400" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="400" y1="256" x2="112" y2="256" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2726
+ deleteIcon: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="368" y1="368" x2="144" y2="144" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="368" y1="144" x2="144" y2="368" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2701
2727
  allowAdd: true,
2702
- allowDelete: true
2728
+ allowDelete: true,
2729
+ addLabel: '',
2730
+ deleteLabel: ''
2703
2731
  }
2704
2732
  this.options = Object.assign({}, DEFAULTS, options)
2705
2733
  this.formData = []
@@ -2708,7 +2736,12 @@ class MultiForm {
2708
2736
  const el = document.getElementById(elementId)
2709
2737
  if (el) {
2710
2738
  el.addEventListener('click', this.handleClick.bind(this))
2711
- el.innerHTML = `<div id='${elementId}_container' class='websy-multi-form-container'></div>`
2739
+ el.innerHTML = `
2740
+ <div id='${elementId}_container' class='websy-multi-form-container'></div>
2741
+ <button id='${this.elementId}_addButton' class='websy-multi-form-add'>
2742
+ ${this.options.addIcon}${this.options.addLabel}
2743
+ </button>
2744
+ `
2712
2745
  }
2713
2746
  this.render()
2714
2747
  }
@@ -2717,24 +2750,28 @@ class MultiForm {
2717
2750
  this.render()
2718
2751
  }
2719
2752
  addEntry () {
2720
- const el = document.getElementById(`${this.elementId}_container`)
2721
- let newId = WebsyDesigns.Utils.createIdentity()
2722
- const newFormEl = document.createElement('div')
2723
- newFormEl.id = `${this.elementId}_${newId}_formContainer`
2724
- newFormEl.classList.add('websy-multi-form-form-container')
2725
- newFormEl.innerHTML = `
2726
- <div id='${this.elementId}_${newId}_form' class='websy-multi-form-form'>
2727
- </div>
2728
- <button id='${this.elementId}_${newId}_deleteButton' data-formid='${newId}' class='hidden websy-multi-form-delete'>
2729
- ${this.options.deleteButton}
2730
- </button>
2731
- <button id='${this.elementId}_${newId}_addButton' data-formid='${newId}' class='websy-multi-form-add'>
2732
- ${this.options.addButton}
2733
- </button>
2734
- `
2735
- el.appendChild(newFormEl)
2736
- let formOptions = Object.assign({}, this.options, { fields: [...this.options.fields.map(f => Object.assign({}, f))] })
2737
- this.forms.push(new WebsyDesigns.Form(`${this.elementId}_${newId}_form`, formOptions))
2753
+ const addEl = document.getElementById(`${this.elementId}_addButton`)
2754
+ if (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) {
2755
+ const el = document.getElementById(`${this.elementId}_container`)
2756
+ let newId = WebsyDesigns.Utils.createIdentity()
2757
+ const newFormEl = document.createElement('div')
2758
+ newFormEl.id = `${this.elementId}_${newId}_formContainer`
2759
+ newFormEl.classList.add('websy-multi-form-form-container')
2760
+ let html = `
2761
+ <div id='${this.elementId}_${newId}_form' class='websy-multi-form-form'>
2762
+ </div>
2763
+ <button id='${this.elementId}_${newId}_deleteButton' data-formid='${newId}' class='websy-multi-form-delete'>
2764
+ ${this.options.deleteIcon}${this.options.deleteLabel}
2765
+ </button>
2766
+ `
2767
+ newFormEl.innerHTML = html
2768
+ el.appendChild(newFormEl)
2769
+ let formOptions = Object.assign({}, this.options, { fields: [...this.options.fields.map(f => Object.assign({}, f))] })
2770
+ this.forms.push(new WebsyDesigns.Form(`${this.elementId}_${newId}_form`, formOptions))
2771
+ if (addEl) {
2772
+ addEl.style.display = this.forms.length < this.options.maxRows ? 'flex' : 'none'
2773
+ }
2774
+ }
2738
2775
  }
2739
2776
  clear () {
2740
2777
  this.formData = []
@@ -2746,12 +2783,7 @@ class MultiForm {
2746
2783
  }
2747
2784
  }
2748
2785
  get data () {
2749
- const d = this.forms.map(f => (f.data))
2750
- console.log('forms data', d)
2751
- if (this.options.allowAdd !== false) {
2752
- // we don't return the last form
2753
- d.pop()
2754
- }
2786
+ const d = this.forms.map(f => (f.data))
2755
2787
  return d
2756
2788
  }
2757
2789
  set data (d) {
@@ -2763,16 +2795,6 @@ class MultiForm {
2763
2795
  }
2764
2796
  handleClick (event) {
2765
2797
  if (event.target.classList.contains('websy-multi-form-add')) {
2766
- let id = event.target.getAttribute('data-formid')
2767
- // hide add button and show delete button
2768
- const addButtonEl = document.getElementById(`${this.elementId}_${id}_addButton`)
2769
- if (addButtonEl) {
2770
- addButtonEl.classList.add('hidden')
2771
- }
2772
- const deleteButtonEl = document.getElementById(`${this.elementId}_${id}_deleteButton`)
2773
- if (deleteButtonEl) {
2774
- deleteButtonEl.classList.remove('hidden')
2775
- }
2776
2798
  // add new form
2777
2799
  if (this.options.allowAdd === true) {
2778
2800
  this.addEntry()
@@ -2797,6 +2819,10 @@ class MultiForm {
2797
2819
  if (el) {
2798
2820
  el.remove()
2799
2821
  }
2822
+ const addEl = document.getElementById(`${this.elementId}_addButton`)
2823
+ if (addEl) {
2824
+ addEl.style.display = (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) ? 'flex' : 'none'
2825
+ }
2800
2826
  // delete form element based on id
2801
2827
  }
2802
2828
  }
@@ -2816,7 +2842,7 @@ class MultiForm {
2816
2842
  if (this.options.allowDelete === true) {
2817
2843
  html += `
2818
2844
  <button id='${this.elementId}_${d.formId}_deleteButton' data-formid='${d.formId}' data-rowid='${d.id}' class='websy-multi-form-delete'>
2819
- ${this.options.deleteButton}
2845
+ ${this.options.deleteIcon}${this.options.deleteLabel}
2820
2846
  </button>
2821
2847
  `
2822
2848
  }
@@ -2825,20 +2851,6 @@ class MultiForm {
2825
2851
  `
2826
2852
  })
2827
2853
  let id = WebsyDesigns.Utils.createIdentity()
2828
- if (this.options.allowAdd === true) {
2829
- html += `
2830
- <div id='${this.elementId}_${id}_formContainer' class='websy-multi-form-form-container'>
2831
- <div id='${this.elementId}_${id}_form' class='websy-multi-form-form'>
2832
- </div>
2833
- <button id='${this.elementId}_${id}_deleteButton' data-formid='${id}' class='hidden websy-multi-form-delete'>
2834
- ${this.options.deleteButton}
2835
- </button>
2836
- <button id='${this.elementId}_${id}_addButton' data-formid='${id}' class='websy-multi-form-add'>
2837
- ${this.options.addButton}
2838
- </button>
2839
- </div>
2840
- `
2841
- }
2842
2854
  el.innerHTML = html
2843
2855
  this.forms = new Array(this.formData.length)
2844
2856
  this.formData.forEach((d, i) => {
@@ -2847,11 +2859,15 @@ class MultiForm {
2847
2859
  formObject.data = d
2848
2860
  this.forms[i] = formObject
2849
2861
  })
2850
- if (this.options.allowAdd === true) {
2851
- let formOptions = Object.assign({}, this.options, { fields: [...this.options.fields.map(f => Object.assign({}, f))] })
2852
- let formObject = new WebsyDesigns.Form(`${this.elementId}_${id}_form`, formOptions)
2853
- this.forms.push(formObject)
2854
- }
2862
+ const addEl = document.getElementById(`${this.elementId}_addButton`)
2863
+ if (addEl) {
2864
+ if (this.options.allowAdd === true) {
2865
+ addEl.style.display = (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) ? 'flex' : 'none'
2866
+ }
2867
+ else {
2868
+ addEl.style.display = 'none'
2869
+ }
2870
+ }
2855
2871
  }
2856
2872
  }
2857
2873
  validateForm () {
@@ -1882,6 +1882,7 @@ var WebsyDropdown = /*#__PURE__*/function () {
1882
1882
  var headerPos = WebsyUtils.getElementPos(headerEl);
1883
1883
  var contentPos = WebsyUtils.getElementPos(contentEl);
1884
1884
  if (this.options.style === 'plain' && headerPos.width > 0 && headerPos.height > 0) {
1885
+ contentEl.style.left = 'unset';
1885
1886
  contentEl.style.right = "calc(100vw - ".concat(headerPos.right, "px)");
1886
1887
  contentEl.style.width = "".concat(Math.max(this.options.minWidth, headerEl.clientWidth), "px");
1887
1888
  if (headerPos.bottom + contentPos.height > window.innerHeight) {
@@ -1892,6 +1893,7 @@ var WebsyDropdown = /*#__PURE__*/function () {
1892
1893
  }
1893
1894
  } else if (this.options.style === 'plain' && headerPos.width === 0 && headerPos.height === 0) {
1894
1895
  var targetPos = WebsyUtils.getElementPos(event.target);
1896
+ contentEl.style.left = 'unset';
1895
1897
  contentEl.style.right = "calc(100vw - ".concat(targetPos.right, "px)");
1896
1898
  contentEl.style.width = "".concat(Math.max(this.options.minWidth, targetPos.width), "px");
1897
1899
  }
@@ -2128,6 +2130,7 @@ var WebsyForm = /*#__PURE__*/function () {
2128
2130
  classes: []
2129
2131
  },
2130
2132
  useRecaptcha: false,
2133
+ recaptchaAction: 'submit',
2131
2134
  clearAfterSave: false,
2132
2135
  fields: [],
2133
2136
  mode: 'add',
@@ -2198,6 +2201,27 @@ var WebsyForm = /*#__PURE__*/function () {
2198
2201
  } else {
2199
2202
  resolve(false);
2200
2203
  }
2204
+ } else if (_this13.options.useRecaptchaV3 === true) {
2205
+ grecaptcha.ready(function () {
2206
+ grecaptcha.execute(ENVIRONMENT.RECAPTCHA_KEY, {
2207
+ action: _this13.options.recaptchaAction
2208
+ }).then(function (token) {
2209
+ _this13.apiService.add('google/checkrecaptcha', {
2210
+ grecaptcharesponse: token
2211
+ }).then(function (response) {
2212
+ if (response.success && response.success === true) {
2213
+ resolve(true);
2214
+ grecaptcha.reset("".concat(_this13.elementId, "_recaptcha"), {
2215
+ sitekey: ENVIRONMENT.RECAPTCHA_KEY
2216
+ });
2217
+ } else {
2218
+ resolve(false);
2219
+ }
2220
+ });
2221
+ }, function (err) {
2222
+ console.log(err);
2223
+ });
2224
+ });
2201
2225
  } else {
2202
2226
  resolve(true);
2203
2227
  }
@@ -2494,11 +2518,11 @@ var WebsyForm = /*#__PURE__*/function () {
2494
2518
  f.owningElement = _this18.elementId;
2495
2519
  if (f.component) {
2496
2520
  componentsToProcess.push(f);
2497
- html += "\n ".concat(i > 0 ? '-->' : '', "<div id='").concat(_this18.elementId, "_").concat(f.field, "_inputContainer' style='").concat(f.style || '', "' class='websy-input-container ").concat(f.classes ? f.classes.join(' ') : '', "'>\n ").concat(f.label ? "<label for=\"".concat(f.field, "\">").concat(f.label, "</label>") : '').concat(f.required === true ? '<span class="websy-form-required-value">*</span>' : '', "\n <div id='").concat(_this18.elementId, "_input_").concat(f.field, "_component' class='form-component'></div>\n <span id='").concat(_this18.elementId, "_").concat(f.field, "_error' class='websy-form-validation-error'></span>\n </div><!--\n ");
2521
+ html += "\n ".concat(i > 0 ? '-->' : '', "<div id='").concat(_this18.elementId, "_").concat(f.field, "_inputContainer' style='").concat(f.style || '', "' class='websy-input-container ").concat(f.classes ? f.classes.join(' ') : '', " ").concat(f.component === 'MediaUpload' ? 'media-upload' : '', "'>\n ").concat(f.label ? "<label for=\"".concat(f.field, "\">").concat(f.label, "</label>") : '').concat(f.required === true ? '<span class="websy-form-required-value">*</span>' : '', "\n <div id='").concat(_this18.elementId, "_input_").concat(f.field, "_component' class='form-component'></div>\n <span id='").concat(_this18.elementId, "_").concat(f.field, "_error' class='websy-form-validation-error'></span>\n </div><!--\n ");
2498
2522
  } else if (f.type === 'longtext') {
2499
- html += "\n ".concat(i > 0 ? '-->' : '', "<div id='").concat(_this18.elementId, "_").concat(f.field, "_inputContainer' style='").concat(f.style || '', "' class='websy-input-container ").concat(f.classes ? f.classes.join(' ') : '', "'>\n ").concat(f.label ? "<label for=\"".concat(f.field, "\">").concat(f.label, "</label>") : '').concat(f.required === true ? '<span class="websy-form-required-value">*</span>' : '', "\n <textarea\n id=\"").concat(_this18.elementId, "_input_").concat(f.field, "\"\n ").concat(f.required === true ? 'required' : '', " \n placeholder=\"").concat(f.placeholder || '', "\"\n data-user-type=\"").concat(f.type, "\"\n data-index=\"").concat(i, "\"\n name=\"").concat(f.field, "\" \n ").concat((f.attributes || []).join(' '), "\n class=\"websy-input websy-textarea\"\n ></textarea>\n <span id='").concat(_this18.elementId, "_").concat(f.field, "_error' class='websy-form-validation-error'></span>\n </div><!--\n ");
2523
+ html += "\n ".concat(i > 0 ? '-->' : '', "<div id='").concat(_this18.elementId, "_").concat(f.field, "_inputContainer' style='").concat(f.style || '', "' class='websy-input-container ").concat(f.classes ? f.classes.join(' ') : '', "'>\n ").concat(f.label ? "<label for=\"".concat(f.field, "\">").concat(f.label, "</label>") : '').concat(f.required === true ? '<span class="websy-form-required-value">*</span>' : '', "\n <textarea\n id=\"").concat(_this18.elementId, "_input_").concat(f.field, "\"\n ").concat(f.required === true ? 'required' : '', " \n placeholder=\"").concat(f.placeholder || '', "\"\n data-user-type=\"").concat(f.type, "\"\n data-index=\"").concat(i, "\"\n name=\"").concat(f.field, "\" \n ").concat((f.attributes || []).join(' '), "\n class=\"websy-input websy-textarea\"\n >").concat(f.value || '', "</textarea>\n <span id='").concat(_this18.elementId, "_").concat(f.field, "_error' class='websy-form-validation-error'></span>\n </div><!--\n ");
2500
2524
  } else {
2501
- html += "\n ".concat(i > 0 ? '-->' : '', "<div id='").concat(_this18.elementId, "_").concat(f.field, "_inputContainer' style='").concat(f.style || '', "' class='websy-input-container ").concat(f.classes ? f.classes.join(' ') : '', "'>\n ").concat(f.label ? "<label for=\"".concat(f.field, "\">").concat(f.label, "</label>") : '').concat(f.required === true ? '<span class="websy-form-required-value">*</span>' : '', "\n <input \n id=\"").concat(_this18.elementId, "_input_").concat(f.field, "\"\n ").concat(f.required === true ? 'required' : '', " \n type=\"").concat((f.type === 'expiry' ? 'text' : f.type === 'cvv' ? 'number' : f.type) || 'text', "\" \n data-user-type=\"").concat(f.type, "\"\n data-index=\"").concat(i, "\"\n class=\"websy-input\" \n ").concat((f.attributes || []).join(' '), "\n name=\"").concat(f.field, "\" \n placeholder=\"").concat(f.placeholder || '', "\"\n value=\"").concat(f.value || '', "\"\n valueAsDate=\"").concat(f.type === 'date' ? f.value : '', "\"\n oninvalidx=\"this.setCustomValidity('").concat(f.invalidMessage || 'Please fill in this field.', "')\"\n />\n <span id='").concat(_this18.elementId, "_").concat(f.field, "_error' class='websy-form-validation-error'></span>\n </div><!--\n ");
2525
+ html += "\n ".concat(i > 0 ? '-->' : '', "<div id='").concat(_this18.elementId, "_").concat(f.field, "_inputContainer' style='").concat(f.style || '', "' class='websy-input-container ").concat(f.classes ? f.classes.join(' ') : '', "'>\n ").concat(f.label ? "<label for=\"".concat(f.field, "\">").concat(f.label, "</label>") : '').concat(f.required === true ? '<span class="websy-form-required-value">*</span>' : '', "\n <input \n id=\"").concat(_this18.elementId, "_input_").concat(f.field, "\"\n ").concat(f.required === true ? 'required' : '', " \n type=\"").concat((f.type === 'expiry' ? 'text' : f.type === 'cvv' ? 'number' : f.type) || 'text', "\" \n data-user-type=\"").concat(f.type, "\"\n data-index=\"").concat(i, "\"\n class=\"websy-input\" \n ").concat((f.attributes || []).join(' '), "\n name=\"").concat(f.field, "\" \n placeholder=\"").concat(f.placeholder || '', "\"\n value=\"").concat(f.type === 'date' ? '' : f.value || '', "\"\n valueAsDate=\"").concat(f.type === 'date' ? f.value : '', "\"\n oninvalidx=\"this.setCustomValidity('").concat(f.invalidMessage || 'Please fill in this field.', "')\"\n />\n <span id='").concat(_this18.elementId, "_").concat(f.field, "_error' class='websy-form-validation-error'></span>\n </div><!--\n ");
2502
2526
  }
2503
2527
  });
2504
2528
  if (this.options.useRecaptcha === true) {
@@ -2511,7 +2535,7 @@ var WebsyForm = /*#__PURE__*/function () {
2511
2535
  html += " \n </form>\n <div id=\"".concat(this.elementId, "_validationFail\" class=\"websy-validation-failure\"></div>\n ");
2512
2536
  el.innerHTML = html;
2513
2537
  this.processComponents(componentsToProcess, function () {
2514
- if (_this18.options.useRecaptcha === true && typeof grecaptcha !== 'undefined') {
2538
+ if ((_this18.options.useRecaptcha === true || _this18.options.useRecaptchaV3 === true) && typeof grecaptcha !== 'undefined') {
2515
2539
  _this18.recaptchaReady();
2516
2540
  }
2517
2541
  });
@@ -2531,6 +2555,9 @@ var WebsyForm = /*#__PURE__*/function () {
2531
2555
  if (this.fieldMap[field].type === 'checkbox') {
2532
2556
  el.checked = value;
2533
2557
  }
2558
+ if (this.fieldMap[field].type === 'date') {
2559
+ el.valueAsDate = value;
2560
+ }
2534
2561
  } else {
2535
2562
  console.error("Input for ".concat(field, " does not exist in form."));
2536
2563
  }
@@ -2600,6 +2627,9 @@ var WebsyForm = /*#__PURE__*/function () {
2600
2627
  if (recaptchErrEl) {
2601
2628
  recaptchErrEl.classList.remove('websy-hidden');
2602
2629
  }
2630
+ if (_this19.options.submitErr) {
2631
+ _this19.options.submitErr();
2632
+ }
2603
2633
  }
2604
2634
  });
2605
2635
  }
@@ -2677,10 +2707,12 @@ var MultiForm = /*#__PURE__*/function () {
2677
2707
  _classCallCheck(this, MultiForm);
2678
2708
  this.elementId = elementId;
2679
2709
  var DEFAULTS = {
2680
- addButton: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 512 512\"><line x1=\"256\" y1=\"112\" x2=\"256\" y2=\"400\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/><line x1=\"400\" y1=\"256\" x2=\"112\" y2=\"256\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/></svg>",
2681
- deleteButton: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 512 512\"><line x1=\"368\" y1=\"368\" x2=\"144\" y2=\"144\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/><line x1=\"368\" y1=\"144\" x2=\"144\" y2=\"368\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/></svg>",
2710
+ addIcon: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 512 512\"><line x1=\"256\" y1=\"112\" x2=\"256\" y2=\"400\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/><line x1=\"400\" y1=\"256\" x2=\"112\" y2=\"256\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/></svg>",
2711
+ deleteIcon: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 512 512\"><line x1=\"368\" y1=\"368\" x2=\"144\" y2=\"144\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/><line x1=\"368\" y1=\"144\" x2=\"144\" y2=\"368\" style=\"fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px\"/></svg>",
2682
2712
  allowAdd: true,
2683
- allowDelete: true
2713
+ allowDelete: true,
2714
+ addLabel: '',
2715
+ deleteLabel: ''
2684
2716
  };
2685
2717
  this.options = _extends({}, DEFAULTS, options);
2686
2718
  this.formData = [];
@@ -2689,7 +2721,7 @@ var MultiForm = /*#__PURE__*/function () {
2689
2721
  var el = document.getElementById(elementId);
2690
2722
  if (el) {
2691
2723
  el.addEventListener('click', this.handleClick.bind(this));
2692
- el.innerHTML = "<div id='".concat(elementId, "_container' class='websy-multi-form-container'></div>");
2724
+ el.innerHTML = "\n <div id='".concat(elementId, "_container' class='websy-multi-form-container'></div>\n <button id='").concat(this.elementId, "_addButton' class='websy-multi-form-add'>\n ").concat(this.options.addIcon).concat(this.options.addLabel, "\n </button> \n ");
2693
2725
  }
2694
2726
  this.render();
2695
2727
  }
@@ -2702,19 +2734,26 @@ var MultiForm = /*#__PURE__*/function () {
2702
2734
  }, {
2703
2735
  key: "addEntry",
2704
2736
  value: function addEntry() {
2705
- var el = document.getElementById("".concat(this.elementId, "_container"));
2706
- var newId = WebsyDesigns.Utils.createIdentity();
2707
- var newFormEl = document.createElement('div');
2708
- newFormEl.id = "".concat(this.elementId, "_").concat(newId, "_formContainer");
2709
- newFormEl.classList.add('websy-multi-form-form-container');
2710
- newFormEl.innerHTML = "\n <div id='".concat(this.elementId, "_").concat(newId, "_form' class='websy-multi-form-form'>\n </div>\n <button id='").concat(this.elementId, "_").concat(newId, "_deleteButton' data-formid='").concat(newId, "' class='hidden websy-multi-form-delete'>\n ").concat(this.options.deleteButton, "\n </button> \n <button id='").concat(this.elementId, "_").concat(newId, "_addButton' data-formid='").concat(newId, "' class='websy-multi-form-add'>\n ").concat(this.options.addButton, "\n </button> \n ");
2711
- el.appendChild(newFormEl);
2712
- var formOptions = _extends({}, this.options, {
2713
- fields: _toConsumableArray(this.options.fields.map(function (f) {
2714
- return _extends({}, f);
2715
- }))
2716
- });
2717
- this.forms.push(new WebsyDesigns.Form("".concat(this.elementId, "_").concat(newId, "_form"), formOptions));
2737
+ var addEl = document.getElementById("".concat(this.elementId, "_addButton"));
2738
+ if (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) {
2739
+ var el = document.getElementById("".concat(this.elementId, "_container"));
2740
+ var newId = WebsyDesigns.Utils.createIdentity();
2741
+ var newFormEl = document.createElement('div');
2742
+ newFormEl.id = "".concat(this.elementId, "_").concat(newId, "_formContainer");
2743
+ newFormEl.classList.add('websy-multi-form-form-container');
2744
+ var html = "\n <div id='".concat(this.elementId, "_").concat(newId, "_form' class='websy-multi-form-form'>\n </div>\n <button id='").concat(this.elementId, "_").concat(newId, "_deleteButton' data-formid='").concat(newId, "' class='websy-multi-form-delete'>\n ").concat(this.options.deleteIcon).concat(this.options.deleteLabel, "\n </button>\n ");
2745
+ newFormEl.innerHTML = html;
2746
+ el.appendChild(newFormEl);
2747
+ var formOptions = _extends({}, this.options, {
2748
+ fields: _toConsumableArray(this.options.fields.map(function (f) {
2749
+ return _extends({}, f);
2750
+ }))
2751
+ });
2752
+ this.forms.push(new WebsyDesigns.Form("".concat(this.elementId, "_").concat(newId, "_form"), formOptions));
2753
+ if (addEl) {
2754
+ addEl.style.display = this.forms.length < this.options.maxRows ? 'flex' : 'none';
2755
+ }
2756
+ }
2718
2757
  }
2719
2758
  }, {
2720
2759
  key: "clear",
@@ -2733,11 +2772,6 @@ var MultiForm = /*#__PURE__*/function () {
2733
2772
  var d = this.forms.map(function (f) {
2734
2773
  return f.data;
2735
2774
  });
2736
- console.log('forms data', d);
2737
- if (this.options.allowAdd !== false) {
2738
- // we don't return the last form
2739
- d.pop();
2740
- }
2741
2775
  return d;
2742
2776
  },
2743
2777
  set: function set(d) {
@@ -2756,16 +2790,6 @@ var MultiForm = /*#__PURE__*/function () {
2756
2790
  key: "handleClick",
2757
2791
  value: function handleClick(event) {
2758
2792
  if (event.target.classList.contains('websy-multi-form-add')) {
2759
- var id = event.target.getAttribute('data-formid');
2760
- // hide add button and show delete button
2761
- var addButtonEl = document.getElementById("".concat(this.elementId, "_").concat(id, "_addButton"));
2762
- if (addButtonEl) {
2763
- addButtonEl.classList.add('hidden');
2764
- }
2765
- var deleteButtonEl = document.getElementById("".concat(this.elementId, "_").concat(id, "_deleteButton"));
2766
- if (deleteButtonEl) {
2767
- deleteButtonEl.classList.remove('hidden');
2768
- }
2769
2793
  // add new form
2770
2794
  if (this.options.allowAdd === true) {
2771
2795
  this.addEntry();
@@ -2773,12 +2797,12 @@ var MultiForm = /*#__PURE__*/function () {
2773
2797
  }
2774
2798
  if (event.target.classList.contains('websy-multi-form-delete')) {
2775
2799
  // delete form based on index
2776
- var _id = event.target.getAttribute('data-formid');
2800
+ var id = event.target.getAttribute('data-formid');
2777
2801
  var rowId = event.target.getAttribute('data-rowid');
2778
2802
  this.recordsToDelete.push(rowId);
2779
2803
  var indexToDelete = -1;
2780
2804
  for (var i = 0; i < this.forms.length; i++) {
2781
- if (this.forms[i].elementId === "".concat(this.elementId, "_").concat(_id, "_form")) {
2805
+ if (this.forms[i].elementId === "".concat(this.elementId, "_").concat(id, "_form")) {
2782
2806
  indexToDelete = i;
2783
2807
  break;
2784
2808
  }
@@ -2786,10 +2810,14 @@ var MultiForm = /*#__PURE__*/function () {
2786
2810
  if (indexToDelete !== -1) {
2787
2811
  this.forms.splice(indexToDelete, 1);
2788
2812
  }
2789
- var el = document.getElementById("".concat(this.elementId, "_").concat(_id, "_formContainer"));
2813
+ var el = document.getElementById("".concat(this.elementId, "_").concat(id, "_formContainer"));
2790
2814
  if (el) {
2791
2815
  el.remove();
2792
2816
  }
2817
+ var addEl = document.getElementById("".concat(this.elementId, "_addButton"));
2818
+ if (addEl) {
2819
+ addEl.style.display = typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows ? 'flex' : 'none';
2820
+ }
2793
2821
  // delete form element based on id
2794
2822
  }
2795
2823
  }
@@ -2806,14 +2834,11 @@ var MultiForm = /*#__PURE__*/function () {
2806
2834
  d.formId = WebsyDesigns.Utils.createIdentity();
2807
2835
  html += "\n <div id='".concat(_this21.elementId, "_").concat(d.formId, "_formContainer' class='websy-multi-form-form-container'>\n <div id='").concat(_this21.elementId, "_").concat(d.formId, "_form' class='websy-multi-form-form'>\n </div>\n ");
2808
2836
  if (_this21.options.allowDelete === true) {
2809
- html += "\n <button id='".concat(_this21.elementId, "_").concat(d.formId, "_deleteButton' data-formid='").concat(d.formId, "' data-rowid='").concat(d.id, "' class='websy-multi-form-delete'>\n ").concat(_this21.options.deleteButton, "\n </button>\n ");
2837
+ html += "\n <button id='".concat(_this21.elementId, "_").concat(d.formId, "_deleteButton' data-formid='").concat(d.formId, "' data-rowid='").concat(d.id, "' class='websy-multi-form-delete'>\n ").concat(_this21.options.deleteIcon).concat(_this21.options.deleteLabel, "\n </button>\n ");
2810
2838
  }
2811
2839
  html += "\n </div>\n ";
2812
2840
  });
2813
2841
  var id = WebsyDesigns.Utils.createIdentity();
2814
- if (this.options.allowAdd === true) {
2815
- html += "\n <div id='".concat(this.elementId, "_").concat(id, "_formContainer' class='websy-multi-form-form-container'>\n <div id='").concat(this.elementId, "_").concat(id, "_form' class='websy-multi-form-form'>\n </div>\n <button id='").concat(this.elementId, "_").concat(id, "_deleteButton' data-formid='").concat(id, "' class='hidden websy-multi-form-delete'>\n ").concat(this.options.deleteButton, "\n </button> \n <button id='").concat(this.elementId, "_").concat(id, "_addButton' data-formid='").concat(id, "' class='websy-multi-form-add'>\n ").concat(this.options.addButton, "\n </button> \n </div>\n ");
2816
- }
2817
2842
  el.innerHTML = html;
2818
2843
  this.forms = new Array(this.formData.length);
2819
2844
  this.formData.forEach(function (d, i) {
@@ -2826,14 +2851,13 @@ var MultiForm = /*#__PURE__*/function () {
2826
2851
  formObject.data = d;
2827
2852
  _this21.forms[i] = formObject;
2828
2853
  });
2829
- if (this.options.allowAdd === true) {
2830
- var formOptions = _extends({}, this.options, {
2831
- fields: _toConsumableArray(this.options.fields.map(function (f) {
2832
- return _extends({}, f);
2833
- }))
2834
- });
2835
- var formObject = new WebsyDesigns.Form("".concat(this.elementId, "_").concat(id, "_form"), formOptions);
2836
- this.forms.push(formObject);
2854
+ var addEl = document.getElementById("".concat(this.elementId, "_addButton"));
2855
+ if (addEl) {
2856
+ if (this.options.allowAdd === true) {
2857
+ addEl.style.display = typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows ? 'flex' : 'none';
2858
+ } else {
2859
+ addEl.style.display = 'none';
2860
+ }
2837
2861
  }
2838
2862
  }
2839
2863
  }