{"version":3,"file":"crispy-formset-modal.min.js","sources":["../../../../js/src/icons.js","../../../../js/src/utils.js","../../../../js/src/formset.js","../../../../js/src/modal.js","../../../../js/src/modal-form.js","../../../../js/src/formset-modal.js","../../../../js/src/index.js"],"sourcesContent":["const bootstrapPencilIcon = `\n\n`;\n\nconst tailwindPencilIcon = `\n\n`;\n\nexport { bootstrapPencilIcon, tailwindPencilIcon };\n","import { bootstrapPencilIcon, tailwindPencilIcon } from \"./icons\";\n\nlet gettext = window.gettext;\nconst _has = Object.prototype.hasOwnProperty;\n/**\n * Checks if the object has the property passed as argument\n */\nexport function has(object, key) {\n return _has.call(object, key);\n}\n\nexport function uuidv4() {\n function randomHex() {\n return Math.floor((1 + Math.random()) * 0x10000)\n .toString(16)\n .substring(1);\n }\n\n const part1 = randomHex() + randomHex();\n const part2 = randomHex();\n const part3 = ((parseInt(randomHex(), 16) & 0x0fff) | 0x4000).toString(16);\n const part4 = ((parseInt(randomHex(), 16) & 0x3fff) | 0x8000).toString(16);\n const part5 = randomHex() + randomHex() + randomHex();\n\n return `${part1}-${part2}-${part3}-${part4}-${part5}`;\n}\n\nexport function getTextValue(el) {\n let type = el.tagName.toLowerCase();\n let textValue = \"\";\n if (type === \"select\") {\n textValue = el.options[el.selectedIndex].innerText;\n } else {\n if (has(el, \"inputmask\")) {\n textValue = el.inputmask.undoValue;\n } else {\n if (el.getAttribute(\"type\") == \"date\") {\n if (el.value) {\n textValue = new Date(...el.value.split(\"-\")).toLocaleDateString();\n }\n } else {\n if (el.getAttribute(\"type\") == \"checkbox\") {\n textValue = el.checked ? \"on\" : \"off\";\n } else {\n textValue = el.value;\n }\n }\n }\n }\n return textValue;\n}\n\nexport function getNumberValue(value) {\n return Number(value);\n}\n\nexport function executeAllCalculatedFields() {\n if (window.hasOwnProperty(\"calculatedFields\")) {\n calculatedFields.forEach(function (obj) {\n obj.executeAll();\n });\n }\n}\n\nexport function hookCalculatedFields() {\n if (window.hasOwnProperty(\"calculatedFields\")) {\n window.calculatedFields.forEach(function (obj) {\n let el = obj.field;\n let id = el.getAttribute(\"id\");\n let column = document.querySelector(\"td[data-source='\" + id + \"']\");\n if (column) {\n if (!el.hasAttribute(\"data-event\")) {\n el.addEventListener(\"oncalculate\", function () {\n let content = getTextValue(el);\n // The column element is refetched since the table is recreated on each event\n let _column = document.querySelector(\n \"td[data-source='\" + id + \"']\"\n );\n if (_column) {\n _column.innerText = content;\n }\n });\n el.setAttribute(\"data-event\", true);\n }\n }\n });\n }\n}\n\nexport function removeChildren(a) {\n while (a.hasChildNodes()) {\n a.removeChild(a.lastChild);\n }\n}\n\nexport function injectStyles() {\n const css = `\n .cfm-selection-border {\n position: absolute;\n top: 0px;\n bottom: 0px;\n left: 0px;\n width: 0.150rem;\n background-color: rgb(220 53 69 / 1);\n }\n .cfm-modal-backdrop {\n position: fixed;\n inset: 0px;\n z-index: 100;\n background-color: rgb(31 41 55 / 1);\n opacity: 0;\n transition-property: opacity;\n transition-duration: 300ms;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n }\n .cfm-opacity-50 {\n opacity: 0.5;\n }\n .cfm-opacity-100 {\n opacity: 1;\n }\n .cfm-tr.selected{\n background-color: rgba(0, 0, 0, 0.08) !important;\n }\n .cfm .modal-body {\n max-height: calc(100vh - 150px);\n overflow-x: auto;\n }\n .cfm-cursor-pointer {\n cursor: pointer;\n }\n `;\n const style = document.createElement(\"style\");\n style.innerHTML = css;\n document.head.appendChild(style);\n}\n\nexport function getModalInstance(id, instances) {\n if (instances.some((modalInstance) => modalInstance.id === id)) {\n return instances.find((modalInstance) => modalInstance.id === id);\n }\n return false;\n}\n\nexport function ready(fn) {\n if (document.readyState !== \"loading\") {\n fn();\n } else {\n document.addEventListener(\"DOMContentLoaded\", fn);\n }\n}\n\nif (!window.django || !window.django.jsi18n_initialized) {\n gettext = (msg) => msg;\n}\n\nconst bootstrap4Classes = {\n hidden: \"d-none\",\n inlineFlex: \"d-inline-flex\",\n flex: \"d-flex\",\n relative: \"position-relative\",\n textRight: \"text-right\",\n textCenter: \"text-center\",\n alignMiddle: \"align-middle\",\n pointer: \"cfm-cursor-pointer\",\n editBtn: \"btn-open-row btn btn-sm btn-primary\",\n checkbox: \"checkbox\",\n td: \"cfm-td\",\n tr: \"cfm-tr\",\n selectionMark: \"cfm-selection-border\",\n p0: \"p-0\",\n opacity50: \"cfm-opacity-50\",\n opacity100: \"cfm-opacity-100\",\n backdrop: \"cfm-modal-backdrop\",\n justifyStart: \"justify-content-start\",\n justifyCenter: \"justify-content-center\",\n justifyEnd: \"justify-content-end\",\n itemsStart: \"align-items-start\",\n itemsCenter: \"align-items-center\",\n itemsEnd: \"align-items-end\",\n};\n\nconst bootstrap5Classes = {\n ...bootstrap4Classes,\n textRight: \"text-end\",\n};\n\nconst tailwindClasses = {\n hidden: \"hidden\",\n inlineFlex: \"inline-flex\",\n flex: \"flex\",\n relative: \"relative\",\n textRight: \"text-right\",\n textCenter: \"text-center\",\n alignMiddle: \"align-middle\",\n pointer: \"cursor-pointer\",\n editBtn:\n \"btn-open-row mx-auto p-1 border border-transparent rounded-full shadow-sm text-gray-500 bg-gray-100 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-500\",\n checkbox:\n \"w-4 rounded border-gray-300 focus:ring-indigo-500 text-indigo-600 h-4\",\n td: \"whitespace-nowrap px-3 py-3 text-sm text-gray-500\",\n tr: \"divide-x divide-gray-200 border-b\",\n selectionMark:\n \"absolute inset-y-0 left-0 w-0.5 bg-indigo-600 selection-border\",\n p0: \"p-0\",\n opacity50: \"opacity-50\",\n opacity100: \"opacity-100\",\n backdrop:\n \"transition-opacity ease-in-out duration-300 opacity-0 bg-gray-800 fixed inset-0 z-40\",\n justifyStart: \"justify-start\",\n justifyCenter: \"justify-center\",\n justifyEnd: \"justify-end\",\n itemsStart: \"items-start\",\n itemsCenter: \"items-center\",\n itemsEnd: \"items-end\",\n};\n\nconst bulmaClasses = {\n hidden: \"is-hidden\",\n inlineFlex: \"is-inline-flex\",\n flex: \"is-flex\",\n relative: \"is-relative\",\n textRight: \"has-text-right\",\n textCenter: \"has-text-center\",\n alignMiddle: \"is-vcentered\",\n pointer: \"cfm-cursor-pointer\",\n editBtn: \"btn-open-row button is-primary is-small\",\n checkbox: \"checkbox\",\n td: \"cfm-td\",\n tr: \"cfm-tr\",\n selectionMark: \"cfm-selection-border\",\n p0: \"p-0\",\n opacity50: \"cfm-opacity-50\",\n opacity100: \"cfm-opacity-100\",\n backdrop: \"cfm-modal-backdrop\",\n justifyStart: \"is-justify-content-start\",\n justifyCenter: \"is-justify-content-center\",\n justifyEnd: \"is-justify-content-end\",\n itemsStart: \"is-align-items-start\",\n itemsCenter: \"is-align-items-center\",\n itemsEnd: \"is-align-items-end\",\n};\n\n\nconst tailwindSizeClasses = {\n sm: \"max-w-lg\",\n md: \"max-w-4xl\",\n lg: \"max-w-5xl\",\n xl: \"max-w-6xl\",\n};\n\nconst bootstrap4SizeClasses = {\n sm: \"modal-sm\",\n md: \"modal-md\",\n lg: \"modal-lg\",\n xl: \"modal-xl\",\n};\n\nconst bootstrap5SizeClasses = bootstrap4SizeClasses;\n\nconst bulmaSizeClasses = {\n sm: \"modal-sm\",\n md: \"modal-md\",\n lg: \"modal-lg\",\n xl: \"modal-xl\",\n};\n\nconst templatePacks = {\n bootstrap4: {\n classes: bootstrap4Classes,\n sizes: bootstrap4SizeClasses,\n pencilIcon: bootstrapPencilIcon,\n },\n bootstrap5: {\n classes: bootstrap5Classes,\n sizes: bootstrap5SizeClasses,\n pencilIcon: bootstrapPencilIcon,\n },\n tailwind: {\n classes: tailwindClasses,\n sizes: tailwindSizeClasses,\n pencilIcon: tailwindPencilIcon,\n },\n bulma: {\n classes: bulmaClasses,\n sizes: bulmaSizeClasses,\n pencilIcon: bootstrapPencilIcon,\n },\n};\n\nexport { gettext, bootstrap4Classes, templatePacks };\n","/**\n* Django formset helper\n* Copyright (c) 2013, Ionata Web Solutions\n* All rights reserved.\n*\n* Redistribution and use in source and binary forms, with or without\n* modification, are permitted provided that the following conditions are met:\n*\n* Redistributions of source code must retain the above copyright notice, this\n* list of conditions and the following disclaimer.\n*\n* Redistributions in binary form must reproduce the above copyright notice, this\n* list of conditions and the following disclaimer in the documentation and/or\n* other materials provided with the distribution.\n*\n* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\nimport { uuidv4 } from \"./utils\";\nimport $ from \"jquery\";\n\nexport const pluginName = 'formset';\n\n\nclass Formset {\n constructor(el, options) {\n var _this = this;\n\n //Defaults:\n this.opts = $.extend({}, Formset.defaults, options);\n\n this.$formset = $(el);\n this.$formset.attr(\"data-uuid\", uuidv4());\n this.$emptyForm = this.$formset.find(this.opts.emptyForm);\n this.$body = this.$formset.find(this.opts.body);\n this.$add = this.$formset.find(this.opts.add);\n\n this.formsetPrefix = $(el).data('formset-prefix');\n\n // Bind to the `Add form` button\n this.addForm = $.proxy(this, 'addForm');\n this.$add.click(this.addForm);\n\n // Bind receiver to `formAdded` and `formDeleted` events\n this.$formset.on('formAdded formDeleted', this.opts.form, $.proxy(this, 'checkMaxForms'));\n\n // Set up the existing forms\n this.$forms().each(function (i, form) {\n var $form = $(form);\n _this.bindForm($(this), i);\n });\n\n // Store a reference to this in the formset element\n this.$formset.data(pluginName, this);\n\n var extras = ['animateForms'];\n $.each(extras, function (i, extra) {\n if ((extra in _this.opts) && (_this.opts[extra])) {\n _this[extra]();\n }\n });\n }\n static getOrCreate(el, options) {\n var rev = $(el).data(pluginName);\n if (!rev) {\n rev = new Formset(el, options);\n }\n\n return rev;\n }\n addForm() {\n // Don't proceed if the number of maximum forms has been reached\n if (this.hasMaxForms()) {\n throw new Error(\"MAX_NUM_FORMS reached\");\n }\n\n var newIndex = this.totalFormCount();\n this.$managementForm('TOTAL_FORMS').val(newIndex + 1);\n\n var newFormHtml = this.$emptyForm.html()\n .replace(new RegExp('__prefix__', 'g'), newIndex)\n .replace(new RegExp('<\\\\\\\\/script>', 'g'), '');\n\n var $newFormFragment = $($.parseHTML(newFormHtml, this.$body.document, true));\n this.$body.append($newFormFragment);\n\n var $newForm = $newFormFragment.filter(this.opts.form);\n this.bindForm($newForm, newIndex);\n if (this.opts.newFormCallback) {\n this.opts.newFormCallback($newForm);\n }\n return $newForm;\n }\n /**\n * Attach any events needed to a new form\n */\n bindForm($form, index) {\n var prefix = this.formsetPrefix + '-' + index;\n $form.data(pluginName + '__formPrefix', prefix);\n\n var $delete = $form.find('[name=' + prefix + '-DELETE]');\n\n // Trigger `formAdded` / `formDeleted` events when delete checkbox value changes\n $delete.change(function (event) {\n if ($delete.is(':checked')) {\n $form.attr('data-formset-form-deleted', '');\n // Remove required property and pattern attribute to allow submit, back it up to data field\n $form.find(':required').data(pluginName + '-required-field', true).prop('required', false);\n $form.find('input[pattern]').each(function () {\n var pattern = $(this).attr('pattern');\n $(this).data(pluginName + '-field-pattern', pattern).removeAttr('pattern');\n });\n $form.trigger('formDeleted');\n } else {\n $form.removeAttr('data-formset-form-deleted');\n // Restore required property and pattern attributes from data field\n $form.find('*').filter(function () {\n return $(this).data(pluginName + '-required-field') === true;\n }).prop('required', true);\n $form.find('input').each(function () {\n var pattern = $(this).data(pluginName + '-field-pattern');\n if (pattern) {\n $(this).attr('pattern', pattern);\n }\n });\n $form.trigger('formAdded');\n }\n }).trigger('change');\n\n var $deleteButton = $form.find(this.opts.deleteButton);\n\n $deleteButton.bind('click', function () {\n $delete.attr('checked', true).change();\n });\n }\n $forms() {\n return this.$body.find(this.opts.form);\n }\n $managementForm(name) {\n return this.$formset.find('[name=' + this.formsetPrefix + '-' + name + ']');\n }\n totalFormCount() {\n return this.$forms().length;\n }\n deletedFormCount() {\n return this.$forms().filter('[data-formset-form-deleted]').length;\n }\n clear() {\n this.$forms().each(function (key, row) {\n $(row).find(':input').not(':button, :submit, :reset, :hidden, :checkbox, :radio').val('');\n });\n }\n deleteAll() {\n this.$forms().each(function (key, row) {\n $(row).find('[deletecheckbox]').prop(\"checked\", true).change();\n });\n }\n activeFormCount() {\n return this.totalFormCount() - this.deletedFormCount();\n }\n openRownum() {\n var rownum = this.$formset.find('tr.row-open').attr('data-rownum');\n return typeof rownum != undefined ? parseInt(rownum) : 0;\n }\n hasMaxForms() {\n var maxForms = parseInt(this.$managementForm('MAX_NUM_FORMS').val(), 10) || 1000;\n return this.activeFormCount() >= maxForms;\n }\n checkMaxForms() {\n if (this.hasMaxForms()) {\n this.$formset.addClass(this.opts.hasMaxFormsClass);\n this.$add.attr('disabled', 'disabled');\n } else {\n this.$formset.removeClass(this.opts.hasMaxFormsClass);\n this.$add.removeAttr('disabled');\n }\n }\n animateForms() {\n this.$formset.on('formAdded', this.opts.form, function () {\n var $form = $(this);\n $form.slideUp(0);\n $form.slideDown();\n }).on('formDeleted', this.opts.form, function () {\n var $form = $(this);\n $form.slideUp();\n });\n this.$forms().filter('[data-formset-form-deleted]').slideUp(0);\n }\n}\n\nFormset.defaults = {\n form: '[data-formset-form]',\n emptyForm: 'script[type=form-template][data-formset-empty-form]',\n body: '[data-formset-body]',\n add: '[data-formset-add]',\n deleteButton: '[data-formset-delete-button]',\n hasMaxFormsClass: 'has-max-forms',\n animateForms: false,\n newFormCallback: false\n};\n\nexport default Formset;\n","import { uuidv4, templatePacks } from \"./utils\";\n\nconst modalDefault = {\n placement: \"center\",\n size: \"md\",\n templatePack: null,\n onHide: () => {},\n onShow: () => {},\n onToggle: () => {},\n};\n\nclass Modal {\n constructor(targetEl = null, options = {}) {\n this._targetEl = targetEl;\n this._parentEl = targetEl.parentElement;\n this._options = {\n ...modalDefault,\n ...options,\n };\n this._isHidden = true;\n this._init();\n this._addEventListeners();\n }\n _init() {\n this._getPlacementClasses().map((c) => {\n this._targetEl.classList.add(c);\n });\n this._clearSize();\n this._getSizeClasses().map((c) => {\n this._targetEl.firstElementChild.classList.add(c);\n });\n }\n _createBackdrop(id) {\n if (this._isHidden) {\n const backdropEl = document.createElement(\"div\");\n backdropEl.setAttribute(\"data-ref-id\", id);\n backdropEl.setAttribute(\"modal-backdrop\", \"\");\n backdropEl.classList.add(...this._getClasses(\"backdrop\"));\n this._parentEl.append(backdropEl);\n backdropEl.offsetWidth;\n backdropEl.classList.add(...this._getClasses(\"opacity50\"));\n }\n }\n _destroyBackdropEl() {\n if (!this._isHidden) {\n let id = this._targetEl.getAttribute(\"data-ref-id\");\n document.querySelector(`[modal-backdrop][data-ref-id=\"${id}\"]`).remove();\n }\n }\n _getPlacementClasses() {\n let that = this;\n switch (this._options.placement) {\n // top\n case \"top-left\":\n return [\n that._getClasses(\"justifyStart\", false),\n that._getClasses(\"itemsStart\", false),\n ];\n case \"top-center\":\n return [\n that._getClasses(\"justifyCenter\", false),\n that._getClasses(\"itemsStart\", false),\n ];\n case \"top-right\":\n return [\n that._getClasses(\"justifyEnd\", false),\n that._getClasses(\"itemsStart\", false),\n ];\n\n // center\n case \"center-left\":\n return [\n that._getClasses(\"justifyStart\", false),\n that._getClasses(\"itemsCenter\", false),\n ];\n case \"center\":\n return [\n that._getClasses(\"justifyCenter\", false),\n that._getClasses(\"itemsCenter\", false),\n ];\n case \"center-right\":\n return [\n that._getClasses(\"justifyEnd\", false),\n that._getClasses(\"itemsCenter\", false),\n ];\n\n // bottom\n case \"bottom-left\":\n return [\n that._getClasses(\"justifyStart\", false),\n that._getClasses(\"itemsEnd\", false),\n ];\n case \"bottom-center\":\n return [\n that._getClasses(\"justifyCenter\", false),\n that._getClasses(\"itemsEnd\", false),\n ];\n case \"bottom-right\":\n return [\n that._getClasses(\"justifyEnd\", false),\n that._getClasses(\"itemsEnd\", false),\n ];\n\n default:\n return [\n that._getClasses(\"justifyCenter\", false),\n that._getClasses(\"itemsCenter\", false),\n ];\n }\n }\n _getSizeClasses() {\n return templatePacks[this._options.templatePack].sizes[\n this._options.size\n ].split(\" \");\n }\n _getClasses(name, str = false) {\n let names = templatePacks[this._options.templatePack].classes[name];\n return str ? names : names.split(\" \");\n }\n _clearSize() {\n const element = this._targetEl.firstElementChild;\n const obj = templatePacks[this._options.templatePack].sizes;\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n element.classList.remove(obj[key]);\n }\n }\n }\n _addEventListeners() {\n let that = this;\n this._targetEl.addEventListener(\"keyup\", function (e) {\n if (e.key === \"Escape\") {\n that.hide();\n }\n });\n }\n toggle() {\n if (this._isHidden) {\n this.show();\n } else {\n this.hide();\n }\n\n // callback function\n this._options.onToggle(this);\n }\n show() {\n const id = uuidv4();\n this._targetEl.classList.add(...this._getClasses(\"flex\"));\n this._targetEl.classList.remove(...this._getClasses(\"hidden\"));\n this._targetEl.setAttribute(\"aria-modal\", \"true\");\n this._targetEl.setAttribute(\"role\", \"dialog\");\n this._targetEl.removeAttribute(\"aria-hidden\");\n this._targetEl.setAttribute(\"data-ref-id\", id);\n this._createBackdrop(id);\n this._isHidden = false;\n\n document.body.classList.add(\"modal-open\");\n\n // callback function\n this._options.onShow(this);\n\n let firstEl = this._targetEl.querySelector(\n 'select, input:not([type=\"hidden\"]'\n );\n if (firstEl) {\n firstEl.setAttribute(\"tabindex\", \"0\");\n firstEl.focus();\n }\n this._targetEl.offsetWidth;\n this._targetEl.classList.add(...this._getClasses(\"opacity100\"));\n }\n hide() {\n this._targetEl.classList.add(...this._getClasses(\"hidden\"));\n this._targetEl.classList.remove(...this._getClasses(\"flex\"));\n this._targetEl.setAttribute(\"aria-hidden\", \"true\");\n this._targetEl.removeAttribute(\"aria-modal\");\n this._targetEl.removeAttribute(\"role\");\n this._destroyBackdropEl();\n this._isHidden = true;\n\n document.body.classList.remove(\"modal-open\");\n\n // callback function\n this._options.onHide(this);\n }\n}\n\nexport default Modal;\n","import Modal from \"./modal\";\nimport { gettext } from \"./utils\";\n\nconst modalFormOptions = {\n parent: null,\n modalId: null,\n onKeyUp: function () {},\n onClose: function () {},\n onOpen: function () {},\n};\n\nclass ModalForm {\n constructor(targetEl, options = {}) {\n this.targetEl = targetEl;\n this._options = {\n ...modalFormOptions,\n ...options,\n };\n this.modalId = this._options.modalId;\n this._modalEl = null;\n this._modalTitleEl = null;\n this._modalDeleteBt = null;\n this.modalInstance = false;\n this.rownum = null;\n this._init();\n this._addEvents();\n }\n _init() {\n this._modalEl = document.getElementById(this.modalId);\n this._modalTitleEl = this._modalEl.querySelector(\".modal-title\");\n this._modalDeleteBt = this._modalEl.querySelector(\".formset-delete\");\n this._createModal();\n }\n _addEvents() {\n let that = this;\n this._modalEl\n .querySelectorAll('[data-formset-modal-toggle=\"' + this.modalId + '\"]')\n .forEach(function (el) {\n el.addEventListener(\"click\", function () {\n that.close();\n });\n });\n }\n hasFieldError(fieldId) {\n let fieldWrapper = this.targetEl.querySelector(`#div_${fieldId}`);\n let hasError = false;\n let errorText = \"\";\n fieldWrapper.querySelectorAll(\".field-error\").forEach(function (el) {\n hasError = true;\n errorText = el.innerText;\n });\n return { error: hasError, text: errorText };\n }\n hasNonFieldError() {\n return this.targetEl.querySelector(\".non-field-errors\") != null;\n }\n _hiddeDefaultDeleteBt(el) {\n let parentEl = el.parentNode;\n parentEl.classList.add(\"d-none\");\n }\n _createModal() {\n let that = this;\n if (!this.modalInstance) {\n let deleteBt = this.targetEl.querySelector(\".formset-delete\");\n let modal = new Modal(this._modalEl, {\n placement: that._options.placement,\n size: that._options.size,\n templatePack: that._options.templatePack,\n onHide: function (modal) {\n that._onModalClose(modal);\n },\n onShow: function (modal) {\n that._onModalShow(modal);\n },\n });\n modal._targetEl.addEventListener(\"keyup\", function (e) {\n that._options.onKeyUp(e, that);\n });\n this.modalInstance = modal;\n this._hiddeDefaultDeleteBt(deleteBt);\n }\n }\n _onModalShow(modal) {\n this._options.onOpen(this);\n let openRowBt = this._options.parent.targetEl.querySelector(\n \".btn-open-row[data-formset-modal-toggle='\" + this.modalId + \"']\"\n );\n let tr = openRowBt.closest(\"tr\");\n this._modalTitleEl.innerText = gettext(\"Editing row #\") + this.rownum;\n tr.classList.add(\"row-open\");\n }\n _onModalClose(modal) {\n this._options.onClose(this);\n let openRowBt = this._options.parent.targetEl.querySelector(\n \".btn-open-row[data-formset-modal-toggle='\" + this.modalId + \"']\"\n );\n if (openRowBt) {\n let tr = openRowBt.closest(\"tr\");\n tr.classList.remove(\"row-open\");\n }\n }\n isDeleted() {\n return this.targetEl.hasAttribute(\"data-formset-form-deleted\");\n }\n isSelected() {\n return this.targetEl.hasAttribute(\"data-formset-form-selected\");\n }\n open() {\n this.modalInstance.show();\n }\n close() {\n this.modalInstance.hide();\n }\n}\n\nexport default ModalForm;\n","import $ from \"jquery\";\nimport ModalForm from \"./modal-form\";\nimport {\n executeAllCalculatedFields,\n getNumberValue,\n getTextValue,\n gettext,\n hookCalculatedFields,\n removeChildren,\n templatePacks,\n uuidv4,\n} from \"./utils\";\n\nconst variant = {\n tabular: \"tabular\",\n stacked: \"stacked\",\n modal: \"modal\",\n};\n\nconst formsetOptions = {\n editText: gettext(\"Edit\"),\n};\n\nclass FormsetModal {\n constructor(elementId) {\n this._id = elementId;\n this.targetEl = document.getElementById(this._id);\n this.variant = variant.tabular;\n this.$formset = null;\n this._options = {\n ...formsetOptions,\n };\n this._modalForms = [];\n this._deleteBt = null;\n this._table = null;\n this._tbody = null;\n this._tfoot = null;\n this._init();\n this._addEvents();\n }\n _init() {\n this.variant = this._getVariant();\n this.templatePack = this._getTemplatePack();\n this.modalSize = this._getModalSize();\n this.modalPlacement = this._getModalPlacement();\n // Create Formset Helper Instance\n this.$formset = $(`#${this._id}`);\n this.$formset.formset({\n animateForms: true,\n });\n if (this.variant === variant.modal || this.variant == variant.tabular) {\n this._table = this.targetEl.querySelector(\"table\");\n this._tbody = this._table.querySelector(\"tbody\");\n this._tfoot = this._table.querySelector(\"tfoot\");\n this._emptyState = this._tbody.innerHTML;\n }\n if (this.variant === variant.modal) {\n this._deleteBt = this.targetEl.querySelector(\".delete-selected\");\n this._checkInitials();\n this._configureSelectAllToggler();\n this._refresh();\n }\n }\n _getVariant() {\n return this.targetEl.getAttribute(\"data-formset-variant\");\n }\n _getTemplatePack() {\n return this.targetEl.getAttribute(\"data-template-pack\");\n }\n _getClasses(name) {\n return templatePacks[this.templatePack].classes[name].split(\" \");\n }\n _getPencilIcon(){\n return templatePacks[this.templatePack].pencilIcon;\n }\n _getModalSize() {\n return this.targetEl.getAttribute(\"data-modal-size\");\n }\n _getModalPlacement() {\n return this.targetEl.getAttribute(\"data-modal-placement\");\n }\n _getModalFormInstanceByRownum(rownum) {\n let instance = false;\n this._modalForms.forEach(function (obj) {\n if (obj.rownum === rownum && !obj.isDeleted()) {\n instance = obj;\n }\n });\n return instance;\n }\n _addEvents() {\n let that = this;\n this.$formset.on(\"formAdded\", function (e) {\n that._onFormsetAdded(e);\n });\n this.$formset.on(\"formDeleted\", function (e) {\n that._onFormsetDeleted(e);\n });\n }\n /**\n * Check if the page loaded form from the server to configure them.\n */\n _checkInitials() {\n let that = this;\n let modals = [];\n this.targetEl\n .querySelectorAll(\"[data-formset-modal-toggle]\")\n .forEach(function (el) {\n let targetEl = el.closest(\"[data-formset-form]\");\n let modalId = el.getAttribute(\"data-formset-modal-toggle\");\n if (!modals.includes(modalId)) {\n that._newModalForm(targetEl, modalId);\n }\n modals.push(modalId);\n });\n }\n _newModalForm(targetEl, modalId) {\n let that = this;\n let options = {\n parent: that,\n modalId: modalId,\n size: that.modalSize,\n placement: that.modalPlacement,\n templatePack: that.templatePack,\n onKeyUp: function (e, modalForm) {\n that._onModalFormKeyUp(e, modalForm);\n },\n onOpen: function (modalForm) {\n that._onModalFormOpen(modalForm);\n },\n onClose: function (modalForm) {\n that._onModalFormClose(modalForm);\n },\n };\n let modalForm = new ModalForm(targetEl, options);\n that._modalForms.push(modalForm);\n return modalForm;\n }\n _onFormsetAdded(e) {\n if (this.variant === variant.modal) {\n let id = uuidv4();\n $(this.targetEl).find(\"#__dialog_id__\").attr(\"id\", id);\n $(this.targetEl)\n .find('[data-formset-modal-toggle=\"__dialog_id__\"]')\n .attr(\"data-formset-modal-toggle\", id);\n let modalForm = this._newModalForm(e.target, id);\n modalForm.open();\n }\n if (window.hasOwnProperty(\"calculatedFields\")) {\n window.resetCalculatedFields();\n hookCalculatedFields();\n }\n }\n _onFormsetDeleted() {\n if (this.variant == variant.modal) {\n this._refresh();\n }\n executeAllCalculatedFields();\n }\n _onModalFormKeyUp(e, modalForm) {\n if (e.ctrlKey && (e.keyCode === 38 || e.keyCode === 40)) {\n e.preventDefault();\n let openRownum = modalForm.rownum;\n let $formset = this.$formset.formset(\"getOrCreate\");\n let activeFormCount = $formset.activeFormCount();\n if (e.keyCode == 38) {\n // Up\n if (openRownum > 1) {\n modalForm.close();\n let previewsModalForm = this._getModalFormInstanceByRownum(\n openRownum - 1\n );\n previewsModalForm.open();\n }\n }\n\n if (e.keyCode === 40) {\n // Down\n if (openRownum < activeFormCount) {\n modalForm.close();\n let nextsModalForm = this._getModalFormInstanceByRownum(\n openRownum + 1\n );\n nextsModalForm.open();\n }\n if (openRownum == activeFormCount) {\n modalForm.close();\n $formset.addForm();\n }\n }\n }\n }\n _onModalFormOpen(modalForm) {\n this._refresh();\n }\n _onModalFormClose(modalForm) {\n this._refresh();\n }\n _configureSelectAllToggler() {\n let that = this;\n let table = this._table;\n let toggler = this.targetEl.querySelector(\".select-all\");\n let deleteBt = this.targetEl.querySelector(\".delete-selected\");\n\n toggler.addEventListener(\"change\", function (e) {\n let checked = toggler.checked;\n let checkBoxes = table.querySelectorAll(\".select-row\");\n checkBoxes.forEach(function (checkbox) {\n let tr = checkbox.closest(\"tr\");\n let td = checkbox.closest(\"td\");\n let btnOpenRow = tr.querySelector(\"[data-formset-modal-toggle]\");\n let modalId = btnOpenRow.getAttribute(\"data-formset-modal-toggle\");\n let modalEl = document.getElementById(modalId);\n let formsetFormEl = modalEl.closest(\"[data-formset-form]\");\n checkbox.checked = checked;\n that._checker(tr, td, formsetFormEl, checkbox);\n });\n });\n\n deleteBt.addEventListener(\"click\", function (e) {\n let formset = that.targetEl;\n let selectedForms = formset.querySelectorAll(\n \"[data-formset-form-selected]\"\n );\n selectedForms.forEach(function (selectedForm) {\n let deleteCheckbox = selectedForm.querySelector(\".formset-delete\");\n deleteCheckbox.checked = true;\n deleteCheckbox.dispatchEvent(new Event(\"change\"));\n });\n });\n }\n _checkSelectAllState() {\n let table = this._table;\n let formset = this.targetEl;\n let deleteBt = formset.querySelector(\".delete-selected\");\n let showDeleteBt = false;\n let selectAllToggler = table.querySelector(\".select-all\");\n let recordCount = table.querySelectorAll(\"tbody tr\").length;\n let selected = table.querySelectorAll(\"tr.selected\").length;\n\n if (selected === recordCount && recordCount > 0) {\n selectAllToggler.checked = true;\n selectAllToggler.indeterminate = false;\n showDeleteBt = true;\n } else {\n if (selected != recordCount && selected > 0) {\n selectAllToggler.indeterminate = true;\n selectAllToggler.checked = false;\n showDeleteBt = true;\n }\n if (selected === 0) {\n selectAllToggler.checked = false;\n selectAllToggler.indeterminate = false;\n showDeleteBt = false;\n }\n }\n\n if (showDeleteBt) {\n deleteBt.classList.remove(...this._getClasses(\"hidden\"));\n deleteBt.classList.add(...this._getClasses(\"inlineFlex\"));\n } else {\n deleteBt.classList.remove(...this._getClasses(\"inlineFlex\"));\n deleteBt.classList.add(...this._getClasses(\"hidden\"));\n }\n }\n _checker(tr, td, formsetFormEl, checkbox) {\n // remove previous divSel if exists.\n let prevDivSel = td.querySelector(\".cfm-selection-border, .selection-border\");\n if (prevDivSel) {\n prevDivSel.remove();\n }\n if (checkbox.checked) {\n let divSel = document.createElement(\"div\");\n td.classList.add(...this._getClasses(\"relative\"));\n divSel.classList.add(...this._getClasses(\"selectionMark\"));\n td.prepend(divSel);\n formsetFormEl.setAttribute(\"data-formset-form-selected\", \"selected\");\n tr.classList.add(\"selected\");\n } else {\n checkbox.classList.remove(...this._getClasses(\"relative\"));\n tr.classList.remove(\"selected\");\n formsetFormEl.removeAttribute(\"data-formset-form-selected\");\n }\n this._checkSelectAllState();\n }\n _refresh() {\n let that = this;\n let fields = {};\n let fieldNames = [];\n let rows = [];\n\n this._table.querySelectorAll(\"[data-field-name]\").forEach(function (el) {\n fields[el.getAttribute(\"data-field-name\")] = {\n type: el.getAttribute(\"data-field-type\"),\n hasSummary: el.hasAttribute(\"data-field-summary\"),\n summary: 0,\n };\n });\n\n fieldNames = Object.keys(fields);\n\n this._modalForms.forEach(function (modalForm) {\n if (!modalForm.isDeleted()) {\n let row = {};\n modalForm.targetEl\n .querySelectorAll(\"input, select\")\n .forEach(function (el) {\n let match = el.name.match(\n /(?