Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
485 lines
14 KiB
JavaScript
485 lines
14 KiB
JavaScript
/* 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();
|