/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; /* global KeyboardManager, focusManager, Sanitizer */ // The modal dialog listen to mozbrowsershowmodalprompt event. // Blocking the current app and then show cutom modal dialog // (alert/confirm/prompt) var ModalDialog = { // Used for element id access. // e.g., 'modal-dialog-alert-ok' prefix: 'modal-dialog-', // DOM elements: {}, dialog: document.getElementById('modal-dialog'), // Get all elements when inited. getAllElements: function md_getAllElements() { var elementsID = ['alert', 'alert-ok', 'alert-message', 'prompt', 'prompt-ok', 'prompt-cancel', 'prompt-input', 'prompt-message', 'confirm', 'confirm-ok', 'confirm-cancel', 'confirm-message', 'select-one', 'select-one-cancel', 'select-one-menu', 'select-one-title', 'alert-title', 'confirm-title', 'prompt-title']; var toCamelCase = function toCamelCase(str) { return str.replace(/\-(.)/g, function replacer(str, p1) { return p1.toUpperCase(); }); }; // Loop and add element with camel style name to Modal Dialog attribute. elementsID.forEach(function createElementRef(name) { this.elements[toCamelCase(name)] = document.getElementById(this.prefix + name); }, this); this.screen = document.getElementById('screen'); this.overlay = document.getElementById('dialog-overlay'); }, // Save the events returned by mozbrowsershowmodalprompt for later use. // The events are stored according to webapp origin and in some edge cases // (in particular related to system messages) we have to handle multiple // events for one origin, so we queue them. // e.g., 'http://uitest.gaiamobile.org': [evt] currentEvents: {}, get eventForCurrentOrigin() { var originEvents = this.currentEvents[this.currentOrigin]; if (originEvents && originEvents.length) { return originEvents[0]; } return null; }, init: function md_init() { // Get all elements initially. this.getAllElements(); var elements = this.elements; // Bind events window.addEventListener('appwillclose', this); window.addEventListener('appterminated', this); window.addEventListener('resize', this); window.addEventListener('keyboardchange', this); window.addEventListener('keyboardhide', this); window.addEventListener('home', this); window.addEventListener('holdhome', this); for (var id in elements) { var tagName = elements[id].tagName.toLowerCase(); if (tagName == 'button' || tagName == 'ul') { elements[id].addEventListener('click', this); } } focusManager.addUI(this); }, // Default event handler handleEvent: function md_handleEvent(evt) { var elements = this.elements; switch (evt.type) { case 'click': if (evt.currentTarget === elements.confirmCancel || evt.currentTarget === elements.promptCancel || evt.currentTarget === elements.selectOneCancel) { this.cancelHandler(); } else if (evt.currentTarget === elements.selectOneMenu) { this.selectOneHandler(evt.target); } else { this.confirmHandler(); } break; case 'appopen': this.show(evt.target, evt.detail.origin); break; case 'home': case 'holdhome': // Inline activity, which origin is different from foreground app if (this.isVisible()) { this.cancelHandler(); } break; case 'appwillclose': // Do nothing if the app is closed at background. if (evt.detail.origin !== this.currentOrigin) { return; } // Reset currentOrigin this.hide(); break; case 'appterminated': if (this.currentEvents[evt.detail.origin]) { delete this.currentEvents[evt.detail.origin]; } break; case 'resize': case 'keyboardhide': if (!this.currentOrigin) { return; } this.setHeight(window.innerHeight); break; case 'keyboardchange': var keyboardHeight = KeyboardManager.getHeight(); this.setHeight(window.innerHeight - keyboardHeight); break; } }, processNextEvent: function md_processNextEvent() { var originEvents = this.currentEvents[this.currentOrigin]; originEvents.splice(0, 1); if (originEvents.length) { this.show(null, this.currentOrigin); return; } delete this.currentEvents[this.currentOrigin]; }, setHeight: function md_setHeight(height) { if (this.isVisible()) { this.overlay.style.height = height + 'px'; } }, // Show relative dialog and set message/input value well show: function md_show(target, origin) { if (!(origin in this.currentEvents)) { return; } this.currentOrigin = origin; var evt = this.eventForCurrentOrigin; var message = evt.detail.message || ''; var title = evt.detail.title || ''; var elements = this.elements; this.screen.classList.add('modal-dialog'); var type = evt.detail.promptType || evt.detail.type; // XXX: Bug 916658 - Remove unused l10n resources from b2g gecko // If we have removed translations from Gecko, then we can remove this. if (target && target.dataset.frameType === 'window') { title = ''; } function localizeElement(node, payload) { if (typeof payload === 'string') { node.setAttribute('data-l10n-id', payload); return; } if (typeof payload === 'object') { if (payload.raw) { node.removeAttribute('data-l10n-id'); node.textContent = payload.raw; return; } if (payload.id) { navigator.mozL10n.setAttributes(node, payload.id, payload.args); return; } } } switch (type) { case 'alert': localizeElement(elements.alertMessage, message); elements.alert.classList.add('visible'); this.setTitle('alert', title); elements.alertOk.setAttribute('data-l10n-id', evt.yesText ? evt.yesText : 'ok'); break; case 'prompt': elements.prompt.classList.add('visible'); elements.promptInput.value = evt.detail.initialValue; localizeElement(elements.promptMessage, message); this.setTitle('prompt', title); elements.promptOk.setAttribute('data-l10n-id', evt.yesText ? evt.yesText : 'ok'); elements.promptCancel.setAttribute('data-l10n-id', evt.noText ? evt.noText : 'cancel'); break; case 'confirm': elements.confirm.classList.add('visible'); localizeElement(elements.confirmMessage, message); this.setTitle('confirm', title); elements.confirmOk.setAttribute('data-l10n-id', evt.yesText ? evt.yesText : 'ok'); elements.confirmCancel.setAttribute('data-l10n-id', evt.noText ? evt.noText : 'cancel'); break; case 'selectone': this.buildSelectOneDialog(message); elements.selectOne.classList.add('visible'); break; } this.setHeight(window.innerHeight); focusManager.focus(); }, hide: function md_hide() { var evt = this.eventForCurrentOrigin; var type = evt.detail.promptType; if (type == 'prompt') { this.elements.promptInput.blur(); } this.currentOrigin = null; this.screen.classList.remove('modal-dialog'); this.elements[type].classList.remove('visible'); }, setTitle: function md_setTitle(type, title) { this.elements[type + 'Title'].setAttribute('data-l10n-id', title); }, // When user clicks OK button on alert/confirm/prompt confirmHandler: function md_confirmHandler() { this.screen.classList.remove('modal-dialog'); var elements = this.elements; var evt = this.eventForCurrentOrigin; var type = evt.detail.promptType || evt.detail.type; switch (type) { case 'alert': elements.alert.classList.remove('visible'); break; case 'prompt': evt.detail.returnValue = elements.promptInput.value; elements.prompt.classList.remove('visible'); break; case 'confirm': evt.detail.returnValue = true; elements.confirm.classList.remove('visible'); break; } if (evt.isPseudo && evt.callback) { evt.callback(evt.detail.returnValue); } if (evt.detail.unblock) { evt.detail.unblock(); } focusManager.focus(); this.processNextEvent(); }, // When user clicks cancel button on confirm/prompt or // when the user try to escape the dialog with the escape key cancelHandler: function md_cancelHandler() { var evt = this.eventForCurrentOrigin; this.screen.classList.remove('modal-dialog'); var elements = this.elements; var type = evt.detail.promptType || evt.detail.type; switch (type) { case 'alert': elements.alert.classList.remove('visible'); break; case 'prompt': /* return null when click cancel */ evt.detail.returnValue = null; elements.prompt.classList.remove('visible'); break; case 'confirm': /* return false when click cancel */ evt.detail.returnValue = false; elements.confirm.classList.remove('visible'); break; case 'selectone': /* return null when click cancel */ evt.detail.returnValue = null; elements.selectOne.classList.remove('visible'); break; } if (evt.isPseudo && evt.cancelCallback) { evt.cancelCallback(evt.detail.returnValue); } if (evt.detail.unblock) { evt.detail.unblock(); } focusManager.focus(); this.processNextEvent(); }, // When user selects an option on selectone dialog selectOneHandler: function md_confirmHandler(target) { this.screen.classList.remove('modal-dialog'); var elements = this.elements; var evt = this.eventForCurrentOrigin; evt.detail.returnValue = target.id; elements.selectOne.classList.remove('visible'); if (evt.isPseudo && evt.callback) { evt.callback(evt.detail.returnValue); } if (evt.detail.unblock) { evt.detail.unblock(); } focusManager.focus(); this.processNextEvent(); }, buildSelectOneDialog: function md_buildSelectOneDialog(data) { var elements = this.elements; elements.selectOneTitle.setAttribute('data-l10n-id', data.title); elements.selectOneMenu.innerHTML = ''; if (!data.options) { return; } var itemsHTML = ''; for (var i = 0; i < data.options.length; i++) { itemsHTML += Sanitizer.escapeHTML `