bootstrap5-toggle 5.0.6 → 5.1.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.
@@ -1,504 +1,531 @@
1
- /* Copyright Notice
2
- * bootstrap5-toggle v5.0.6
3
- * https://palcarazm.github.io/bootstrap5-toggle/
4
- * @author 2011-2014 Min Hur (https://github.com/minhur)
5
- * @author 2018-2019 Brent Ely (https://github.com/gitbrent)
6
- * @author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)
7
- * @funding GitHub Sponsors
8
- * @see https://github.com/sponsors/palcarazm
9
- * @license MIT
10
- * @see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE
11
- */
12
-
13
-
14
- "use strict";
15
-
16
- (function () {
17
- /**
18
- * `Toggle` is instantiated for each toggle-button
19
- */
20
- class Toggle {
21
- constructor(element, options) {
22
- const DEPRECATION = {
23
- value:
24
- "BOOTSTRAP TOGGLE DEPRECATION CHECK -- a0Jhux0QySypjjs4tLtEo8xT2kx0AbYaq9K6mgNjWSs0HF0L8T8J0M0o3Kr7zkm7 --",
25
- ATTRIBUTE: "attribute",
26
- OPTION: "option",
27
- log: function (type, oldlabel, newlabel) {
28
- console.warn(
29
- `Bootstrap Toggle deprecation warning: Using ${oldlabel} ${type} is deprected. Use ${newlabel} instead.`
30
- );
31
- },
32
- };
33
- const DEFAULTS = {
34
- onlabel: "On",
35
- onstyle: "primary",
36
- onvalue: null,
37
- ontitle: null,
38
- offlabel: "Off",
39
- offstyle: "secondary",
40
- offvalue: null,
41
- offtitle: null,
42
- size: "",
43
- style: "",
44
- width: null,
45
- height: null,
46
- tabindex: 0,
47
- tristate: false,
48
- name: null,
49
- };
50
- options = options || {};
51
-
52
- // A: Capture ref to HMTL element
53
- this.element = element;
54
-
55
- // B: Set options
56
- this.options = {
57
- onlabel:
58
- this.element.getAttribute("data-onlabel") ||
59
- options.onlabel ||
60
- DEPRECATION.value ||
61
- DEFAULTS.onlabel,
62
- onstyle:
63
- this.element.getAttribute("data-onstyle") ||
64
- options.onstyle ||
65
- DEFAULTS.onstyle,
66
- onvalue:
67
- this.element.getAttribute("value") ||
68
- this.element.getAttribute("data-onvalue") ||
69
- options.onvalue ||
70
- DEFAULTS.onvalue,
71
- ontitle:
72
- this.element.getAttribute("data-ontitle") ||
73
- options.ontitle ||
74
- this.element.getAttribute("title") ||
75
- DEFAULTS.ontitle,
76
- offlabel:
77
- this.element.getAttribute("data-offlabel") ||
78
- options.offlabel ||
79
- DEPRECATION.value ||
80
- DEFAULTS.offlabel,
81
- offstyle:
82
- this.element.getAttribute("data-offstyle") ||
83
- options.offstyle ||
84
- DEFAULTS.offstyle,
85
- offvalue:
86
- this.element.getAttribute("data-offvalue") ||
87
- options.offvalue ||
88
- DEFAULTS.offvalue,
89
- offtitle:
90
- this.element.getAttribute("data-offtitle") ||
91
- options.offtitle ||
92
- this.element.getAttribute("title") ||
93
- DEFAULTS.offtitle,
94
- size:
95
- this.element.getAttribute("data-size") ||
96
- options.size ||
97
- DEFAULTS.size,
98
- style:
99
- this.element.getAttribute("data-style") ||
100
- options.style ||
101
- DEFAULTS.style,
102
- width:
103
- this.element.getAttribute("data-width") ||
104
- options.width ||
105
- DEFAULTS.width,
106
- height:
107
- this.element.getAttribute("data-height") ||
108
- options.height ||
109
- DEFAULTS.height,
110
- tabindex:
111
- this.element.getAttribute("tabindex") ||
112
- options.tabindex ||
113
- DEFAULTS.tabindex,
114
- tristate:
115
- this.element.hasAttribute("tristate") ||
116
- options.tristate ||
117
- DEFAULTS.tristate,
118
- name:
119
- this.element.getAttribute("name") || options.name || DEFAULTS.name,
120
- };
121
-
122
- // C: Check deprecations
123
- if (this.options.onlabel === DEPRECATION.value) {
124
- if (this.element.getAttribute("data-on")) {
125
- DEPRECATION.log(DEPRECATION.ATTRIBUTE, "data-on", "data-onlabel");
126
- this.options.onlabel = this.element.getAttribute("data-on");
127
- } else if (options.on) {
128
- DEPRECATION.log(DEPRECATION.OPTION, "on", "onlabel");
129
- this.options.onlabel = options.on;
130
- } else {
131
- this.options.onlabel = DEFAULTS.onlabel;
132
- }
133
- }
134
- if (this.options.offlabel === DEPRECATION.value) {
135
- if (this.element.getAttribute("data-off")) {
136
- DEPRECATION.log(DEPRECATION.ATTRIBUTE, "data-off", "data-offlabel");
137
- this.options.offlabel = this.element.getAttribute("data-off");
138
- } else if (options.off) {
139
- DEPRECATION.log(DEPRECATION.OPTION, "off", "offlabel");
140
- this.options.offlabel = options.off;
141
- } else {
142
- this.options.offlabel = DEFAULTS.offlabel;
143
- }
144
- }
145
-
146
- // LAST: Render Toggle
147
- this.render();
148
- }
149
- render() {
150
- function calcH(el) {
151
- const styles = window.getComputedStyle(el);
152
- const height = el.offsetHeight;
153
- const borderTopWidth = parseFloat(styles.borderTopWidth);
154
- const borderBottomWidth = parseFloat(styles.borderBottomWidth);
155
- const paddingTop = parseFloat(styles.paddingTop);
156
- const paddingBottom = parseFloat(styles.paddingBottom);
157
-
158
- return (
159
- height -
160
- borderBottomWidth -
161
- borderTopWidth -
162
- paddingTop -
163
- paddingBottom
164
- );
165
- }
166
- // 0: Parse size
167
- let size;
168
- switch (this.options.size) {
169
- case "large":
170
- case "lg":
171
- size = "btn-lg";
172
- break;
173
- case "small":
174
- case "sm":
175
- size = "btn-sm";
176
- break;
177
- case "mini":
178
- case "xs":
179
- size = "btn-xs";
180
- break;
181
- default:
182
- size = "";
183
- break;
184
- }
185
-
186
- // 1: On
187
- let ecmasToggleOn = document.createElement("span");
188
- ecmasToggleOn.setAttribute(
189
- "class",
190
- "btn btn-" + this.options.onstyle + " " + size
191
- );
192
- ecmasToggleOn.innerHTML = this.options.onlabel;
193
- if (this.options.ontitle) {
194
- ecmasToggleOn.setAttribute("title", this.options.ontitle);
195
- }
196
-
197
- // 2: Off
198
- let ecmasToggleOff = document.createElement("span");
199
- ecmasToggleOff.setAttribute(
200
- "class",
201
- "btn btn-" + this.options.offstyle + " " + size
202
- );
203
- ecmasToggleOff.innerHTML = this.options.offlabel;
204
- if (this.options.offtitle) {
205
- ecmasToggleOff.setAttribute("title", this.options.offtitle);
206
- }
207
-
208
- // 3: Handle
209
- let ecmasToggleHandle = document.createElement("span");
210
- ecmasToggleHandle.setAttribute("class", "toggle-handle btn " + size);
211
-
212
- // 4: Toggle Group
213
- let ecmasToggleGroup = document.createElement("div");
214
- ecmasToggleGroup.setAttribute("class", "toggle-group");
215
- ecmasToggleGroup.appendChild(ecmasToggleOn);
216
- ecmasToggleGroup.appendChild(ecmasToggleOff);
217
- ecmasToggleGroup.appendChild(ecmasToggleHandle);
218
-
219
- // 5: Toggle
220
- let ecmasToggle = document.createElement("div");
221
- ecmasToggle.setAttribute("class", "toggle btn");
222
- ecmasToggle.classList.add(
223
- this.element.checked
224
- ? "btn-" + this.options.onstyle
225
- : "btn-" + this.options.offstyle
226
- );
227
- ecmasToggle.setAttribute("tabindex", this.options.tabindex);
228
- if (!this.element.checked) ecmasToggle.classList.add("off");
229
- if (this.options.size) ecmasToggle.classList.add(size);
230
- if (this.options.style) {
231
- this.options.style.split(" ").forEach((style) => {
232
- ecmasToggle.classList.add(style);
233
- });
234
- }
235
- if (this.element.disabled || this.element.readOnly) {
236
- ecmasToggle.classList.add("disabled");
237
- ecmasToggle.setAttribute("disabled", "disabled");
238
- }
239
-
240
- // 6: Set form values
241
- if (this.options.onvalue)
242
- this.element.setAttribute("value", this.options.onvalue);
243
- let invElement = null;
244
- if (this.options.offvalue) {
245
- invElement = this.element.cloneNode();
246
- invElement.setAttribute("value", this.options.offvalue);
247
- invElement.setAttribute("data-toggle", "invert-toggle");
248
- invElement.removeAttribute("id");
249
- invElement.checked = !this.element.checked;
250
- }
251
-
252
- // 7: Replace HTML checkbox with Toggle-Button
253
- this.element.parentElement.insertBefore(ecmasToggle, this.element);
254
- ecmasToggle.appendChild(this.element);
255
- if (invElement) ecmasToggle.appendChild(invElement);
256
- ecmasToggle.appendChild(ecmasToggleGroup);
257
-
258
- // 8: Set button W/H, lineHeight
259
- {
260
- // A: Set style W/H
261
- // NOTE: `offsetWidth` returns *rounded* integer values, so use `getBoundingClientRect` instead.
262
- if (this.options.width) {
263
- ecmasToggle.style.width = `${this.options.width}px`;
264
- } else {
265
- ecmasToggle.style["min-width"] = "100px"; // First approach for better calculation
266
- ecmasToggle.style["min-width"] = `${
267
- Math.max(
268
- ecmasToggleOn.getBoundingClientRect().width,
269
- ecmasToggleOff.getBoundingClientRect().width
270
- ) +
271
- ecmasToggleHandle.getBoundingClientRect().width / 2
272
- }px`;
273
- }
274
-
275
- if (this.options.height) {
276
- ecmasToggle.style.height = `${this.options.height}px`;
277
- } else {
278
- ecmasToggle.style["min-height"] = "36px"; // First approach for better calculation
279
- ecmasToggle.style["min-height"] = `${Math.max(
280
- ecmasToggleOn.getBoundingClientRect().height,
281
- ecmasToggleOff.getBoundingClientRect().height
282
- )}px`;
283
- }
284
-
285
- // B: Apply on/off class
286
- ecmasToggleOn.classList.add("toggle-on");
287
- ecmasToggleOff.classList.add("toggle-off");
288
-
289
- // C: Finally, set lineHeight if needed
290
- if (this.options.height) {
291
- ecmasToggleOn.style.lineHeight = calcH(ecmasToggleOn) + "px";
292
- ecmasToggleOff.style.lineHeight = calcH(ecmasToggleOff) + "px";
293
- }
294
- }
295
-
296
- // 9: Add listeners
297
- ecmasToggle.addEventListener("touchstart", (e) => {
298
- this.#toggleActionPerformed(e);
299
- });
300
- ecmasToggle.addEventListener("click", (e) => {
301
- this.#toggleActionPerformed(e);
302
- });
303
- ecmasToggle.addEventListener("keypress", (e) => {
304
- if (e.key == " ") {
305
- this.#toggleActionPerformed(e);
306
- }
307
- });
308
-
309
- if (this.element.id) {
310
- document
311
- .querySelectorAll('label[for="' + this.element.id + '"]')
312
- .forEach((label) => {
313
- label.addEventListener("touchstart", (_e) => {
314
- this.toggle();
315
- ecmasToggle.focus();
316
- });
317
- label.addEventListener("click", (_e) => {
318
- this.toggle();
319
- ecmasToggle.focus();
320
- });
321
- });
322
- }
323
-
324
- // 10: Set elements to bootstrap object
325
- this.ecmasToggle = ecmasToggle;
326
- this.invElement = invElement;
327
-
328
- // 11: Keep reference to this instance for subsequent calls via `getElementById().bootstrapToggle()`
329
- this.element.bsToggle = this;
330
- }
331
-
332
- /**
333
- * Trigger actions
334
- * @param {Event} e event
335
- */
336
- #toggleActionPerformed(e) {
337
- if (this.options.tristate) {
338
- if (this.ecmasToggle.classList.contains("indeterminate")) {
339
- this.determinate(true);
340
- this.toggle();
341
- } else {
342
- this.indeterminate();
343
- }
344
- } else {
345
- this.toggle();
346
- }
347
- e.preventDefault();
348
- }
349
-
350
- toggle(silent = false) {
351
- if (this.element.checked) this.off(silent);
352
- else this.on(silent);
353
- }
354
-
355
- on(silent = false) {
356
- if (this.element.disabled || this.element.readOnly) return false;
357
- this.ecmasToggle.classList.remove("btn-" + this.options.offstyle);
358
- this.ecmasToggle.classList.add("btn-" + this.options.onstyle);
359
- this.ecmasToggle.classList.remove("off");
360
- this.element.checked = true;
361
- if (this.invElement) this.invElement.checked = false;
362
- if (!silent) this.trigger();
363
- }
364
-
365
- off(silent = false) {
366
- if (this.element.disabled || this.element.readOnly) return false;
367
- this.ecmasToggle.classList.remove("btn-" + this.options.onstyle);
368
- this.ecmasToggle.classList.add("btn-" + this.options.offstyle);
369
- this.ecmasToggle.classList.add("off");
370
- this.element.checked = false;
371
- if (this.invElement) this.invElement.checked = true;
372
- if (!silent) this.trigger();
373
- }
374
-
375
- indeterminate(silent = false) {
376
- if (
377
- !this.options.tristate ||
378
- this.element.disabled ||
379
- this.element.readOnly
380
- )
381
- return false;
382
- this.ecmasToggle.classList.add("indeterminate");
383
- this.element.indeterminate = true;
384
- this.element.removeAttribute("name");
385
- if (this.invElement) this.invElement.indeterminate = true;
386
- if (this.invElement) this.invElement.removeAttribute("name");
387
- if (!silent) this.trigger();
388
- }
389
-
390
- determinate(silent = false) {
391
- if (
392
- !this.options.tristate ||
393
- this.element.disabled ||
394
- this.element.readOnly
395
- )
396
- return false;
397
- this.ecmasToggle.classList.remove("indeterminate");
398
- this.element.indeterminate = false;
399
- if (this.options.name)
400
- this.element.setAttribute("name", this.options.name);
401
- if (this.invElement) this.invElement.indeterminate = false;
402
- if (this.invElement && this.options.name)
403
- this.invElement.setAttribute("name", this.options.name);
404
- if (!silent) this.trigger();
405
- }
406
-
407
- enable() {
408
- this.ecmasToggle.classList.remove("disabled");
409
- this.ecmasToggle.removeAttribute("disabled");
410
- this.element.removeAttribute("disabled");
411
- this.element.removeAttribute("readonly");
412
- if (this.invElement) {
413
- this.invElement.removeAttribute("disabled");
414
- this.invElement.removeAttribute("readonly");
415
- }
416
- }
417
-
418
- disable() {
419
- this.ecmasToggle.classList.add("disabled");
420
- this.ecmasToggle.setAttribute("disabled", "");
421
- this.element.setAttribute("disabled", "");
422
- this.element.removeAttribute("readonly");
423
- if (this.invElement) {
424
- this.invElement.setAttribute("disabled", "");
425
- this.invElement.removeAttribute("readonly");
426
- }
427
- }
428
-
429
- readonly() {
430
- this.ecmasToggle.classList.add("disabled");
431
- this.ecmasToggle.setAttribute("disabled", "");
432
- this.element.removeAttribute("disabled");
433
- this.element.setAttribute("readonly", "");
434
- if (this.invElement) {
435
- this.invElement.removeAttribute("disabled");
436
- this.invElement.setAttribute("readonly", "");
437
- }
438
- }
439
-
440
- update(silent) {
441
- if (this.element.disabled) this.disable();
442
- else if (this.element.readOnly) this.readonly();
443
- else this.enable();
444
- if (this.element.checked) this.on(silent);
445
- else this.off(silent);
446
- }
447
-
448
- trigger(silent) {
449
- if (!silent)
450
- this.element.dispatchEvent(new Event("change", { bubbles: true }));
451
- }
452
-
453
- destroy() {
454
- // A: Remove button-group from UI, replace checkbox element
455
- this.ecmasToggle.parentNode.insertBefore(this.element, this.ecmasToggle);
456
- this.ecmasToggle.parentNode.removeChild(this.ecmasToggle);
457
-
458
- // B: Delete internal refs
459
- delete this.element.bsToggle;
460
- delete this.ecmasToggle;
461
- }
462
- }
463
-
464
- /**
465
- * Add `bootstrapToggle` prototype function to HTML Elements
466
- * Enables execution when used with HTML - ex: `document.getElementById('toggle').bootstrapToggle('on')`
467
- */
468
- Element.prototype.bootstrapToggle = function (options, silent) {
469
- let _bsToggle = this.bsToggle || new Toggle(this, options);
470
-
471
- // Execute method calls
472
- if (options && typeof options === "string") {
473
- if (options.toLowerCase() == "toggle") _bsToggle.toggle(silent);
474
- else if (options.toLowerCase() == "on") _bsToggle.on(silent);
475
- else if (options.toLowerCase() == "off") _bsToggle.off(silent);
476
- else if (options.toLowerCase() == "indeterminate")
477
- _bsToggle.indeterminate(silent);
478
- else if (options.toLowerCase() == "determinate")
479
- _bsToggle.determinate(silent);
480
- else if (options.toLowerCase() == "enable") _bsToggle.enable();
481
- else if (options.toLowerCase() == "disable") _bsToggle.disable();
482
- else if (options.toLowerCase() == "readonly") _bsToggle.readonly();
483
- else if (options.toLowerCase() == "destroy") _bsToggle.destroy();
484
- }
485
- };
486
-
487
- /**
488
- * Replace all `input[type=checkbox][data-toggle="toggle"]` inputs with "Bootstrap-Toggle"
489
- * Executes once page elements have rendered enabling script to be placed in `<head>`
490
- */
491
- if (typeof window !== "undefined")
492
- window.onload = function () {
493
- document
494
- .querySelectorAll('input[type=checkbox][data-toggle="toggle"]')
495
- .forEach(function (ele) {
496
- ele.bootstrapToggle();
497
- });
498
- };
499
-
500
- // Export library if possible
501
- if (typeof module !== "undefined" && module.exports) {
502
- module.exports = Toggle;
503
- }
504
- })();
1
+ /* Copyright Notice
2
+ * bootstrap5-toggle v5.1.0
3
+ * https://palcarazm.github.io/bootstrap5-toggle/
4
+ * @author 2011-2014 Min Hur (https://github.com/minhur)
5
+ * @author 2018-2019 Brent Ely (https://github.com/gitbrent)
6
+ * @author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)
7
+ * @funding GitHub Sponsors
8
+ * @see https://github.com/sponsors/palcarazm
9
+ * @license MIT
10
+ * @see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE
11
+ */
12
+
13
+
14
+ "use strict";
15
+ function sanitize(text) {
16
+ if (!text) return text; // handle null or undefined
17
+ var map = {
18
+ "&": "&amp;",
19
+ "<": "&lt;",
20
+ ">": "&gt;",
21
+ '"': "&quot;",
22
+ "'": "&#39;",
23
+ "/": "&#x2F;",
24
+ };
25
+ return text.replace(/[&<>"'/]/g, function (m) {
26
+ return map[m];
27
+ });
28
+ }
29
+
30
+ (function () {
31
+ /**
32
+ * `Toggle` is instantiated for each toggle-button
33
+ */
34
+ class Toggle {
35
+ constructor(element, options) {
36
+ const DEPRECATION = {
37
+ value:
38
+ "BOOTSTRAP TOGGLE DEPRECATION CHECK -- a0Jhux0QySypjjs4tLtEo8xT2kx0AbYaq9K6mgNjWSs0HF0L8T8J0M0o3Kr7zkm7 --",
39
+ ATTRIBUTE: "attribute",
40
+ OPTION: "option",
41
+ log: function (type, oldlabel, newlabel) {
42
+ console.warn(
43
+ `Bootstrap Toggle deprecation warning: Using ${oldlabel} ${type} is deprected. Use ${newlabel} instead.`
44
+ );
45
+ },
46
+ };
47
+ const DEFAULTS = {
48
+ onlabel: "On",
49
+ onstyle: "primary",
50
+ onvalue: null,
51
+ ontitle: null,
52
+ offlabel: "Off",
53
+ offstyle: "secondary",
54
+ offvalue: null,
55
+ offtitle: null,
56
+ size: "",
57
+ style: "",
58
+ width: null,
59
+ height: null,
60
+ tabindex: 0,
61
+ tristate: false,
62
+ name: null,
63
+ };
64
+ options = options || {};
65
+
66
+ // A: Capture ref to HMTL element
67
+ this.element = element;
68
+
69
+ // B: Set options
70
+ this.options = {
71
+ onlabel:
72
+ sanitize(this.element.getAttribute("data-onlabel")) ||
73
+ options.onlabel ||
74
+ DEPRECATION.value ||
75
+ DEFAULTS.onlabel,
76
+ onstyle:
77
+ sanitize(this.element.getAttribute("data-onstyle")) ||
78
+ options.onstyle ||
79
+ DEFAULTS.onstyle,
80
+ onvalue:
81
+ sanitize(this.element.getAttribute("value")) ||
82
+ sanitize(this.element.getAttribute("data-onvalue")) ||
83
+ options.onvalue ||
84
+ DEFAULTS.onvalue,
85
+ ontitle:
86
+ sanitize(this.element.getAttribute("data-ontitle")) ||
87
+ options.ontitle ||
88
+ sanitize(this.element.getAttribute("title")) ||
89
+ DEFAULTS.ontitle,
90
+ offlabel:
91
+ sanitize(this.element.getAttribute("data-offlabel")) ||
92
+ options.offlabel ||
93
+ DEPRECATION.value ||
94
+ DEFAULTS.offlabel,
95
+ offstyle:
96
+ sanitize(this.element.getAttribute("data-offstyle")) ||
97
+ options.offstyle ||
98
+ DEFAULTS.offstyle,
99
+ offvalue:
100
+ sanitize(this.element.getAttribute("data-offvalue")) ||
101
+ options.offvalue ||
102
+ DEFAULTS.offvalue,
103
+ offtitle:
104
+ sanitize(this.element.getAttribute("data-offtitle")) ||
105
+ options.offtitle ||
106
+ sanitize(this.element.getAttribute("title")) ||
107
+ DEFAULTS.offtitle,
108
+ size:
109
+ sanitize(this.element.getAttribute("data-size")) ||
110
+ options.size ||
111
+ DEFAULTS.size,
112
+ style:
113
+ sanitize(this.element.getAttribute("data-style")) ||
114
+ options.style ||
115
+ DEFAULTS.style,
116
+ width:
117
+ sanitize(this.element.getAttribute("data-width")) ||
118
+ options.width ||
119
+ DEFAULTS.width,
120
+ height:
121
+ sanitize(this.element.getAttribute("data-height")) ||
122
+ options.height ||
123
+ DEFAULTS.height,
124
+ tabindex:
125
+ sanitize(this.element.getAttribute("tabindex")) ||
126
+ options.tabindex ||
127
+ DEFAULTS.tabindex,
128
+ tristate:
129
+ this.element.hasAttribute("tristate") ||
130
+ options.tristate ||
131
+ DEFAULTS.tristate,
132
+ name:
133
+ sanitize(this.element.getAttribute("name")) ||
134
+ options.name ||
135
+ DEFAULTS.name,
136
+ };
137
+
138
+ // C: Check deprecations
139
+ if (this.options.onlabel === DEPRECATION.value) {
140
+ if (sanitize(this.element.getAttribute("data-on"))) {
141
+ DEPRECATION.log(DEPRECATION.ATTRIBUTE, "data-on", "data-onlabel");
142
+ this.options.onlabel = sanitize(this.element.getAttribute("data-on"));
143
+ } else if (options.on) {
144
+ DEPRECATION.log(DEPRECATION.OPTION, "on", "onlabel");
145
+ this.options.onlabel = options.on;
146
+ } else {
147
+ this.options.onlabel = DEFAULTS.onlabel;
148
+ }
149
+ }
150
+ if (this.options.offlabel === DEPRECATION.value) {
151
+ if (sanitize(this.element.getAttribute("data-off"))) {
152
+ DEPRECATION.log(DEPRECATION.ATTRIBUTE, "data-off", "data-offlabel");
153
+ this.options.offlabel = sanitize(
154
+ this.element.getAttribute("data-off")
155
+ );
156
+ } else if (options.off) {
157
+ DEPRECATION.log(DEPRECATION.OPTION, "off", "offlabel");
158
+ this.options.offlabel = options.off;
159
+ } else {
160
+ this.options.offlabel = DEFAULTS.offlabel;
161
+ }
162
+ }
163
+
164
+ // LAST: Render Toggle
165
+ this.render();
166
+ }
167
+ render() {
168
+ function calcH(el) {
169
+ const styles = window.getComputedStyle(el);
170
+ const height = el.offsetHeight;
171
+ const borderTopWidth = parseFloat(styles.borderTopWidth);
172
+ const borderBottomWidth = parseFloat(styles.borderBottomWidth);
173
+ const paddingTop = parseFloat(styles.paddingTop);
174
+ const paddingBottom = parseFloat(styles.paddingBottom);
175
+
176
+ return (
177
+ height -
178
+ borderBottomWidth -
179
+ borderTopWidth -
180
+ paddingTop -
181
+ paddingBottom
182
+ );
183
+ }
184
+ // 0: Parse size
185
+ let size;
186
+ switch (this.options.size) {
187
+ case "large":
188
+ case "lg":
189
+ size = "btn-lg";
190
+ break;
191
+ case "small":
192
+ case "sm":
193
+ size = "btn-sm";
194
+ break;
195
+ case "mini":
196
+ case "xs":
197
+ size = "btn-xs";
198
+ break;
199
+ default:
200
+ size = "";
201
+ break;
202
+ }
203
+
204
+ // 1: On
205
+ let ecmasToggleOn = document.createElement("span");
206
+ ecmasToggleOn.setAttribute(
207
+ "class",
208
+ "btn btn-" + this.options.onstyle + " " + size
209
+ );
210
+ ecmasToggleOn.innerHTML = this.options.onlabel;
211
+ if (this.options.ontitle) {
212
+ ecmasToggleOn.setAttribute("title", this.options.ontitle);
213
+ }
214
+
215
+ // 2: Off
216
+ let ecmasToggleOff = document.createElement("span");
217
+ ecmasToggleOff.setAttribute(
218
+ "class",
219
+ "btn btn-" + this.options.offstyle + " " + size
220
+ );
221
+ ecmasToggleOff.innerHTML = this.options.offlabel;
222
+ if (this.options.offtitle) {
223
+ ecmasToggleOff.setAttribute("title", this.options.offtitle);
224
+ }
225
+
226
+ // 3: Handle
227
+ let ecmasToggleHandle = document.createElement("span");
228
+ ecmasToggleHandle.setAttribute("class", "toggle-handle btn " + size);
229
+
230
+ // 4: Toggle Group
231
+ let ecmasToggleGroup = document.createElement("div");
232
+ ecmasToggleGroup.setAttribute("class", "toggle-group");
233
+ ecmasToggleGroup.appendChild(ecmasToggleOn);
234
+ ecmasToggleGroup.appendChild(ecmasToggleOff);
235
+ ecmasToggleGroup.appendChild(ecmasToggleHandle);
236
+
237
+ // 5: Toggle
238
+ let ecmasToggle = document.createElement("div");
239
+ ecmasToggle.setAttribute("class", "toggle btn");
240
+ ecmasToggle.classList.add(
241
+ this.element.checked
242
+ ? "btn-" + this.options.onstyle
243
+ : "btn-" + this.options.offstyle
244
+ );
245
+ ecmasToggle.setAttribute("tabindex", this.options.tabindex);
246
+ if (!this.element.checked) ecmasToggle.classList.add("off");
247
+ if (this.options.size) ecmasToggle.classList.add(size);
248
+ if (this.options.style) {
249
+ this.options.style.split(" ").forEach((style) => {
250
+ ecmasToggle.classList.add(style);
251
+ });
252
+ }
253
+ if (this.element.disabled || this.element.readOnly) {
254
+ ecmasToggle.classList.add("disabled");
255
+ ecmasToggle.setAttribute("disabled", "disabled");
256
+ }
257
+
258
+ // 6: Set form values
259
+ if (this.options.onvalue)
260
+ this.element.setAttribute("value", this.options.onvalue);
261
+ let invElement = null;
262
+ if (this.options.offvalue) {
263
+ invElement = this.element.cloneNode();
264
+ invElement.setAttribute("value", this.options.offvalue);
265
+ invElement.setAttribute("data-toggle", "invert-toggle");
266
+ invElement.removeAttribute("id");
267
+ invElement.checked = !this.element.checked;
268
+ }
269
+
270
+ // 7: Replace HTML checkbox with Toggle-Button
271
+ this.element.parentElement.insertBefore(ecmasToggle, this.element);
272
+ ecmasToggle.appendChild(this.element);
273
+ if (invElement) ecmasToggle.appendChild(invElement);
274
+ ecmasToggle.appendChild(ecmasToggleGroup);
275
+
276
+ // 8: Set button W/H, lineHeight
277
+ {
278
+ // A: Set style W/H
279
+ // NOTE: `offsetWidth` returns *rounded* integer values, so use `getBoundingClientRect` instead.
280
+ if (this.options.width) {
281
+ ecmasToggle.style.width = `${this.options.width}px`;
282
+ } else {
283
+ ecmasToggle.style["min-width"] = "100px"; // First approach for better calculation
284
+ ecmasToggle.style["min-width"] = `${
285
+ Math.max(
286
+ ecmasToggleOn.getBoundingClientRect().width,
287
+ ecmasToggleOff.getBoundingClientRect().width
288
+ ) +
289
+ ecmasToggleHandle.getBoundingClientRect().width / 2
290
+ }px`;
291
+ }
292
+
293
+ if (this.options.height) {
294
+ ecmasToggle.style.height = `${this.options.height}px`;
295
+ } else {
296
+ ecmasToggle.style["min-height"] = "36px"; // First approach for better calculation
297
+ ecmasToggle.style["min-height"] = `${Math.max(
298
+ ecmasToggleOn.getBoundingClientRect().height,
299
+ ecmasToggleOff.getBoundingClientRect().height
300
+ )}px`;
301
+ }
302
+
303
+ // B: Apply on/off class
304
+ ecmasToggleOn.classList.add("toggle-on");
305
+ ecmasToggleOff.classList.add("toggle-off");
306
+
307
+ // C: Finally, set lineHeight if needed
308
+ if (this.options.height) {
309
+ ecmasToggleOn.style.lineHeight = calcH(ecmasToggleOn) + "px";
310
+ ecmasToggleOff.style.lineHeight = calcH(ecmasToggleOff) + "px";
311
+ }
312
+ }
313
+
314
+ // 9: Add listeners
315
+ ecmasToggle.addEventListener(
316
+ "touchstart",
317
+ (e) => {
318
+ this.#toggleActionPerformed(e);
319
+ },
320
+ { passive: true }
321
+ );
322
+ ecmasToggle.addEventListener("click", (e) => {
323
+ this.#toggleActionPerformed(e);
324
+ });
325
+ ecmasToggle.addEventListener("keypress", (e) => {
326
+ if (e.key == " ") {
327
+ this.#toggleActionPerformed(e);
328
+ }
329
+ });
330
+
331
+ if (this.element.id) {
332
+ document
333
+ .querySelectorAll('label[for="' + this.element.id + '"]')
334
+ .forEach((label) => {
335
+ label.addEventListener("touchstart", (_e) => {
336
+ this.toggle();
337
+ ecmasToggle.focus();
338
+ });
339
+ label.addEventListener("click", (_e) => {
340
+ this.toggle();
341
+ ecmasToggle.focus();
342
+ });
343
+ });
344
+ }
345
+
346
+ // 10: Set elements to bootstrap object
347
+ this.ecmasToggle = ecmasToggle;
348
+ this.invElement = invElement;
349
+
350
+ // 11: Keep reference to this instance for subsequent calls via `getElementById().bootstrapToggle()`
351
+ this.element.bsToggle = this;
352
+ }
353
+
354
+ /**
355
+ * Trigger actions
356
+ * @param {Event} e event
357
+ */
358
+ #toggleActionPerformed(e) {
359
+ if (this.options.tristate) {
360
+ if (this.ecmasToggle.classList.contains("indeterminate")) {
361
+ this.determinate(true);
362
+ this.toggle();
363
+ } else {
364
+ this.indeterminate();
365
+ }
366
+ } else {
367
+ this.toggle();
368
+ }
369
+ }
370
+
371
+ toggle(silent = false) {
372
+ if (this.element.checked) this.off(silent);
373
+ else this.on(silent);
374
+ }
375
+
376
+ on(silent = false) {
377
+ if (this.element.disabled || this.element.readOnly) return false;
378
+ this.ecmasToggle.classList.remove("btn-" + this.options.offstyle);
379
+ this.ecmasToggle.classList.add("btn-" + this.options.onstyle);
380
+ this.ecmasToggle.classList.remove("off");
381
+ this.element.checked = true;
382
+ if (this.invElement) this.invElement.checked = false;
383
+ if (!silent) this.trigger();
384
+ }
385
+
386
+ off(silent = false) {
387
+ if (this.element.disabled || this.element.readOnly) return false;
388
+ this.ecmasToggle.classList.remove("btn-" + this.options.onstyle);
389
+ this.ecmasToggle.classList.add("btn-" + this.options.offstyle);
390
+ this.ecmasToggle.classList.add("off");
391
+ this.element.checked = false;
392
+ if (this.invElement) this.invElement.checked = true;
393
+ if (!silent) this.trigger();
394
+ }
395
+
396
+ indeterminate(silent = false) {
397
+ if (
398
+ !this.options.tristate ||
399
+ this.element.disabled ||
400
+ this.element.readOnly
401
+ )
402
+ return false;
403
+ this.ecmasToggle.classList.add("indeterminate");
404
+ this.element.indeterminate = true;
405
+ this.element.removeAttribute("name");
406
+ if (this.invElement) this.invElement.indeterminate = true;
407
+ if (this.invElement) this.invElement.removeAttribute("name");
408
+ if (!silent) this.trigger();
409
+ }
410
+
411
+ determinate(silent = false) {
412
+ if (
413
+ !this.options.tristate ||
414
+ this.element.disabled ||
415
+ this.element.readOnly
416
+ )
417
+ return false;
418
+ this.ecmasToggle.classList.remove("indeterminate");
419
+ this.element.indeterminate = false;
420
+ if (this.options.name)
421
+ this.element.setAttribute("name", this.options.name);
422
+ if (this.invElement) this.invElement.indeterminate = false;
423
+ if (this.invElement && this.options.name)
424
+ this.invElement.setAttribute("name", this.options.name);
425
+ if (!silent) this.trigger();
426
+ }
427
+
428
+ enable() {
429
+ this.ecmasToggle.classList.remove("disabled");
430
+ this.ecmasToggle.removeAttribute("disabled");
431
+ this.element.removeAttribute("disabled");
432
+ this.element.removeAttribute("readonly");
433
+ if (this.invElement) {
434
+ this.invElement.removeAttribute("disabled");
435
+ this.invElement.removeAttribute("readonly");
436
+ }
437
+ }
438
+
439
+ disable() {
440
+ this.ecmasToggle.classList.add("disabled");
441
+ this.ecmasToggle.setAttribute("disabled", "");
442
+ this.element.setAttribute("disabled", "");
443
+ this.element.removeAttribute("readonly");
444
+ if (this.invElement) {
445
+ this.invElement.setAttribute("disabled", "");
446
+ this.invElement.removeAttribute("readonly");
447
+ }
448
+ }
449
+
450
+ readonly() {
451
+ this.ecmasToggle.classList.add("disabled");
452
+ this.ecmasToggle.setAttribute("disabled", "");
453
+ this.element.removeAttribute("disabled");
454
+ this.element.setAttribute("readonly", "");
455
+ if (this.invElement) {
456
+ this.invElement.removeAttribute("disabled");
457
+ this.invElement.setAttribute("readonly", "");
458
+ }
459
+ }
460
+
461
+ update(silent) {
462
+ if (this.element.disabled) this.disable();
463
+ else if (this.element.readOnly) this.readonly();
464
+ else this.enable();
465
+ if (this.element.checked) this.on(silent);
466
+ else this.off(silent);
467
+ }
468
+
469
+ trigger(silent) {
470
+ if (!silent)
471
+ this.element.dispatchEvent(new Event("change", { bubbles: true }));
472
+ }
473
+
474
+ destroy() {
475
+ // A: Remove button-group from UI, replace checkbox element
476
+ this.ecmasToggle.parentNode.insertBefore(this.element, this.ecmasToggle);
477
+ this.ecmasToggle.parentNode.removeChild(this.ecmasToggle);
478
+
479
+ // B: Delete internal refs
480
+ delete this.element.bsToggle;
481
+ delete this.ecmasToggle;
482
+ }
483
+
484
+ rerender() {
485
+ this.destroy();
486
+ this.element.bootstrapToggle();
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Add `bootstrapToggle` prototype function to HTML Elements
492
+ * Enables execution when used with HTML - ex: `document.getElementById('toggle').bootstrapToggle('on')`
493
+ */
494
+ Element.prototype.bootstrapToggle = function (options, silent) {
495
+ let _bsToggle = this.bsToggle || new Toggle(this, options);
496
+
497
+ // Execute method calls
498
+ if (options && typeof options === "string") {
499
+ if (options.toLowerCase() == "toggle") _bsToggle.toggle(silent);
500
+ else if (options.toLowerCase() == "on") _bsToggle.on(silent);
501
+ else if (options.toLowerCase() == "off") _bsToggle.off(silent);
502
+ else if (options.toLowerCase() == "indeterminate")
503
+ _bsToggle.indeterminate(silent);
504
+ else if (options.toLowerCase() == "determinate")
505
+ _bsToggle.determinate(silent);
506
+ else if (options.toLowerCase() == "enable") _bsToggle.enable();
507
+ else if (options.toLowerCase() == "disable") _bsToggle.disable();
508
+ else if (options.toLowerCase() == "readonly") _bsToggle.readonly();
509
+ else if (options.toLowerCase() == "destroy") _bsToggle.destroy();
510
+ else if (options.toLowerCase() == "rerender") _bsToggle.rerender();
511
+ }
512
+ };
513
+
514
+ /**
515
+ * Replace all `input[type=checkbox][data-toggle="toggle"]` inputs with "Bootstrap-Toggle"
516
+ * Executes once page elements have rendered enabling script to be placed in `<head>`
517
+ */
518
+ if (typeof window !== "undefined")
519
+ window.onload = function () {
520
+ document
521
+ .querySelectorAll('input[type=checkbox][data-toggle="toggle"]')
522
+ .forEach(function (ele) {
523
+ ele.bootstrapToggle();
524
+ });
525
+ };
526
+
527
+ // Export library if possible
528
+ if (typeof module !== "undefined" && module.exports) {
529
+ module.exports = Toggle;
530
+ }
531
+ })();