493 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			493 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * BSDialog5
 | |
|  * @version v0.2.0.055 (2023/11/12 1428)
 | |
|  */
 | |
| var BSDialog5 = {
 | |
| 	Default: function() {
 | |
| 		return {
 | |
| 			ShowOptions: {
 | |
| 				ID: null,
 | |
| 				Title: "",
 | |
| 				Message: "",
 | |
| 				URL: null,
 | |
| 				Size: "md",
 | |
| 				Colour: "secondary",
 | |
| 				ShowFooter: true,
 | |
| 				EasyClose: true
 | |
| 			},
 | |
| 			PromptOptions: {
 | |
| 				Type: "button",
 | |
| 				Title: "",
 | |
| 				Message: "",
 | |
| 				Size: "md",
 | |
| 				EasyClose: true,
 | |
| 				Buttons: [
 | |
| 					{ Label: "Yes", Value: "Yes", Colour: "primary" },
 | |
| 					{ Label: "No", Value: "No", Colour: "secondary" },
 | |
| 					{ Label: "Cancel", Value: "Cancel", Colour: "secondary" }
 | |
| 				],
 | |
| 				Textbox: {
 | |
| 					Label: "",
 | |
| 					LabelSize: 4,
 | |
| 					Placeholder: "",
 | |
| 					Value: "",
 | |
| 					BoxSize: 8
 | |
| 				}
 | |
| 			},
 | |
| 			UpdateOptions: {
 | |
| 				ID: null,
 | |
| 				Title: null,
 | |
| 				Body: null,
 | |
| 				BodyURL: null,
 | |
| 				Footer: null,
 | |
| 				Size: null
 | |
| 			}
 | |
| 		};
 | |
| 	},
 | |
| 	Show: async function (options) {
 | |
| 		const a = this;
 | |
| 		const _options = Object.assign(a.Default().ShowOptions, options);
 | |
| 
 | |
| 		a.id = _options.ID;
 | |
| 		a.pfx = "bsdia5_";
 | |
| 
 | |
| 		a.addBackdrop();
 | |
| 		a.addModal(_options.ID, _options.Title, _options.Size, _options.ShowFooter, _options.EasyClose, _options.Colour);
 | |
| 
 | |
| 		if (_options.URL == null) {
 | |
| 			await a.Update({ ID: _options.ID, Body: _options.Message });
 | |
| 		} else {
 | |
| 			if (a.isNullOrWhitespace(_options.URL)) {
 | |
| 				await a.Update({ ID: _options.ID, Body: URL });
 | |
| 			} else {
 | |
| 				if (_options.URL.startsWith("http://") || _options.URL.startsWith("https://") || _options.URL.startsWith("/")) {
 | |
| 					await a.UpdateBodyRemote(_options.ID, _options.URL);
 | |
| 				} else {
 | |
| 					await a.Update({ ID: _options.ID, Body: URL });
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| 	Prompt: async function (options) {
 | |
| 		const a = this;
 | |
| 		const id = "prompt" + Math.floor(Math.random() * 10000) + 1000;
 | |
| 		const _options = Object.assign(a.Default().PromptOptions, options);
 | |
| 
 | |
| 		switch (_options.Type) {
 | |
| 			case "textbox":
 | |
| 				return await a.showTextboxPrompt(id, _options);
 | |
| 			case "button":
 | |
| 			default:
 | |
| 				return await a.showButtonPrompt(id, _options);
 | |
| 		}
 | |
| 	},
 | |
| 	Clear: function () {
 | |
| 		this.GetBody().querySelectorAll(".modal").forEach(function(e) {
 | |
| 			e.parentNode.removeChild(e);
 | |
| 		});
 | |
| 
 | |
| 		this.removeBackdrop();
 | |
| 	},
 | |
| 	Close: function (id) {
 | |
| 		let modal = this.Find(id);
 | |
| 		if (modal === null) {
 | |
| 		 	return;
 | |
| 		}
 | |
| 
 | |
| 		modal.Modal.parentNode.removeChild(modal.Modal);
 | |
| 
 | |
| 		modal = this.Find(id);
 | |
| 		if (modal !== null) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		this.removeBackdrop();
 | |
| 	},
 | |
| 	Update: async function (options) {
 | |
| 		const a = this;
 | |
| 		let _options = Object.assign(a.Default().UpdateOptions, options);
 | |
| 
 | |
| 		const modal = a.Find(_options.ID);
 | |
| 		if (modal === null) {
 | |
| 		 	return;
 | |
| 		}
 | |
| 
 | |
| 		if (!this.isNullOrWhitespace(_options.Title)) modal.Title.innerHTML = _options.Title;
 | |
| 
 | |
| 		if (!this.isNullOrWhitespace(_options.Body)) a.html(modal.Body, _options.Body);
 | |
| 
 | |
| 		if (!this.isNullOrWhitespace(_options.BodyURL)) {
 | |
| 			if (_options.BodyURL.startsWith("http://") || _options.BodyURL.startsWith("https://") || _options.BodyURL.startsWith("/")) {
 | |
| 				// ok
 | |
| 			} else {
 | |
| 				_options.BodyURL = null;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (!this.isNullOrWhitespace(_options.BodyURL)) {
 | |
| 			await a.UpdateBodyRemote(_options.ID, _options.BodyURL);
 | |
| 		}
 | |
| 
 | |
| 		if (!this.isNullOrWhitespace(_options.Footer)) a.html(modal.Footer, _options.Footer);
 | |
| 
 | |
| 		if (!this.isNullOrWhitespace(_options.Size)) {
 | |
| 			modal.Modal.classList.remove("modal-sm");
 | |
| 			modal.Modal.classList.remove("modal-md");
 | |
| 			modal.Modal.classList.remove("modal-lg");
 | |
| 			modal.Modal.classList.remove("modal-xl");
 | |
| 			modal.Modal.classList.add("modal-" + _options.Size);
 | |
| 		}
 | |
| 
 | |
| 	},
 | |
| 	UpdateBodyRemote: async function (id, url) {
 | |
| 		const a = this;
 | |
| 
 | |
| 		if (!a.Exists(id)) {
 | |
| 		 	return;
 | |
| 		}
 | |
| 
 | |
| 		await fetch(url, {
 | |
| 			cache: 'no-cache',
 | |
| 			credentials: 'same-origin'
 | |
| 		}).then(data => data.text()).then(data => {
 | |
| 			a.Update({ ID: id, Body: data });
 | |
| 		}).catch((error) => {
 | |
| 			a.Update({ ID: id, Body: "Error: " + error });
 | |
| 		});
 | |
| 	},
 | |
| 	Exists: function (id) {
 | |
| 		return (this.Find(id) !== null);
 | |
| 	},
 | |
| 	Find: function (id) {
 | |
| 		const modal = this.GetBody().querySelectorAll("#" + this.pfx + id + ".modal");
 | |
| 		if (!modal) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		if (modal.length <= 0) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		return {
 | |
| 			Title: modal[0].querySelectorAll(".modal-title")[0],
 | |
| 			Header: modal[0].querySelectorAll(".modal-header")[0],
 | |
| 			Body: modal[0].querySelectorAll(".modal-body")[0],
 | |
| 			Footer: modal[0].querySelectorAll(".modal-footer")[0],
 | |
| 			Close: modal[0].querySelectorAll("[data-bs-dismiss='modal']"),
 | |
| 			Modal: modal[0]
 | |
| 		};
 | |
| 	},
 | |
| 	GetBody: function() {
 | |
| 		return document.getElementsByTagName("body")[0];
 | |
| 	},
 | |
| 	addBackdrop: function () {
 | |
| 		const a = this;
 | |
| 
 | |
| 		// don't allow duplicates
 | |
| 		if (a.GetBody().querySelectorAll(".modal-backdrop").length > 0) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		a.appendHtml(a.GetBody(), '<div class="modal-backdrop fade show"></div>');
 | |
| 
 | |
| 		// lock background
 | |
| 		a.GetBody().classList.add("modal-open");
 | |
| 		a.GetBody().style.overflow = "hidden";
 | |
| 	},
 | |
| 	addModal: function (id, title, size, showFooter, easyClose, closeColour) {
 | |
| 		const a = this;
 | |
| 
 | |
| 		// don't allow duplicates
 | |
| 		let modal = a.Find(id);
 | |
| 		if (modal !== null) {
 | |
| 		 	return;
 | |
| 		}
 | |
| 
 | |
| 		let html = "";
 | |
| 		html += '<div class="modal modal-' + size + ' fade show d-block" id="' + a.pfx + id + '" tabindex="-1" aria-modal="true" role="dialog">';
 | |
| 		html += '  <div class="modal-dialog">';
 | |
| 		html += '    <div class="modal-content">';
 | |
| 		html += '      <div class="modal-header">';
 | |
| 		html += '        <span class="modal-title fs-5" style="cursor: default; user-select: none;">' + title + '</span>';
 | |
| 		html += '        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" style="user-select: none;"></button>';
 | |
| 		html += '      </div>';
 | |
| 		html += '      <div class="modal-body">';
 | |
| 
 | |
| 		html += '<div class="row mt-3 pb-3">';
 | |
| 		html += '  <div class="col-12 text-center">';
 | |
| 		html += '    <div class="spinner-border">';
 | |
| 		html += '	     <span class="visually-hidden">Loading...</span>';
 | |
| 		html += '    </div>';
 | |
| 		html += '  </div>';
 | |
| 		html += '</div>';
 | |
| 
 | |
| 		html += '      </div>';
 | |
| 
 | |
| 		if (showFooter === true) {
 | |
| 			html += '      <div class="modal-footer">';
 | |
| 			html += '        <button type="button" class="btn btn-' + closeColour + '" data-bs-dismiss="modal">Close</button>';
 | |
| 			html += '      </div>';
 | |
| 		}
 | |
| 
 | |
| 		html += '    </div>';
 | |
| 		html += '  </div>';
 | |
| 		html += '</div>';
 | |
| 
 | |
| 		a.appendHtml(a.GetBody(), html);
 | |
| 
 | |
| 		modal = a.Find(id);
 | |
| 		if (modal === null) {
 | |
| 			return;
 | |
| 	 	}
 | |
| 
 | |
| 		// Close on backdrp
 | |
| 		if (easyClose === true) {
 | |
| 			modal.Modal.addEventListener("click", function(e){
 | |
| 				e.stopPropagation();
 | |
| 				e.preventDefault();
 | |
| 
 | |
| 				a.Close(id);
 | |
| 			});
 | |
| 
 | |
| 			const modalContent = modal.Modal.querySelectorAll(".modal-content")[0];
 | |
| 			modalContent.addEventListener("click", function(e){
 | |
| 				e.stopPropagation();
 | |
| 				e.preventDefault();
 | |
| 			});
 | |
| 		}
 | |
| 
 | |
| 		modal.Close.forEach(function(e){
 | |
| 			e.addEventListener("click", function(e){
 | |
| 				e.stopPropagation();
 | |
| 				e.preventDefault();
 | |
| 
 | |
| 					a.Close(id);
 | |
| 				});
 | |
| 		});
 | |
| 
 | |
| 		modal.Title.addEventListener("dblclick", function(e){
 | |
| 			e.stopPropagation();
 | |
| 			e.preventDefault();
 | |
| 
 | |
| 			a.toggleSize();
 | |
| 		});
 | |
| 	},
 | |
| 
 | |
| 
 | |
| 
 | |
| 	showButtonPrompt: async function (id, options) {
 | |
| 		const a = this;
 | |
| 
 | |
| 		return await new Promise(async (resolve, reject) => {
 | |
| 			await a.Show({
 | |
| 				ID: id,
 | |
| 				Title: options.Title,
 | |
| 				Message: options.Message,
 | |
| 				Size: options.Size,
 | |
| 				EasyClose: options.EasyClose
 | |
| 			});
 | |
| 
 | |
| 			let html = '';
 | |
| 			options.Buttons.forEach(function(e) {
 | |
| 				html += '<button type="button" class="btn btn-' + e.Colour + '" data-prompt-value="' + e.Value + '">' + e.Label + '</button>';
 | |
| 			});
 | |
| 
 | |
| 			a.Update({ ID: id, Footer: html });
 | |
| 
 | |
| 			const modal = a.Find(id);
 | |
| 			modal.Footer.querySelectorAll("button").forEach(function(button) {
 | |
| 				button.addEventListener("click", function(e){
 | |
| 					e.stopPropagation();
 | |
| 					e.preventDefault();
 | |
| 
 | |
| 					const value = button.getAttribute("data-prompt-value");
 | |
| 
 | |
| 					a.Close(id);
 | |
| 
 | |
| 					resolve(value);
 | |
| 				});
 | |
| 			});
 | |
| 
 | |
| 			modal.Close.forEach(function(sender) {
 | |
| 				sender.addEventListener("click", function(e){
 | |
| 					e.stopPropagation();
 | |
| 					e.preventDefault();
 | |
| 
 | |
| 					a.Close(id);
 | |
| 
 | |
| 					resolve("");
 | |
| 				});
 | |
| 			});
 | |
| 
 | |
| 		});
 | |
| 	},
 | |
| 	showTextboxPrompt: async function (id, options) {
 | |
| 		const a = this;
 | |
| 
 | |
| 		return await new Promise(async (resolve, reject) => {
 | |
| 			options.Buttons = [
 | |
| 				{ Label: "OK", Value: "", Colour: "primary" },
 | |
| 				{ Label: "Cancel", Value: "", Colour: "secondary" }
 | |
| 			];
 | |
| 
 | |
| 			await a.Show({
 | |
| 				ID: id,
 | |
| 				Title: options.Title,
 | |
| 				Message: options.Message,
 | |
| 				Size: options.Size,
 | |
| 				EasyClose: options.EasyClose
 | |
| 			});
 | |
| 
 | |
| 			let body = '';
 | |
| 
 | |
| 			if (!a.isNullOrWhitespace(options.Message)) {
 | |
| 				body += '<p>' + options.Message + '</p>';
 | |
| 			}
 | |
| 
 | |
| 			body += '<div class="form-group row">';
 | |
| 
 | |
| 			if (a.isNullOrWhitespace(options.Textbox.Label) || (options.Textbox.LabelSize <= 0)) {
 | |
| 				body += '<div class="col-sm-12">';
 | |
| 				body += '<input type="text" class="form-control" id="textbox' + id + '" placeholder="' + options.Textbox.Placeholder + '" value="' + options.Textbox.Value + '">';
 | |
| 				body += '</div>';
 | |
| 			} else {
 | |
| 				body += '<label for="textbox' + id + '" class="col-sm-' + options.Textbox.LabelSize + ' col-form-label">' + options.Textbox.Label + '</label>';
 | |
| 				body += '<div class="col-sm-' + options.Textbox.BoxSize + '">';
 | |
| 				body += '<input type="text" class="form-control" id="textbox' + id + '" placeholder="' + options.Textbox.Placeholder + '" value="' + options.Textbox.Value + '">';
 | |
| 				body += '</div>';
 | |
| 			}
 | |
| 
 | |
| 			body += '</div>';
 | |
| 
 | |
| 			let footer = '';
 | |
| 			options.Buttons.forEach(function(e) {
 | |
| 				footer += '<button type="button" class="btn btn-' + e.Colour + '" data-prompt-value="' + e.Value + '">' + e.Label + '</button>';
 | |
| 			});
 | |
| 
 | |
| 			a.Update({
 | |
| 				ID: id,
 | |
| 				Body: body,
 | |
| 				Footer: footer
 | |
| 			});
 | |
| 
 | |
| 			const modal = a.Find(id);
 | |
| 			const buttons = modal.Footer.querySelectorAll("button");
 | |
| 
 | |
| 			buttons[0].addEventListener("click", function(e){
 | |
| 				e.stopPropagation();
 | |
| 				e.preventDefault();
 | |
| 
 | |
| 				const value = modal.Body.querySelectorAll("input")[0].value;
 | |
| 
 | |
| 				a.Close(id);
 | |
| 
 | |
| 				resolve(value);
 | |
| 			});
 | |
| 
 | |
| 			buttons[1].addEventListener("click", function(e){
 | |
| 				e.stopPropagation();
 | |
| 				e.preventDefault();
 | |
| 
 | |
| 				a.Close(id);
 | |
| 
 | |
| 				resolve("");
 | |
| 			});
 | |
| 
 | |
| 			modal.Close.forEach(function(sender) {
 | |
| 				sender.addEventListener("click", function(e){
 | |
| 					e.stopPropagation();
 | |
| 					e.preventDefault();
 | |
| 
 | |
| 					a.Close(id);
 | |
| 
 | |
| 					resolve("");
 | |
| 				});
 | |
| 			});
 | |
| 
 | |
| 		});
 | |
| 	},
 | |
| 
 | |
| 
 | |
| 
 | |
| 	appendHtml: function (el, html) {
 | |
| 		let node = document.createElement('template');
 | |
| 		node.innerHTML = html;
 | |
| 
 | |
| 		node = node.content.firstChild;
 | |
| 
 | |
| 		el.appendChild(node);
 | |
| 	},
 | |
| 	html: function (el, newHtml) {
 | |
| 		/// todo: replace with pure JS
 | |
| 		if (jQuery) {
 | |
| 			jQuery(el).html(newHtml);
 | |
| 		} else {
 | |
| 			el.innerHTML = newHtml;
 | |
| 		}
 | |
| 	},
 | |
| 	isNullOrWhitespace: function(e) {
 | |
| 		if (typeof (e) == "undefined") {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		if (e == null) {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		if (e == false) {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		return (e.trim().length <= 0);
 | |
| 	},
 | |
| 	removeBackdrop: function () {
 | |
| 		const a = this;
 | |
| 
 | |
| 		if (a.GetBody().querySelectorAll(".modal-backdrop").length <= 0) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (a.GetBody().querySelectorAll(".modal").length > 0) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		let backdrop = a.GetBody().querySelectorAll(".modal-backdrop")[0];
 | |
| 		backdrop.parentNode.removeChild(backdrop);
 | |
| 
 | |
| 		// unlock background
 | |
| 		a.GetBody().classList.remove("modal-open");
 | |
| 		a.GetBody().style.overflow = null;
 | |
| 	},
 | |
| 	toggleSize: function () {
 | |
| 		const a = this;
 | |
| 
 | |
| 		let modal = a.Find(a.id);
 | |
| 		if (modal === null) {
 | |
| 		 	return;
 | |
| 		}
 | |
| 
 | |
| 		if (modal.Modal.classList.contains('modal-sm')) {
 | |
| 			modal.Modal.classList.remove("modal-sm");
 | |
| 			modal.Modal.classList.add("modal-md");
 | |
| 		} else if (modal.Modal.classList.contains('modal-md')) {
 | |
| 			modal.Modal.classList.remove("modal-md");
 | |
| 			modal.Modal.classList.add("modal-lg");
 | |
| 		} else if (modal.Modal.classList.contains('modal-lg')) {
 | |
| 			modal.Modal.classList.remove("modal-lg");
 | |
| 			modal.Modal.classList.add("modal-xl");
 | |
| 		} else if (modal.Modal.classList.contains('modal-xl')) {
 | |
| 			modal.Modal.classList.remove("modal-xl");
 | |
| 			modal.Modal.classList.add("modal-xxl");
 | |
| 		} else if (modal.Modal.classList.contains('modal-xxl')) {
 | |
| 			modal.Modal.classList.remove("modal-xxl");
 | |
| 			modal.Modal.classList.add("modal-sm");
 | |
| 		} else {
 | |
| 			modal.Modal.classList.remove("modal-sm");
 | |
| 			modal.Modal.classList.remove("modal-md");
 | |
| 			modal.Modal.classList.remove("modal-lg");
 | |
| 			modal.Modal.classList.remove("modal-xl");
 | |
| 			modal.Modal.classList.remove("modal-xxl");
 | |
| 			modal.Modal.classList.add("modal-md");
 | |
| 		}
 | |
| 	}
 | |
| }; | 
