Adrià Vilanova Martínez | 5af8651 | 2023-12-02 20:44:16 +0100 | [diff] [blame] | 1 | /* (license-header) |
| 2 | * hores |
| 3 | * Copyright (c) 2023 Adrià Vilanova Martínez |
| 4 | * |
| 5 | * This program is free software: you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU Affero General Public License as |
| 7 | * published by the Free Software Foundation, either version 3 of the |
| 8 | * License, or (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU Affero General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU Affero General Public |
| 16 | * License along with this program. |
| 17 | * If not, see http://www.gnu.org/licenses/. |
| 18 | */ |
Copybara bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 19 | /** |
| 20 | * Functions hasClass(), addClass() and removeClass() developed by Jake Trent (http://jaketrent.com/post/addremove-classes-raw-javascript/) |
| 21 | */ |
| 22 | function hasClass(el, className) { |
| 23 | if (el.classList) |
| 24 | return el.classList.contains(className) |
| 25 | else |
| 26 | return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)')) |
| 27 | } |
| 28 | |
| 29 | function addClass(el, className) { |
| 30 | if (el.classList) |
| 31 | el.classList.add(className) |
| 32 | else if (!hasClass(el, className)) el.className += " " + className |
| 33 | } |
| 34 | |
| 35 | function removeClass(el, className) { |
| 36 | if (el.classList) |
| 37 | el.classList.remove(className) |
| 38 | else if (hasClass(el, className)) { |
| 39 | var reg = new RegExp('(\\s|^)' + className + '(\\s|$)') |
| 40 | el.className=el.className.replace(reg, ' ') |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // MultiSelect implementation: |
| 45 | var MultiSelect = function MultiSelect(element) { |
| 46 | this.element_ = element; |
| 47 | this.selected_ = []; |
| 48 | |
| 49 | var forElId = this.element_.getAttribute("for") || this.element_.getAttribute("data-for"); |
| 50 | if (forElId) { |
| 51 | this.forEl_ = (forElId ? document.getElementById(this.element_.getAttribute("for")) : null); |
| 52 | } |
| 53 | |
| 54 | this.init(); |
| 55 | }; |
| 56 | |
| 57 | MultiSelect.prototype.renderSummary = function() { |
| 58 | if (this.forEl_) { |
| 59 | this.forEl_.innerText = this.selected_.join(", ") || "-"; |
| 60 | } |
| 61 | }; |
| 62 | |
| 63 | MultiSelect.prototype.init = function() { |
| 64 | if (this.element_) { |
| 65 | this.element_.addEventListener("click", e => { |
| 66 | e.stopImmediatePropagation(); |
| 67 | }, true); |
| 68 | |
| 69 | this.element_.querySelectorAll(".mdl-custom-multiselect__item").forEach(item => { |
| 70 | var checkbox = item.querySelector("input[type=\"checkbox\"]"); |
| 71 | var label = item.querySelector(".mdl-checkbox__label").innerText; |
| 72 | checkbox.addEventListener("change", e => { |
| 73 | if(checkbox.checked) { |
| 74 | this.selected_.push(label); |
| 75 | } else { |
| 76 | this.selected_ = this.selected_.filter(item => item !== label); |
| 77 | } |
| 78 | this.renderSummary(); |
| 79 | |
| 80 | var customEvent = new Event('custom-multiselect-change'); |
| 81 | this.element_.dispatchEvent(customEvent); |
| 82 | }); |
| 83 | }); |
| 84 | } |
| 85 | }; |
| 86 | |
| 87 | var dynDialog = { |
| 88 | didItInit: false, |
| 89 | dialog: null, |
| 90 | url: null, |
| 91 | load: function(url, reload) { |
| 92 | if (this.didItInit === false) { |
| 93 | this.init(); |
| 94 | } |
| 95 | |
| 96 | if (this.url == url && reload !== true) { |
| 97 | this.show(); |
| 98 | return; |
| 99 | } |
| 100 | |
| 101 | this.url = url; |
| 102 | |
| 103 | fetch(url).then(response => response.text()).then(response => { |
| 104 | if (this.dialog.open) { |
| 105 | this.close(); |
| 106 | } |
| 107 | |
| 108 | this.dialog.innerHTML = response; |
| 109 | componentHandler.upgradeElements(this.dialog); |
| 110 | |
| 111 | this.dialog.querySelectorAll("[data-required]").forEach(input => { |
| 112 | input.setAttribute("required", "true"); |
| 113 | }); |
| 114 | |
| 115 | var script = this.dialog.querySelectorAll("dynscript"); |
| 116 | if (script.length > 0) { |
| 117 | for (var i = 0; i < script.length; i++) { |
| 118 | eval(script[i].innerText); |
| 119 | } |
| 120 | } |
| 121 | this.dialog.querySelectorAll("[data-dyndialog-close]").forEach(btn => { |
| 122 | btn.addEventListener("click", e => { |
| 123 | e.preventDefault(); |
| 124 | this.close(); |
| 125 | }); |
| 126 | }); |
| 127 | |
| 128 | this.dialog.showModal(); |
| 129 | }); |
| 130 | }, |
| 131 | reload: function() { |
| 132 | this.load(this.url, true); |
| 133 | }, |
| 134 | show: function() { |
| 135 | this.dialog.showModal(); |
| 136 | }, |
| 137 | close: function() { |
| 138 | this.dialog.close(); |
| 139 | }, |
| 140 | init: function() { |
| 141 | this.dialog = document.createElement("dialog"); |
| 142 | this.dialog.setAttribute("id", "dynDialog"); |
| 143 | this.dialog.setAttribute("class", "mdl-dialog"); |
| 144 | dialogPolyfill.registerDialog(this.dialog); |
| 145 | document.body.appendChild(this.dialog); |
| 146 | |
| 147 | this.didItInit = true; |
| 148 | } |
| 149 | }; |
| 150 | |
| 151 | // From nodep-date-input-polyfill |
| 152 | function isDateInputSupported() { |
| 153 | const input = document.createElement("input"); |
| 154 | input.setAttribute("type", "date"); |
| 155 | |
| 156 | const notADateValue = "not-a-date"; |
| 157 | input.setAttribute("value", notADateValue); |
| 158 | |
| 159 | return (input.value !== notADateValue); |
| 160 | } |
| 161 | |
| 162 | document.addEventListener("DOMContentLoaded", function() { |
| 163 | var dialogs = document.querySelectorAll("dialog"); |
| 164 | for (var i = 0; i < dialogs.length; i++) { |
| 165 | dialogPolyfill.registerDialog(dialogs[i]); |
| 166 | } |
| 167 | |
| 168 | document.querySelectorAll("[data-dyndialog-href]").forEach(link => { |
| 169 | link.addEventListener("click", e => { |
| 170 | e.preventDefault(); |
| 171 | dynDialog.load(link.getAttribute("data-dyndialog-href")); |
| 172 | }); |
| 173 | }); |
| 174 | |
| 175 | document.querySelectorAll(".mdl-custom-multiselect-js").forEach(menu => { |
| 176 | new MultiSelect(menu); |
| 177 | }); |
| 178 | }); |
| 179 | |
| 180 | function importCSS(url) { |
| 181 | var link = document.createElement("link"); |
| 182 | link.setAttribute("rel", "stylesheet"); |
| 183 | link.setAttribute("href", url); |
| 184 | document.head.appendChild(link); |
| 185 | } |
| 186 | |
| 187 | var loadScriptAsync = function(uri) { |
| 188 | return new Promise((resolve, reject) => { |
| 189 | var script = document.createElement('script'); |
| 190 | script.src = uri; |
| 191 | script.async = true; |
| 192 | script.onload = () => { |
| 193 | resolve(); |
| 194 | }; |
| 195 | document.head.appendChild(script); |
| 196 | }); |
| 197 | } |
| 198 | |
| 199 | function hasParentDialog(el) { |
| 200 | while (el != document.documentElement) { |
| 201 | if (el.tagName == "DIALOG") { |
| 202 | return el; |
| 203 | } |
| 204 | |
| 205 | el = el.parentNode; |
| 206 | } |
| 207 | |
| 208 | return undefined; |
| 209 | } |
| 210 | |
| 211 | function polyfillDateSupport(container) { |
| 212 | container.querySelectorAll("input[type=\"date\"]").forEach(el => { |
| 213 | el.setAttribute("type", "text"); |
| 214 | |
| 215 | dialogParent = hasParentDialog(el); |
| 216 | |
| 217 | var options = { |
| 218 | format: "yyyy-mm-dd", |
| 219 | todayHighlight: true, |
| 220 | weekStart: 1, |
| 221 | zIndexOffset: 100, |
| 222 | container: dialogParent || container |
| 223 | }; |
| 224 | |
| 225 | if (el.hasAttribute("max")) { |
| 226 | options.endDate = el.getAttribute("max"); |
| 227 | } |
| 228 | |
| 229 | $(el).datepicker(options); |
| 230 | }); |
| 231 | |
| 232 | container.querySelectorAll("input[type=\"time\"]").forEach(el => { |
| 233 | el.setAttribute("placeholder", "hh:mm"); |
| 234 | }); |
| 235 | } |
| 236 | |
| 237 | function initPolyfillDateSupport() { |
| 238 | console.info("Polyfilling date support"); |
| 239 | |
| 240 | importCSS("https://unpkg.com/bootstrap-datepicker@1.9.0/dist/css/bootstrap-datepicker3.standalone.min.css", "css"); |
| 241 | |
| 242 | loadScriptAsync("https://unpkg.com/jquery@3.4.1/dist/jquery.min.js", "js").then(_ => { |
| 243 | return loadScriptAsync("https://unpkg.com/bootstrap-datepicker@1.9.0/dist/js/bootstrap-datepicker.min.js"); |
| 244 | }).then(_ => { |
| 245 | return loadScriptAsync("https://unpkg.com/bootstrap-datepicker@1.9.0/dist/locales/bootstrap-datepicker.es.min.js"); |
| 246 | }).then(_ => { |
| 247 | console.log("[Date polyfill] Scripts loaded."); |
| 248 | polyfillDateSupport(document.documentElement); |
| 249 | }); |
| 250 | } |
| 251 | |
| 252 | window.addEventListener("load", function() { |
| 253 | document.querySelectorAll("[data-required]").forEach(input => { |
| 254 | input.setAttribute("required", "true"); |
| 255 | }); |
| 256 | |
| 257 | if (!isDateInputSupported()) { |
| 258 | initPolyfillDateSupport(); |
| 259 | } |
| 260 | }); |