blob: dd433bf2f13d31a6fb0692c04d2d4df49d88c1b3 [file] [log] [blame]
Adrià Vilanova Martínez5af86512023-12-02 20:44:16 +01001/* (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 botbe50d492023-11-30 00:16:42 +010019/**
20 * Functions hasClass(), addClass() and removeClass() developed by Jake Trent (http://jaketrent.com/post/addremove-classes-raw-javascript/)
21 */
22function 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
29function addClass(el, className) {
30 if (el.classList)
31 el.classList.add(className)
32 else if (!hasClass(el, className)) el.className += " " + className
33}
34
35function 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:
45var 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
57MultiSelect.prototype.renderSummary = function() {
58 if (this.forEl_) {
59 this.forEl_.innerText = this.selected_.join(", ") || "-";
60 }
61};
62
63MultiSelect.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
87var 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
152function 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
162document.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
180function 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
187var 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
199function 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
211function 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
237function 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
252window.addEventListener("load", function() {
253 document.querySelectorAll("[data-required]").forEach(input => {
254 input.setAttribute("required", "true");
255 });
256
257 if (!isDateInputSupported()) {
258 initPolyfillDateSupport();
259 }
260});