// Converse.js // https://conversejs.org // // Copyright (c) 2013-2019, the Converse.js developers // Licensed under the Mozilla Public License (MPLv2) /** * @module converse-modal */ import "backbone.vdomview"; import bootstrap from "bootstrap.native"; import converse from "@converse/headless/converse-core"; import { isString } from "lodash"; import tpl_alert from "templates/alert.html"; import tpl_alert_modal from "templates/alert_modal.html"; import tpl_prompt from "templates/prompt.html"; const { Backbone, sizzle } = converse.env; const u = converse.env.utils; converse.plugins.add('converse-modal', { initialize () { const { _converse } = this; const { __ } = _converse; _converse.BootstrapModal = Backbone.VDOMView.extend({ events: { 'click .nav-item .nav-link': 'switchTab' }, initialize () { this.render().insertIntoDOM(); this.modal = new bootstrap.Modal(this.el, { backdrop: 'static', keyboard: true }); this.el.addEventListener('hide.bs.modal', () => u.removeClass('selected', this.trigger_el), false); }, insertIntoDOM () { const container_el = _converse.chatboxviews.el.querySelector("#converse-modals"); container_el.insertAdjacentElement('beforeEnd', this.el); }, switchTab (ev) { ev.stopPropagation(); ev.preventDefault(); sizzle('.nav-link.active', this.el).forEach(el => { u.removeClass('active', this.el.querySelector(el.getAttribute('href'))); u.removeClass('active', el); }); u.addClass('active', ev.target); u.addClass('active', this.el.querySelector(ev.target.getAttribute('href'))) }, alert (message, type='primary') { const body = this.el.querySelector('.modal-body'); body.insertAdjacentHTML( 'afterBegin', tpl_alert({ 'type': `alert-${type}`, 'message': message }) ); const el = body.firstElementChild; setTimeout(() => { u.addClass('fade-out', el); setTimeout(() => u.removeElement(el), 600); }, 5000); }, show (ev) { if (ev) { ev.preventDefault(); this.trigger_el = ev.target; this.trigger_el.classList.add('selected'); } this.modal.show(); } }); _converse.Confirm = _converse.BootstrapModal.extend({ events: { 'submit .confirm': 'onConfimation' }, initialize () { this.confirmation = u.getResolveablePromise(); _converse.BootstrapModal.prototype.initialize.apply(this, arguments); this.listenTo(this.model, 'change', this.render) this.el.addEventListener('closed.bs.modal', () => this.confirmation.reject(), false); }, toHTML () { return tpl_prompt(Object.assign({__}, this.model.toJSON())); }, afterRender () { if (!this.close_handler_registered) { this.el.addEventListener('closed.bs.modal', () => { if (!this.confirmation.isResolved) { this.confirmation.reject() } }, false); this.close_handler_registered = true; } }, onConfimation (ev) { ev.preventDefault(); this.confirmation.resolve(true); this.modal.hide(); } }); _converse.Prompt = _converse.Confirm.extend({ toHTML () { return tpl_prompt(Object.assign({__}, this.model.toJSON())); }, onConfimation (ev) { ev.preventDefault(); const form_data = new FormData(ev.target); this.confirmation.resolve(form_data.get('reason')); this.modal.hide(); } }); _converse.Alert = _converse.BootstrapModal.extend({ initialize () { _converse.BootstrapModal.prototype.initialize.apply(this, arguments); this.listenTo(this.model, 'change', this.render) }, toHTML () { return tpl_alert_modal( Object.assign({__}, this.model.toJSON())); } }); /************************ BEGIN Event Listeners ************************/ _converse.api.listen.on('afterTearDown', () => { if (!_converse.chatboxviews) { return; } const container = _converse.chatboxviews.el.querySelector("#converse-modals"); if (container) { container.innerHTML = ''; } }); /************************ BEGIN API ************************/ // We extend the default converse.js API to add methods specific to MUC chat rooms. let alert, prompt, confirm; Object.assign(_converse.api, { /** * Show a confirm modal to the user. * @method _converse.api.confirm * @param { String } title - The header text for the confirmation dialog * @param { (String[]|String) } messages - The text to show to the user * @returns { Promise } A promise which resolves with true or false */ async confirm (title, messages=[]) { if (isString(messages)) { messages = [messages]; } if (confirm === undefined) { const model = new Backbone.Model({ 'title': title, 'messages': messages, 'type': 'confirm' }) confirm = new _converse.Confirm({model}); } else { confirm.model.set({ 'title': title, 'messages': messages, 'type': 'confirm' }); } confirm.show(); try { return await confirm.confirmation; } catch (e) { return false; } }, /** * Show a prompt modal to the user. * @method _converse.api.prompt * @param { String } title - The header text for the prompt * @param { (String[]|String) } messages - The prompt text to show to the user * @param { String } placeholder - The placeholder text for the prompt input * @returns { Promise } A promise which resolves with the text provided by the * user or `false` if the user canceled the prompt. */ async prompt (title, messages=[], placeholder='') { if (isString(messages)) { messages = [messages]; } if (prompt === undefined) { const model = new Backbone.Model({ 'title': title, 'messages': messages, 'placeholder': placeholder, 'type': 'prompt' }) prompt = new _converse.Prompt({model}); } else { prompt.model.set({ 'title': title, 'messages': messages, 'type': 'prompt' }); } prompt.show(); try { return await prompt.confirmation; } catch (e) { return false; } }, /** * Show an alert modal to the user. * @method _converse.api.alert * @param { ('info'|'warn'|'error') } type - The type of alert. * @param { String } title - The header text for the alert. * @param { (String[]|String) } messages - The alert text to show to the user. */ alert (type, title, messages) { if (isString(messages)) { messages = [messages]; } let level; if (type === 'error') { level = 'alert-danger'; } else if (type === 'info') { level = 'alert-info'; } else if (type === 'warn') { level = 'alert-warning'; } if (alert === undefined) { const model = new Backbone.Model({ 'title': title, 'messages': messages, 'level': level, 'type': 'alert' }) alert = new _converse.Alert({model}); } else { alert.model.set({ 'title': title, 'messages': messages, 'level': level }); } alert.show(); } }); } });