Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
485 lines
14 KiB
JavaScript
Executable file
485 lines
14 KiB
JavaScript
Executable file
/* global Service */
|
|
'use strict';
|
|
|
|
// 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: {},
|
|
|
|
// 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('system-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);
|
|
}
|
|
}
|
|
},
|
|
|
|
// 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;
|
|
}
|
|
|
|
if (this.isVisible()) {
|
|
this.updateHeight();
|
|
}
|
|
break;
|
|
|
|
case 'system-resize':
|
|
case 'keyboardchange':
|
|
if (this.isVisible()) {
|
|
this.updateHeight();
|
|
}
|
|
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];
|
|
},
|
|
|
|
updateHeight: function sd_updateHeight() {
|
|
var height = Service.query('LayoutManager.height') -
|
|
Service.query('Statusbar.height');
|
|
this.overlay.style.height = height + 'px';
|
|
},
|
|
|
|
_localizeElement: function(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;
|
|
}
|
|
}
|
|
},
|
|
|
|
// 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 = '';
|
|
}
|
|
|
|
switch (type) {
|
|
case 'alert':
|
|
this._localizeElement(elements.alertMessage, message);
|
|
elements.alert.classList.add('visible');
|
|
this.setTitle('alert', title);
|
|
elements.alertOk.setAttribute('data-l10n-id', evt.yesText ?
|
|
evt.yesText : 'ok');
|
|
elements.alert.focus();
|
|
break;
|
|
|
|
case 'prompt':
|
|
elements.prompt.classList.add('visible');
|
|
elements.promptInput.value = evt.detail.initialValue;
|
|
this._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');
|
|
elements.prompt.focus();
|
|
break;
|
|
|
|
case 'confirm':
|
|
elements.confirm.classList.add('visible');
|
|
this._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');
|
|
elements.confirm.focus();
|
|
break;
|
|
|
|
case 'selectone':
|
|
this.setTitle('selectOne', title);
|
|
this.buildSelectOneDialog(message);
|
|
elements.selectOne.classList.add('visible');
|
|
elements.selectOne.focus();
|
|
break;
|
|
}
|
|
|
|
this.updateHeight();
|
|
},
|
|
|
|
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._localizeElement(this.elements[type + 'Title'], 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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
this.processNextEvent();
|
|
},
|
|
|
|
buildSelectOneDialog: function md_buildSelectOneDialog(data) {
|
|
var elements = this.elements;
|
|
var menu = elements.selectOneMenu;
|
|
while (menu.firstChild) {
|
|
menu.removeChild(menu.firstChild);
|
|
}
|
|
|
|
if (!data) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
var li = document.createElement('li');
|
|
var button = document.createElement('button');
|
|
button.setAttribute('id', data[i].id);
|
|
this._localizeElement(button, data[i].text);
|
|
li.appendChild(button);
|
|
menu.appendChild(li);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method about customized alert
|
|
* @param {String} title the title of the dialog. null or empty for
|
|
* no title.
|
|
* @param {String} text message for the dialog.
|
|
* @param {Object} confirm {title, callback} object when confirm.
|
|
*/
|
|
alert: function md_alert(title, text, confirm) {
|
|
this.showWithPseudoEvent({
|
|
type: 'alert',
|
|
text: text,
|
|
callback: confirm.callback,
|
|
title: title,
|
|
yesText: confirm.title
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Method about customized confirm
|
|
* @param {String} title the title of the dialog. null or empty for
|
|
* no title.
|
|
* @param {String} text message for the dialog.
|
|
* @param {Object} confirm {title, callback} object when confirm.
|
|
* @param {Object} cancel {title, callback} object when cancel.
|
|
*/
|
|
confirm: function md_confirm(title, text, confirm, cancel) {
|
|
this.showWithPseudoEvent({
|
|
type: 'confirm',
|
|
text: text,
|
|
callback: confirm.callback,
|
|
cancel: cancel.callback,
|
|
title: title,
|
|
yesText: confirm.title,
|
|
noText: cancel.title
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Method about customized prompt
|
|
* @param {String} title the title of the dialog. null or empty for
|
|
* no title.
|
|
* @param {String} text message for the dialog.
|
|
* @param {String} default_value message in the text field.
|
|
* @param {Object} confirm {title, callback} object when confirm.
|
|
* @param {Object} cancel {title, callback} object when cancel.
|
|
*/
|
|
prompt: function md_prompt(title, text, default_value, confirm, cancel) {
|
|
this.showWithPseudoEvent({
|
|
type: 'prompt',
|
|
text: text,
|
|
initialValue: default_value,
|
|
callback: confirm.callback,
|
|
cancel: cancel.callback,
|
|
title: title,
|
|
yesText: confirm.title,
|
|
noText: cancel.title
|
|
});
|
|
},
|
|
|
|
selectOne: function md_selectOne(title, options, callback) {
|
|
this.showWithPseudoEvent({
|
|
type: 'selectone',
|
|
title: title,
|
|
text: options,
|
|
callback: callback
|
|
});
|
|
},
|
|
|
|
showWithPseudoEvent: function md_showWithPseudoEvent(config) {
|
|
var pseudoEvt = {
|
|
isPseudo: true,
|
|
detail: {
|
|
unblock: null
|
|
}
|
|
};
|
|
|
|
pseudoEvt.detail.title = config.title;
|
|
pseudoEvt.detail.message = config.text;
|
|
pseudoEvt.callback = config.callback;
|
|
pseudoEvt.detail.promptType = config.type;
|
|
pseudoEvt.cancelCallback = config.cancel;
|
|
pseudoEvt.yesText = config.yesText;
|
|
pseudoEvt.noText = config.noText;
|
|
if (config.type == 'prompt') {
|
|
pseudoEvt.detail.initialValue = config.initialValue;
|
|
}
|
|
|
|
// Create a virtual mapping in this.currentEvents,
|
|
// since system-app uses the different way to call ModalDialog.
|
|
if (!this.currentEvents.system) {
|
|
this.currentEvents.system = [];
|
|
}
|
|
this.currentEvents.system.push(pseudoEvt);
|
|
this.show(null, 'system');
|
|
},
|
|
|
|
isVisible: function md_isVisible() {
|
|
return this.screen ? this.screen.classList.contains('modal-dialog') : false;
|
|
}
|
|
};
|
|
|
|
ModalDialog.init();
|