From 6e2aa084e8eeca98c836dfcc3c77bd5359a68ac1 Mon Sep 17 00:00:00 2001 From: Horilla Date: Fri, 25 Oct 2024 15:34:12 +0530 Subject: [PATCH] [UPDT] BASE: Holiday import by adding csv file import option --- base/methods.py | 11 + .../base/action_type/action_type_view.html | 2 +- .../base/department/department_view.html | 2 +- .../base/employee_type/type_view.html | 2 +- .../base/job_position/job_position_view.html | 2 +- .../base/job_role/job_role_view.html | 2 +- .../rotating_work_type_view.html | 2 +- base/templates/base/shift/schedule_view.html | 2 +- base/templates/base/shift/shift_view.html | 2 +- base/templates/base/tags/tags_view.html | 2 +- .../base/work_type/work_type_view.html | 2 +- base/views.py | 252 +- employee/scheduler.py | 2 +- .../navbar_component/profile_section.html | 2 +- employee/templates/tabs/attendance-tab.html | 2 +- employee/templates/tabs/document_tab.html | 2 +- horilla/horilla_settings.py | 5 + horilla/rest_conf.py | 51 +- static/htmx/htmx.min.js | 5155 ++++++++++++++++- static/images/ui/server error.png | Bin 0 -> 21027 bytes static/images/ui/server.png | Bin 0 -> 23211 bytes static/index/index.js | 48 +- templates/demo_database/auth_load_data.html | 2 +- templates/import_popup.html | 2 - templates/settings.html | 2 +- templates/sidebar.html | 2 +- 26 files changed, 5419 insertions(+), 139 deletions(-) create mode 100644 static/images/ui/server error.png create mode 100644 static/images/ui/server.png diff --git a/base/methods.py b/base/methods.py index 9384665e3..9c6ac2a81 100644 --- a/base/methods.py +++ b/base/methods.py @@ -845,3 +845,14 @@ def get_next_month_same_date(date_obj): total_days_in_month = calendar.monthrange(year, month)[1] day = min(day, total_days_in_month) return date(day=day, month=month, year=year) + + +def format_date(date_str): + # List of possible date formats to try + + for format_name, format_string in HORILLA_DATE_FORMATS.items(): + try: + return datetime.strptime(date_str, format_string).strftime("%Y-%m-%d") + except ValueError: + continue + raise ValueError(f"Invalid date format: {date_str}") diff --git a/base/templates/base/action_type/action_type_view.html b/base/templates/base/action_type/action_type_view.html index b6428fed5..015cb6c3a 100644 --- a/base/templates/base/action_type/action_type_view.html +++ b/base/templates/base/action_type/action_type_view.html @@ -40,7 +40,7 @@
+ hx-swap="outerHTML" class="w-50"> {% csrf_token %}
diff --git a/employee/templates/tabs/document_tab.html b/employee/templates/tabs/document_tab.html index b3822c700..e5d625ed3 100644 --- a/employee/templates/tabs/document_tab.html +++ b/employee/templates/tabs/document_tab.html @@ -133,7 +133,7 @@
diff --git a/horilla/horilla_settings.py b/horilla/horilla_settings.py index e46ab3171..aa019aeb4 100644 --- a/horilla/horilla_settings.py +++ b/horilla/horilla_settings.py @@ -1,3 +1,5 @@ +from django.core.files.storage import FileSystemStorage + from horilla import settings """ @@ -10,6 +12,7 @@ DB_INIT_PASSWORD = "d3f6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d" HORILLA_DATE_FORMATS = { + "DD/MM/YY": "%d/%m/%y", "DD-MM-YYYY": "%d-%m-%Y", "DD.MM.YYYY": "%d.%m.%Y", "DD/MM/YYYY": "%d/%m/%Y", @@ -34,6 +37,8 @@ BIO_DEVICE_THREADS = {} DYNAMIC_URL_PATTERNS = [] +FILE_STORAGE = FileSystemStorage(location="csv_tmp/") + APP_URLS = [ "base.urls", "employee.urls", diff --git a/horilla/rest_conf.py b/horilla/rest_conf.py index a9c08e10b..158aba7b5 100644 --- a/horilla/rest_conf.py +++ b/horilla/rest_conf.py @@ -1,49 +1,46 @@ """ rest_conf.py """ -from horilla import settings -from horilla.settings import INSTALLED_APPS + from datetime import timedelta +from horilla import settings +from horilla.settings import INSTALLED_APPS # Injecting installed apps to settings -REST_APPS = ["rest_framework", - "rest_framework_simplejwt", - "drf_yasg", - "horilla_api" - ] +REST_APPS = ["rest_framework", "rest_framework_simplejwt", "drf_yasg", "horilla_api"] INSTALLED_APPS.extend(REST_APPS) REST_FRAMEWORK_SETTINGS = { - 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework_simplejwt.authentication.JWTAuthentication', + "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", ), - 'PAGE_SIZE': 20, + "PAGE_SIZE": 20, } SIMPLE_JWT = { - 'ACCESS_TOKEN_LIFETIME': timedelta(days=30), + "ACCESS_TOKEN_LIFETIME": timedelta(days=30), } SWAGGER_SETTINGS = { - 'SECURITY_DEFINITIONS': { - 'Bearer': { - 'type': 'apiKey', - 'name': 'Authorization', - 'in': 'header', - 'description': 'Enter your Bearer token here', + "SECURITY_DEFINITIONS": { + "Bearer": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "description": "Enter your Bearer token here", + }, + "Basic": { + "type": "basic", + "description": "Basic authentication. Enter your username and password.", }, - 'Basic': { - 'type': 'basic', - 'description': 'Basic authentication. Enter your username and password.', - } }, - 'SECURITY': [{'Bearer': []}, {'Basic': []}], + "SECURITY": [{"Bearer": []}, {"Basic": []}], } # Inject the REST framework settings into the Django project settings -setattr(settings, 'REST_FRAMEWORK', REST_FRAMEWORK_SETTINGS) -setattr(settings, 'SIMPLE_JWT', SIMPLE_JWT) -setattr(settings, 'SWAGGER_SETTINGS', SWAGGER_SETTINGS) +setattr(settings, "REST_FRAMEWORK", REST_FRAMEWORK_SETTINGS) +setattr(settings, "SIMPLE_JWT", SIMPLE_JWT) +setattr(settings, "SWAGGER_SETTINGS", SWAGGER_SETTINGS) diff --git a/static/htmx/htmx.min.js b/static/htmx/htmx.min.js index 84f9f943b..e7613d151 100644 --- a/static/htmx/htmx.min.js +++ b/static/htmx/htmx.min.js @@ -1 +1,5154 @@ -(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var v={onLoad:b,process:Ye,on:P,off:U,trigger:it,ajax:jt,find:S,findAll:E,closest:D,remove:C,addClass:A,removeClass:L,toggleClass:O,takeClass:T,defineExtension:$t,removeExtension:Jt,logAll:w,logger:null,config:{historyEnabled:true,historyCacheSize:10,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:100,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,attributesToSettle:["class","style","width","height"]},parseInterval:f,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){return new WebSocket(e,[])}};var t=["get","post","put","delete","patch"];var n=t.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function f(e){if(e==undefined){return undefined}if(e.slice(-2)=="ms"){return parseFloat(e.slice(0,-2))||undefined}if(e.slice(-1)=="s"){return parseFloat(e.slice(0,-1))*1e3||undefined}return parseFloat(e)||undefined}function l(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function R(e,t){return l(e,t)||l(e,"data-"+t)}function s(e){return e.parentElement}function q(){return document}function c(e,t){if(t(e)){return e}else if(s(e)){return c(s(e),t)}else{return null}}function H(e,t){var r=null;c(e,function(e){return r=R(e,t)});return r}function h(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function r(e){var t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;var r=t.exec(e);if(r){return r[1].toLowerCase()}else{return""}}function i(e,t){var r=new DOMParser;var n=r.parseFromString(e,"text/html");var i=n.body;while(t>0){t--;i=i.firstChild}if(i==null){i=q().createDocumentFragment()}return i}function d(e){var t=r(e);switch(t){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return i(""+e+"
",1);case"col":return i(""+e+"
",2);case"tr":return i(""+e+"
",2);case"td":case"th":return i(""+e+"
",3);case"script":return i("
"+e+"
",1);default:return i(e,0)}}function a(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function u(e){return a(e,"Function")}function g(e){return a(e,"Object")}function N(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function p(e){var t=[];if(e){for(var r=0;r=0}function I(e){return q().body.contains(e)}function y(e){return e.trim().split(/\s+/)}function M(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function x(e){try{return JSON.parse(e)}catch(e){nt(e);return null}}function e(e){return Mt(q().body,function(){return eval(e)})}function b(t){var e=v.on("htmx:load",function(e){t(e.detail.elt)});return e}function w(){v.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function S(e,t){if(t){return e.querySelector(t)}else{return S(q(),e)}}function E(e,t){if(t){return e.querySelectorAll(t)}else{return E(q(),e)}}function C(e,t){e=X(e);if(t){setTimeout(function(){C(e)},t)}else{e.parentElement.removeChild(e)}}function A(e,t,r){e=X(e);if(r){setTimeout(function(){A(e,t)},r)}else{e.classList.add(t)}}function L(e,t,r){e=X(e);if(r){setTimeout(function(){L(e,t)},r)}else{e.classList.remove(t)}}function O(e,t){e=X(e);e.classList.toggle(t)}function T(e,t){e=X(e);k(e.parentElement.children,function(e){L(e,t)});A(e,t)}function D(e,t){e=X(e);do{if(e==null||h(e,t))return e}while(e=e&&s(e))}function X(e){if(a(e,"String")){return S(e)}else{return e}}function F(e,t,r){if(u(t)){return{target:q().body,event:e,listener:t}}else{return{target:X(e),event:t,listener:r}}}function P(t,r,n){Gt(function(){var e=F(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=u(r);return e?r:n}function U(t,r,n){Gt(function(){var e=F(t,r,n);e.target.removeEventListener(e.event,e.listener)});return u(r)?r:n}function z(e){var t=c(e,function(e){return R(e,"hx-target")!==null});if(t){var r=R(t,"hx-target");if(r==="this"){return t}else if(r.indexOf("closest ")===0){return D(e,r.substr(8))}else if(r.indexOf("find ")===0){return S(e,r.substr(5))}else{return q().querySelector(r)}}else{var n=N(e);if(n.boosted){return q().body}else{return e}}}function j(e){var t=v.config.attributesToSettle;for(var r=0;r0){i=e.substr(0,e.indexOf(":"));n=e.substr(e.indexOf(":")+1,e.length)}else{i=e}var a=q().querySelector(n);if(a){var o;o=q().createDocumentFragment();o.appendChild(t);if(!_(i,a)){o=t}oe(i,a,a,o,r)}else{t.parentNode.removeChild(t);et(q().body,"htmx:oobErrorNoTarget",{content:t})}return e}function B(e,r){k(E(e,"[hx-swap-oob], [data-hx-swap-oob]"),function(e){var t=R(e,"hx-swap-oob");if(t!=null){W(t,e,r)}})}function $(e){k(E(e,"[hx-preserve], [data-hx-preserve]"),function(e){var t=R(e,"id");var r=q().getElementById(t);e.parentNode.replaceChild(r,e)})}function J(n,e,i){k(e.querySelectorAll("[id]"),function(e){if(e.id&&e.id.length>0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();V(e,t);i.tasks.push(function(){V(e,r)})}}})}function Z(e){return function(){Ye(e);$e(e);G(e);it(e,"htmx:load")}}function G(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function Y(e,t,r,n){J(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Z(i))}}}function K(e){var t=N(e);if(t.webSocket){t.webSocket.close()}if(t.sseEventSource){t.sseEventSource.close()}if(e.children){k(e.children,function(e){K(e)})}}function Q(e,t,r){if(e.tagName==="BODY"){return ie(e,t)}else{var n=e.previousSibling;Y(s(e),e,t,r);if(n==null){var i=s(e).firstChild}else{var i=n.nextSibling}N(e).replacedWith=i;while(i&&i!==e){if(i.nodeType===Node.ELEMENT_NODE){r.elts.push(i)}i=i.nextElementSibling}K(e);s(e).removeChild(e)}}function ee(e,t,r){return Y(e,e.firstChild,t,r)}function te(e,t,r){return Y(s(e),e,t,r)}function re(e,t,r){return Y(e,null,t,r)}function ne(e,t,r){return Y(s(e),e.nextSibling,t,r)}function ie(e,t,r){var n=e.firstChild;Y(e,n,t,r);if(n){while(n.nextSibling){K(n.nextSibling);e.removeChild(n.nextSibling)}K(n);e.removeChild(n)}}function ae(e,t){var r=H(e,"hx-select");if(r){var n=q().createDocumentFragment();k(t.querySelectorAll(r),function(e){n.appendChild(e)});t=n}return t}function oe(e,t,r,n,i){switch(e){case"none":return;case"outerHTML":Q(r,n,i);return;case"afterbegin":ee(r,n,i);return;case"beforebegin":te(r,n,i);return;case"beforeend":re(r,n,i);return;case"afterend":ne(r,n,i);return;default:var a=Zt(t);for(var o=0;o([\s\S]+?)<\/title>/im;function se(e){var t=ue.exec(e);if(t){return t[1]}}function le(e,t,r,n,i){var a=se(n);if(a){var o=S("title");if(o){o.innerHTML=a}else{window.document.title=a}}var u=d(n);if(u){B(u,i);$(u);u=ae(r,u);return oe(e,r,t,u,i)}}function fe(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=x(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!g(o)){o={value:o}}it(r,a,o)}}}else{it(r,n,[])}}var ce=/\s/;var he=/[\s,]/;var ve=/[_$a-zA-Z]/;var de=/[_$a-zA-Z0-9]/;var ge=['"',"'","/"];var pe=/[^\s]/;function me(e){var t=[];var r=0;while(r0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var u=Mt(e,function(){return Function(i)()},function(){return true});u.source=i;return u}catch(e){et(q().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(ye(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function be(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}function we(e){var t=R(e,"hx-trigger");var r=[];if(t){var n=me(t);do{be(n,pe);var i=n.length;var a=be(n,/[,\[\s]/);if(a!==""){if(a==="every"){var o={trigger:"every"};be(n,pe);o.pollInterval=f(be(n,ce));r.push(o)}else if(a.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:a.substr(4)})}else{var u={trigger:a};var s=xe(e,n,"event");if(s){u.eventFilter=s}while(n.length>0&&n[0]!==","){be(n,pe);var l=n.shift();if(l==="changed"){u.changed=true}else if(l==="once"){u.once=true}else if(l==="delay"&&n[0]===":"){n.shift();u.delay=f(be(n,he))}else if(l==="from"&&n[0]===":"){n.shift();u.from=be(n,he)}else if(l==="throttle"&&n[0]===":"){n.shift();u.throttle=f(be(n,he))}else{et(e,"htmx:syntax:error",{token:n.shift()})}}r.push(u)}}if(n.length===i){et(e,"htmx:syntax:error",{token:n.shift()})}be(n,pe)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,"input, textarea, select")){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Se(e){N(e).cancelled=true}function Ee(e,t,r,n){var i=N(e);i.timeout=setTimeout(function(){if(I(e)&&i.cancelled!==true){Vt(t,r,e);Ee(e,t,R(e,"hx-"+t),n)}},n)}function Ce(e){return location.hostname===e.hostname&&l(e,"href")&&l(e,"href").indexOf("#")!==0}function Ae(t,r,e){if(t.tagName==="A"&&Ce(t)||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=l(t,"href")}else{var a=l(t,"method");n=a?a.toLowerCase():"get";i=l(t,"action")}e.forEach(function(e){Re(t,n,i,r,e,true)})}}function Le(e){return e.tagName==="FORM"||h(e,'input[type="submit"], button')&&D(e,"form")!==null||e.tagName==="A"&&e.href&&e.href.indexOf("#")!==0}function Oe(e,t){return N(e).boosted&&e.tagName==="A"&&t.type==="click"&&t.ctrlKey}function Te(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){et(q().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function Re(n,i,a,e,o,u){var s=n;if(o.from){s=S(o.from)}var l=function(e){if(!I(n)){s.removeEventListener(o.trigger,l);return}if(Te(o,e)){return}if(Oe(n,e)){return}if(u||Le(n)){e.preventDefault()}var t=N(e);var r=N(n);if(!t.handled){t.handled=true;if(o.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(o.changed){if(r.lastValue===n.value){return}else{r.lastValue=n.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(o.throttle){r.throttle=setTimeout(function(){Vt(i,a,n,e);r.throttle=null},o.throttle)}else if(o.delay){r.delayed=setTimeout(function(){Vt(i,a,n,e)},o.delay)}else{Vt(i,a,n,e)}}};e.trigger=o.trigger;e.eventListener=l;s.addEventListener(o.trigger,l)}var qe=false;var He=null;function Ne(){if(!He){He=function(){qe=true};window.addEventListener("scroll",He);setInterval(function(){if(qe){qe=false;k(q().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){ke(e)})}},200)}}function ke(e){var t=N(e);if(!t.revealed&&m(e)){t.revealed=true;Vt(t.verb,t.path,e)}}function Ie(e,t,r){var n=y(r);for(var i=0;i0){it(s,"htmx:validation:halted",n);return}f.send(JSON.stringify(u));if(Le(s)){e.preventDefault()}})}else{et(s,"htmx:noWebSocketSourceError")}}function Fe(e,t,r){var n=y(r);for(var i=0;iv.config.historyCacheSize){i.shift()}try{localStorage.setItem("htmx-history-cache",JSON.stringify(i))}catch(e){et(q().body,"htmx:historyCacheError",{cause:e})}}function st(e){var t=x(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){it(q().body,"htmx:historyCacheMissLoad",i);var e=d(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=ot();var r=Nt(t);ie(t,e,r);ht(r.tasks);at=n}else{et(q().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function dt(e){ft();e=e||location.pathname+location.search;it(q().body,"htmx:historyRestore",{path:e});var t=st(e);if(t){var r=d(t.content);var n=ot();var i=Nt(n);ie(n,r,i);ht(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);at=e}else{vt(e)}}function gt(e){var t=H(e,"hx-push-url");return t&&t!=="false"||e.tagName==="A"&&N(e).boosted}function pt(e){var t=H(e,"hx-push-url");return t==="true"||t==="false"?null:t}function mt(e){xt(e,"add")}function yt(e){xt(e,"remove")}function xt(e,t){var r=H(e,"hx-indicator");if(r){var n;if(r.indexOf("closest ")===0){n=[D(e,r.substr(8))]}else{n=q().querySelectorAll(r)}}else{n=[e]}k(n,function(e){e.classList[t].call(e.classList,v.config.requestClass)})}function bt(e,t){for(var r=0;r0){r["swapStyle"]=n[0];for(var i=1;i0){it(r,"htmx:validation:halted",b);return s()}var w=t.split("#");var S=w[0];var E=w[1];if(e==="get"){var C=S;var A=Object.keys(x).length!==0;if(A){if(C.indexOf("?")<0){C+="?"}else{C+="&"}C+=Lt(x);if(E){C+="#"+E}}h.open("GET",C,true)}else{h.open(e.toUpperCase(),t,true)}h.overrideMimeType("text/html");for(var L in v){if(v.hasOwnProperty(L)){var O=v[L];Pt(h,L,O)}}var T={xhr:h,target:o,requestConfig:b,pathInfo:{path:t,finalPath:C,anchor:E}};h.onload=function(){try{i(r,T)}catch(e){et(r,"htmx:onLoadError",M({error:e},T));throw e}finally{yt(r);var e=N(r).replacedWith||r;it(e,"htmx:afterRequest",T);it(e,"htmx:afterOnLoad",T);s()}};h.onerror=function(){yt(r);et(r,"htmx:afterRequest",T);et(r,"htmx:sendError",T);s()};h.onabort=function(){yt(r);s()};if(!it(r,"htmx:beforeRequest",T))return s();mt(r);k(["loadstart","loadend","progress","abort"],function(t){k([h,h.upload],function(e){e.addEventListener(t,function(e){it(r,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});h.send(e==="get"?null:Ht(h,r,x))}function _t(a,o){var u=o.xhr;var s=o.target;if(!it(a,"htmx:beforeOnLoad",o))return;if(zt(u,/HX-Trigger:/i)){fe(u,"HX-Trigger",a)}if(zt(u,/HX-Push:/i)){var l=u.getResponseHeader("HX-Push")}if(zt(u,/HX-Redirect:/i)){window.location.href=u.getResponseHeader("HX-Redirect");return}if(zt(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}var f=gt(a)||l;if(u.status>=200&&u.status<400){if(u.status===286){Se(a)}if(u.status!==204){if(!it(s,"htmx:beforeSwap",o))return;var c=u.response;rt(a,function(e){c=e.transformResponse(c,u,a)});if(f){ft()}var h=qt(a);s.classList.add(v.config.swappingClass);var e=function(){try{var e=document.activeElement;var t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null};var r=Nt(s);le(h.swapStyle,s,a,c,r);if(t.elt&&!I(t.elt)&&t.elt.id){var n=document.getElementById(t.elt.id);if(n){if(t.start&&n.setSelectionRange){n.setSelectionRange(t.start,t.end)}n.focus()}}s.classList.remove(v.config.swappingClass);k(r.elts,function(e){if(e.classList){e.classList.add(v.config.settlingClass)}it(e,"htmx:afterSwap",o)});if(o.pathInfo.anchor){location.hash=o.pathInfo.anchor}if(zt(u,/HX-Trigger-After-Swap:/i)){fe(u,"HX-Trigger-After-Swap",a)}var i=function(){k(r.tasks,function(e){e.call()});k(r.elts,function(e){if(e.classList){e.classList.remove(v.config.settlingClass)}it(e,"htmx:afterSettle",o)});if(f){var e=l||pt(a)||Ut(u)||o.pathInfo.finalPath||o.pathInfo.path;ct(e);it(q().body,"htmx:pushedIntoHistory",{path:e})}kt(s,r.elts,h);if(zt(u,/HX-Trigger-After-Settle:/i)){fe(u,"HX-Trigger-After-Settle",a)}};if(h.settleDelay>0){setTimeout(i,h.settleDelay)}else{i()}}catch(e){et(a,"htmx:swapError",o);throw e}};if(h.swapDelay>0){setTimeout(e,h.swapDelay)}else{e()}}}else{et(a,"htmx:responseError",M({error:"Response Status Error Code "+u.status+" from "+o.pathInfo.path},o))}}var Wt={};function Bt(){return{onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function $t(e,t){Wt[e]=M(Bt(),t)}function Jt(e){delete Wt[e]}function Zt(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=R(e,"hx-ext");if(t){k(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Wt[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Zt(s(e),r,n)}function Gt(e){if(q().readyState!=="loading"){e()}else{q().addEventListener("DOMContentLoaded",e)}}function Yt(){if(v.config.includeIndicatorStyles!==false){q().head.insertAdjacentHTML("beforeend","")}}function Kt(){var e=q().querySelector('meta[name="htmx-config"]');if(e){return x(e.content)}else{return null}}function Qt(){var e=Kt();if(e){v.config=M(v.config,e)}}Gt(function(){Qt();Yt();var e=q().body;Ye(e);window.onpopstate=function(e){if(e.state&&e.state.htmx){dt()}};setTimeout(function(){it(e,"htmx:load",{})},0)});return v}()}); +var htmx = (function() { + 'use strict' + + // Public API + const htmx = { + // Tsc madness here, assigning the functions directly results in an invalid TypeScript output, but reassigning is fine + /* Event processing */ + /** @type {typeof onLoadHelper} */ + onLoad: null, + /** @type {typeof processNode} */ + process: null, + /** @type {typeof addEventListenerImpl} */ + on: null, + /** @type {typeof removeEventListenerImpl} */ + off: null, + /** @type {typeof triggerEvent} */ + trigger: null, + /** @type {typeof ajaxHelper} */ + ajax: null, + /* DOM querying helpers */ + /** @type {typeof find} */ + find: null, + /** @type {typeof findAll} */ + findAll: null, + /** @type {typeof closest} */ + closest: null, + /** + * Returns the input values that would resolve for a given element via the htmx value resolution mechanism + * + * @see https://htmx.org/api/#values + * + * @param {Element} elt the element to resolve values on + * @param {HttpVerb} type the request type (e.g. **get** or **post**) non-GET's will include the enclosing form of the element. Defaults to **post** + * @returns {Object} + */ + values: function(elt, type) { + const inputValues = getInputValues(elt, type || 'post') + return inputValues.values + }, + /* DOM manipulation helpers */ + /** @type {typeof removeElement} */ + remove: null, + /** @type {typeof addClassToElement} */ + addClass: null, + /** @type {typeof removeClassFromElement} */ + removeClass: null, + /** @type {typeof toggleClassOnElement} */ + toggleClass: null, + /** @type {typeof takeClassForElement} */ + takeClass: null, + /** @type {typeof swap} */ + swap: null, + /* Extension entrypoints */ + /** @type {typeof defineExtension} */ + defineExtension: null, + /** @type {typeof removeExtension} */ + removeExtension: null, + /* Debugging */ + /** @type {typeof logAll} */ + logAll: null, + /** @type {typeof logNone} */ + logNone: null, + /* Debugging */ + /** + * The logger htmx uses to log with + * + * @see https://htmx.org/api/#logger + */ + logger: null, + /** + * A property holding the configuration htmx uses at runtime. + * + * Note that using a [meta tag](https://htmx.org/docs/#config) is the preferred mechanism for setting these properties. + * + * @see https://htmx.org/api/#config + */ + config: { + /** + * Whether to use history. + * @type boolean + * @default true + */ + historyEnabled: true, + /** + * The number of pages to keep in **localStorage** for history support. + * @type number + * @default 10 + */ + historyCacheSize: 10, + /** + * @type boolean + * @default false + */ + refreshOnHistoryMiss: false, + /** + * The default swap style to use if **[hx-swap](https://htmx.org/attributes/hx-swap)** is omitted. + * @type HtmxSwapStyle + * @default 'innerHTML' + */ + defaultSwapStyle: 'innerHTML', + /** + * The default delay between receiving a response from the server and doing the swap. + * @type number + * @default 0 + */ + defaultSwapDelay: 0, + /** + * The default delay between completing the content swap and settling attributes. + * @type number + * @default 20 + */ + defaultSettleDelay: 20, + /** + * If true, htmx will inject a small amount of CSS into the page to make indicators invisible unless the **htmx-indicator** class is present. + * @type boolean + * @default true + */ + includeIndicatorStyles: true, + /** + * The class to place on indicators when a request is in flight. + * @type string + * @default 'htmx-indicator' + */ + indicatorClass: 'htmx-indicator', + /** + * The class to place on triggering elements when a request is in flight. + * @type string + * @default 'htmx-request' + */ + requestClass: 'htmx-request', + /** + * The class to temporarily place on elements that htmx has added to the DOM. + * @type string + * @default 'htmx-added' + */ + addedClass: 'htmx-added', + /** + * The class to place on target elements when htmx is in the settling phase. + * @type string + * @default 'htmx-settling' + */ + settlingClass: 'htmx-settling', + /** + * The class to place on target elements when htmx is in the swapping phase. + * @type string + * @default 'htmx-swapping' + */ + swappingClass: 'htmx-swapping', + /** + * Allows the use of eval-like functionality in htmx, to enable **hx-vars**, trigger conditions & script tag evaluation. Can be set to **false** for CSP compatibility. + * @type boolean + * @default true + */ + allowEval: true, + /** + * If set to false, disables the interpretation of script tags. + * @type boolean + * @default true + */ + allowScriptTags: true, + /** + * If set, the nonce will be added to inline scripts. + * @type string + * @default '' + */ + inlineScriptNonce: '', + /** + * If set, the nonce will be added to inline styles. + * @type string + * @default '' + */ + inlineStyleNonce: '', + /** + * The attributes to settle during the settling phase. + * @type string[] + * @default ['class', 'style', 'width', 'height'] + */ + attributesToSettle: ['class', 'style', 'width', 'height'], + /** + * Allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates. + * @type boolean + * @default false + */ + withCredentials: false, + /** + * @type number + * @default 0 + */ + timeout: 0, + /** + * The default implementation of **getWebSocketReconnectDelay** for reconnecting after unexpected connection loss by the event code **Abnormal Closure**, **Service Restart** or **Try Again Later**. + * @type {'full-jitter' | ((retryCount:number) => number)} + * @default "full-jitter" + */ + wsReconnectDelay: 'full-jitter', + /** + * The type of binary data being received over the WebSocket connection + * @type BinaryType + * @default 'blob' + */ + wsBinaryType: 'blob', + /** + * @type string + * @default '[hx-disable], [data-hx-disable]' + */ + disableSelector: '[hx-disable], [data-hx-disable]', + /** + * @type {'auto' | 'instant' | 'smooth'} + * @default 'instant' + */ + scrollBehavior: 'instant', + /** + * If the focused element should be scrolled into view. + * @type boolean + * @default false + */ + defaultFocusScroll: false, + /** + * If set to true htmx will include a cache-busting parameter in GET requests to avoid caching partial responses by the browser + * @type boolean + * @default false + */ + getCacheBusterParam: false, + /** + * If set to true, htmx will use the View Transition API when swapping in new content. + * @type boolean + * @default false + */ + globalViewTransitions: false, + /** + * htmx will format requests with these methods by encoding their parameters in the URL, not the request body + * @type {(HttpVerb)[]} + * @default ['get', 'delete'] + */ + methodsThatUseUrlParams: ['get', 'delete'], + /** + * If set to true, disables htmx-based requests to non-origin hosts. + * @type boolean + * @default false + */ + selfRequestsOnly: true, + /** + * If set to true htmx will not update the title of the document when a title tag is found in new content + * @type boolean + * @default false + */ + ignoreTitle: false, + /** + * Whether the target of a boosted element is scrolled into the viewport. + * @type boolean + * @default true + */ + scrollIntoViewOnBoost: true, + /** + * The cache to store evaluated trigger specifications into. + * You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) + * @type {Object|null} + * @default null + */ + triggerSpecsCache: null, + /** @type boolean */ + disableInheritance: false, + /** @type HtmxResponseHandlingConfig[] */ + responseHandling: [ + { code: '204', swap: false }, + { code: '[23]..', swap: true }, + { code: '[45]..', swap: false, error: true } + ], + /** + * Whether to process OOB swaps on elements that are nested within the main response element. + * @type boolean + * @default true + */ + allowNestedOobSwaps: true + }, + /** @type {typeof parseInterval} */ + parseInterval: null, + /** @type {typeof internalEval} */ + _: null, + version: '2.0.2' + } + // Tsc madness part 2 + htmx.onLoad = onLoadHelper + htmx.process = processNode + htmx.on = addEventListenerImpl + htmx.off = removeEventListenerImpl + htmx.trigger = triggerEvent + htmx.ajax = ajaxHelper + htmx.find = find + htmx.findAll = findAll + htmx.closest = closest + htmx.remove = removeElement + htmx.addClass = addClassToElement + htmx.removeClass = removeClassFromElement + htmx.toggleClass = toggleClassOnElement + htmx.takeClass = takeClassForElement + htmx.swap = swap + htmx.defineExtension = defineExtension + htmx.removeExtension = removeExtension + htmx.logAll = logAll + htmx.logNone = logNone + htmx.parseInterval = parseInterval + htmx._ = internalEval + + const internalAPI = { + addTriggerHandler, + bodyContains, + canAccessLocalStorage, + findThisElement, + filterValues, + swap, + hasAttribute, + getAttributeValue, + getClosestAttributeValue, + getClosestMatch, + getExpressionVars, + getHeaders, + getInputValues, + getInternalData, + getSwapSpecification, + getTriggerSpecs, + getTarget, + makeFragment, + mergeObjects, + makeSettleInfo, + oobSwap, + querySelectorExt, + settleImmediately, + shouldCancel, + triggerEvent, + triggerErrorEvent, + withExtensions + } + + const VERBS = ['get', 'post', 'put', 'delete', 'patch'] + const VERB_SELECTOR = VERBS.map(function(verb) { + return '[hx-' + verb + '], [data-hx-' + verb + ']' + }).join(', ') + + const HEAD_TAG_REGEX = makeTagRegEx('head') + + //= =================================================================== + // Utilities + //= =================================================================== + + /** + * @param {string} tag + * @param {boolean} global + * @returns {RegExp} + */ + function makeTagRegEx(tag, global = false) { + return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`, + global ? 'gim' : 'im') + } + + /** + * Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes. + * + * Caution: Accepts an int followed by either **s** or **ms**. All other values use **parseFloat** + * + * @see https://htmx.org/api/#parseInterval + * + * @param {string} str timing string + * @returns {number|undefined} + */ + function parseInterval(str) { + if (str == undefined) { + return undefined + } + + let interval = NaN + if (str.slice(-2) == 'ms') { + interval = parseFloat(str.slice(0, -2)) + } else if (str.slice(-1) == 's') { + interval = parseFloat(str.slice(0, -1)) * 1000 + } else if (str.slice(-1) == 'm') { + interval = parseFloat(str.slice(0, -1)) * 1000 * 60 + } else { + interval = parseFloat(str) + } + return isNaN(interval) ? undefined : interval + } + + /** + * @param {Node} elt + * @param {string} name + * @returns {(string | null)} + */ + function getRawAttribute(elt, name) { + return elt instanceof Element && elt.getAttribute(name) + } + + /** + * @param {Element} elt + * @param {string} qualifiedName + * @returns {boolean} + */ + // resolve with both hx and data-hx prefixes + function hasAttribute(elt, qualifiedName) { + return !!elt.hasAttribute && (elt.hasAttribute(qualifiedName) || + elt.hasAttribute('data-' + qualifiedName)) + } + + /** + * + * @param {Node} elt + * @param {string} qualifiedName + * @returns {(string | null)} + */ + function getAttributeValue(elt, qualifiedName) { + return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, 'data-' + qualifiedName) + } + + /** + * @param {Node} elt + * @returns {Node | null} + */ + function parentElt(elt) { + const parent = elt.parentElement + if (!parent && elt.parentNode instanceof ShadowRoot) return elt.parentNode + return parent + } + + /** + * @returns {Document} + */ + function getDocument() { + return document + } + + /** + * @param {Node} elt + * @param {boolean} global + * @returns {Node|Document} + */ + function getRootNode(elt, global) { + return elt.getRootNode ? elt.getRootNode({ composed: global }) : getDocument() + } + + /** + * @param {Node} elt + * @param {(e:Node) => boolean} condition + * @returns {Node | null} + */ + function getClosestMatch(elt, condition) { + while (elt && !condition(elt)) { + elt = parentElt(elt) + } + + return elt || null + } + + /** + * @param {Element} initialElement + * @param {Element} ancestor + * @param {string} attributeName + * @returns {string|null} + */ + function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) { + const attributeValue = getAttributeValue(ancestor, attributeName) + const disinherit = getAttributeValue(ancestor, 'hx-disinherit') + var inherit = getAttributeValue(ancestor, 'hx-inherit') + if (initialElement !== ancestor) { + if (htmx.config.disableInheritance) { + if (inherit && (inherit === '*' || inherit.split(' ').indexOf(attributeName) >= 0)) { + return attributeValue + } else { + return null + } + } + if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) { + return 'unset' + } + } + return attributeValue + } + + /** + * @param {Element} elt + * @param {string} attributeName + * @returns {string | null} + */ + function getClosestAttributeValue(elt, attributeName) { + let closestAttr = null + getClosestMatch(elt, function(e) { + return !!(closestAttr = getAttributeValueWithDisinheritance(elt, asElement(e), attributeName)) + }) + if (closestAttr !== 'unset') { + return closestAttr + } + } + + /** + * @param {Node} elt + * @param {string} selector + * @returns {boolean} + */ + function matches(elt, selector) { + // @ts-ignore: non-standard properties for browser compatibility + // noinspection JSUnresolvedVariable + const matchesFunction = elt instanceof Element && (elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector) + return !!matchesFunction && matchesFunction.call(elt, selector) + } + + /** + * @param {string} str + * @returns {string} + */ + function getStartTag(str) { + const tagMatcher = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i + const match = tagMatcher.exec(str) + if (match) { + return match[1].toLowerCase() + } else { + return '' + } + } + + /** + * @param {string} resp + * @returns {Document} + */ + function parseHTML(resp) { + const parser = new DOMParser() + return parser.parseFromString(resp, 'text/html') + } + + /** + * @param {DocumentFragment} fragment + * @param {Node} elt + */ + function takeChildrenFor(fragment, elt) { + while (elt.childNodes.length > 0) { + fragment.append(elt.childNodes[0]) + } + } + + /** + * @param {HTMLScriptElement} script + * @returns {HTMLScriptElement} + */ + function duplicateScript(script) { + const newScript = getDocument().createElement('script') + forEach(script.attributes, function(attr) { + newScript.setAttribute(attr.name, attr.value) + }) + newScript.textContent = script.textContent + newScript.async = false + if (htmx.config.inlineScriptNonce) { + newScript.nonce = htmx.config.inlineScriptNonce + } + return newScript + } + + /** + * @param {HTMLScriptElement} script + * @returns {boolean} + */ + function isJavaScriptScriptNode(script) { + return script.matches('script') && (script.type === 'text/javascript' || script.type === 'module' || script.type === '') + } + + /** + * we have to make new copies of script tags that we are going to insert because + * SOME browsers (not saying who, but it involves an element and an animal) don't + * execute scripts created in