Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,60 @@
'use strict';
/* exported AddContactMenu */
/* globals LazyLoader, MozActivity, OptionMenu */
var AddContactMenu = (function() {
var _createNewContact = function(phoneNumber) {
launchActivity('new', phoneNumber);
};
var _addToExistingContact = function(phoneNumber) {
launchActivity('update', phoneNumber);
};
var launchActivity = function(name, phoneNumber) {
try {
/* jshint nonew: false */
new MozActivity({
name: name,
data: {
type: 'webcontacts/contact',
params: {
'tel': phoneNumber
}
}
});
} catch (e) {
console.error('Error while creating activity');
}
};
return {
show: function(phoneNumber) {
var params = {
items: [{
l10nId: 'createNewContact',
method: _createNewContact,
params: [phoneNumber]
},{
l10nId: 'addToExistingContact',
method: _addToExistingContact,
params: [phoneNumber]
},{ // Last item is the Cancel button
l10nId: 'cancel',
incomplete: true
}],
header: phoneNumber
};
LazyLoader.load(['/shared/js/option_menu.js',
'/shared/style/action_menu.css'], function() {
/* jshint nonew: false */
new OptionMenu(params).show();
});
}
};
}());

View file

@ -0,0 +1,60 @@
'use strict';
/* exported CallGroupMenu */
/* globals CallInfo, LazyLoader, MozActivity, OptionMenu */
var CallGroupMenu = (function() {
var _showCallInfo = function(phoneNumber, date, type, status) {
LazyLoader.load(['/dialer/js/call_info.js',
'/dialer/style/call_info.css'], function() {
CallInfo.show(phoneNumber, date, type, status);
});
};
var _sendSms = function(phoneNumber) {
/* jshint nonew: false */
try {
new MozActivity({
name: 'new',
data: {
type: 'websms/sms',
number: phoneNumber
}
});
} catch (e) {
console.error('Error while creating activity: ' + e);
}
};
return {
show: function(groupPrimaryInfo, phoneNumber, date, type, status) {
var span = document.createElement('span');
span.dir = 'auto';
span.textContent = groupPrimaryInfo;
var params = {
items: [{
l10nId: 'callInformation',
method: _showCallInfo,
params: [phoneNumber, date, type, status]
},{
l10nId: 'sendSms',
method: _sendSms,
params: [phoneNumber]
},{ // Last item is the Cancel button
l10nId: 'cancel',
incomplete: true
}],
header: span,
classes: ['call-group-menu']
};
LazyLoader.load(['/shared/js/option_menu.js',
'/shared/style/action_menu.css'], function() {
new OptionMenu(params).show();
});
}
};
}());

View file

@ -0,0 +1,295 @@
'use strict';
/* global CallLog, CallLogDBManager, Contacts, ContactsButtons, LazyLoader,
MozActivity, SimplePhoneMatcher, Utils */
(function(exports) {
var currentGroup;
var detailsButton;
var addToContactButton;
var createContactButton;
var callInfoView;
var phoneDetailsElt;
var emailDetailsElt;
var listDetailsElt;
function updateViewIfNeeded(evt) {
if (evt.detail.group.id !== currentGroup.id) {
return;
}
updateView(evt.detail.group);
}
function updateView(group) {
currentGroup = group;
updateGroupInformation(group);
updateCallDurations(group);
updateActionButtons(group);
callInfoView.hidden = false;
CallLog.hideEditModeButton();
}
function isMissedCall(group) {
return group.type === 'incoming' && group.status !== 'connected';
}
function updateGroupInformation(group) {
var titleElt = document.getElementById('call-info-title');
if (group.contact) {
titleElt.textContent = group.contact.primaryInfo;
} else if (group.voicemail) {
titleElt.setAttribute('data-l10n-id', 'voiceMail');
} else if (group.emergency) {
titleElt.setAttribute('data-l10n-id', 'emergencyNumber');
} else {
titleElt.textContent = group.number;
}
Utils.setHeaderDate(
document.getElementById('call-info-day'), parseInt(group.date));
var classList = document.getElementById('call-info-direction').classList;
classList.remove('icon-outgoing', 'icon-incoming', 'icon-missed');
if (isMissedCall(group)) {
classList.add('icon-missed');
} else if (group.type === 'dialing' || group.type === 'alerting') {
classList.add('icon-outgoing');
} else if (group.type === 'incoming'){
classList.add('icon-incoming');
}
}
function updateCallDurations(group) {
var callDurationsElt = document.getElementById('call-durations');
callDurationsElt.innerHTML = '';
/* Old groups did not record call durations */
if (!group.calls) {
return;
}
group.calls.forEach(function(call) {
var startTime = document.createElement('p');
startTime.classList.add('ci__grow');
startTime.classList.add('js-ci-start-times');
startTime.dataset.date = call.date;
startTime.textContent = Utils.prettyDate(call.date);
var durationElt = document.createElement('p');
durationElt.classList.add('cd__duration');
navigator.mozL10n.once(function() {
if (call.duration === 0) {
if (group.type === 'incoming') {
durationElt.setAttribute('data-l10n-id', 'info-missed');
} else {
durationElt.setAttribute('data-l10n-id', 'canceled');
}
} else {
Utils.prettyDuration(durationElt, call.duration,
'callDurationTextFormat');
}
});
var row = document.createElement('div');
row.classList.add('call-duration');
row.appendChild(startTime);
row.appendChild(durationElt);
callDurationsElt.appendChild(row);
});
}
function updateStartTimes() {
var startTimeElts = document.querySelectorAll('.js-ci-start-times');
for (var i=0, il=startTimeElts.length; i<il; i++) {
var startTimeElt = startTimeElts[i];
var date = parseInt(startTimeElt.dataset.date, 10);
startTimeElt.textContent = Utils.prettyDate(date);
}
}
function renderPhones(group, contact) {
var contactTels = contact.tel.map(function(tel) {
return tel.value;
});
if (!contactTels.some((tel) => tel != null)) {
return;
}
LazyLoader.load(['/shared/js/simple_phone_matcher.js'], function() {
ContactsButtons.renderPhones(contact);
// Highlight the contact number that the call info page was opened for,
// with a color depending on whether the call was missed or not.
var remark = isMissedCall(group) ? 'remark-missed' : 'remark';
var groupTel = SimplePhoneMatcher.sanitizedNumber(
group.number || group.contact.matchingTel.number);
var groupTelVariants = SimplePhoneMatcher.generateVariants(groupTel);
// SimplePhoneMatch expects a series of contacts, so we pass it an array
// containing only the relevant contact.
contactTels = [contactTels];
var matchingTels = SimplePhoneMatcher.bestMatch(groupTelVariants,
contactTels);
var matchingTel = {value: ''};
if (matchingTels.totalMatchNum) {
matchingTel = contact.tel[matchingTels.allMatches[0][0]];
}
ContactsButtons.reMark('tel', matchingTel.value, remark);
});
}
function updateActionButtons(group) {
listDetailsElt.innerHTML = '';
detailsButton.hidden = true;
addToContactButton.hidden = true;
createContactButton.hidden = true;
if (group.contact) {
detailsButton.hidden = false;
Contacts.findByNumber(group.contact.matchingTel.number,
function(contact, matchingTel) {
ContactsButtons.renderEmails(contact);
renderPhones(group, contact);
});
} else {
addToContactButton.hidden = false;
createContactButton.hidden = false;
var contact = {
tel: [
{
value: group.number,
type: 'mobile'
}
]
};
renderPhones(group, contact);
}
}
function close(evt) {
if (evt.detail.type !== 'back') {
return;
}
window.removeEventListener('CallLogDbNewCall', updateViewIfNeeded);
callInfoView.hidden = true;
CallLog.showEditModeButton();
}
function viewContact() {
/*jshint nonew: false */
new MozActivity({
name: 'open',
data: {
type: 'webcontacts/contact',
params: {id: currentGroup.contact.id}
}
});
}
function createNewContact() {
launchActivity('new', currentGroup.number);
}
function addToExistingContact() {
launchActivity('update', currentGroup.number);
}
function launchActivity(name, phoneNumber) {
try {
/* jshint nonew: false */
new MozActivity({
name: name,
data: {
type: 'webcontacts/contact',
params: {
'tel': phoneNumber
}
}
});
} catch (e) {
console.error('Error while creating activity');
}
}
function initListeners() {
detailsButton = document.getElementById('call-info-details');
detailsButton.addEventListener('click', viewContact);
addToContactButton = document.getElementById('call-info-add');
addToContactButton.addEventListener('click', addToExistingContact);
createContactButton = document.getElementById('call-info-create');
createContactButton.addEventListener('click', createNewContact);
var header = document.getElementById('call-info-gaia-header');
header.addEventListener('action', close);
window.addEventListener('timeformatchange', updateStartTimes);
}
// FIXME/bug 1060290: The build system doesn't allow nested fragments, so we
// can work around this for now by loading all elements at the root level and
// then resolving them to the correct location, e.g. fragment-name-stub, on
// runtime.
function replaceFragmentStub(stub, fragment) {
var parent = stub.parentNode;
parent.insertBefore(fragment, stub);
parent.removeChild(stub);
fragment.hidden = false;
}
function initFragments() {
var phoneDetailsStub = document.getElementById('phone-details-stub');
phoneDetailsElt = document.getElementById('phone-details');
var emailDetailsStub = document.getElementById('email-details-stub');
emailDetailsElt = document.getElementById('email-details');
var contactDetailsElt = document.getElementById('contact-detail');
listDetailsElt = document.getElementById('call-info-list-details');
LazyLoader.load([phoneDetailsElt, emailDetailsElt], function() {
replaceFragmentStub(phoneDetailsStub, phoneDetailsElt);
replaceFragmentStub(emailDetailsStub, emailDetailsElt);
ContactsButtons.init(listDetailsElt, contactDetailsElt);
});
}
var CallInfo = {
_initialised: false,
show: function(number, date, type, status) {
callInfoView = document.getElementById('call-info-view');
var self = this;
LazyLoader.load([callInfoView,
'/shared/js/dialer/contacts.js',
'/shared/js/dialer/utils.js',
'/shared/js/contacts/contacts_buttons.js',
'/shared/js/contacts/utilities/templates.js',
'/shared/js/contacts/sms_integration.js',
'/shared/js/text_normalizer.js',
'/dialer/js/telephony_helper.js',
'/shared/style/contacts/contacts_buttons.css',
'/shared/style/contacts.css',
'/dialer/style/buttons.css'], function() {
if (!self._initialised) {
self._initialised = true;
initListeners();
initFragments();
}
date = parseInt(date, 10);
CallLogDBManager.getGroup(number, date, type, status)
.then(updateView);
window.addEventListener('CallLogDbNewCall', updateViewIfNeeded);
});
}
};
exports.CallInfo = CallInfo;
})(window);

View file

@ -0,0 +1,55 @@
;'use strict';var CallLog={_initialized:false,_headersInterval:null,_empty:true,_contactCache:false,init:function cl_init(){if(this._initialized){this.becameVisible();return;}
window.performance.mark('callLogStart');this._initialized=true;var lazyFiles=['/shared/style/confirm.css','/shared/style/switches.css','/shared/style/lists.css','/shared/js/confirm.js','/shared/js/dialer/utils.js','/shared/js/sticky_header.js','/shared/js/sim_settings_helper.js','/shared/js/date_time_helper.js'];var self=this;var validContactsCachePromise=this._validateContactsCache();LazyLoader.load(lazyFiles,function resourcesLoaded(){var mainNodes=['all-filter','call-log-container','call-log-edit-mode','call-log-filter','call-log-icon-edit','call-log-view','deselect-all-threads','delete-button','edit-mode-header','header-edit-mode-text','missed-filter','select-all-threads','edit-mode'];mainNodes.forEach(function(id){this[Utils.toCamelCase(id)]=document.getElementById(id);},self);var dualSim=!!navigator.mozIccManager&&navigator.mozIccManager.iccIds.length>1;self.callLogContainer.classList.toggle('dual-sim',dualSim);validContactsCachePromise.then(function(){self.render();window.addEventListener('timeformatchange',self._updateCallTimes.bind(self));self.callLogIconEdit.addEventListener('click',self.showEditMode.bind(self));self.editModeHeader.addEventListener('action',self.hideEditMode.bind(self));self.missedFilter.addEventListener('click',self.filter.bind(self));self.allFilter.addEventListener('click',self.unfilter.bind(self));self.callLogContainer.addEventListener('click',self);self.callLogContainer.addEventListener('contextmenu',self);self.selectAllThreads.addEventListener('click',self.selectAll.bind(self));self.deselectAllThreads.addEventListener('click',self.deselectAll.bind(self));self.deleteButton.addEventListener('click',self.deleteLogGroups.bind(self));document.addEventListener('visibilitychange',function(){if(document.hidden){self.pauseHeaders();}else{self.updateHeadersContinuously();if(Navigation.currentView==='recents'){self.becameVisible();}}});self.sticky=new StickyHeader(self.callLogContainer,document.getElementById('sticky'));self.becameVisible();});});},_validateContactsCache:function cl_validateContactsCache(){var self=this;return new Promise(function(resolve,reject){window.asyncStorage.getItem('contactCacheRevision',function onItem(cacheRevision){Contacts.getRevision(function(contactsRevision){if(cacheRevision>contactsRevision){window.asyncStorage.setItem('contactCacheRevision',contactsRevision);self._contactCache=true;resolve();return;}
self._contactCache=(cacheRevision>=contactsRevision);if(self._contactCache){resolve();return;}
CallLogDBManager.invalidateContactsCache(function(error){if(!error){self._contactCache=true;resolve();}});});});});},_updateCallTimes:function cl_updateCallTimes(){var logItemElts=this.callLogContainer.querySelectorAll('.log-item');for(var i=0;i<logItemElts.length;i++){var logItemElt=logItemElts[i];var timestamp=logItemElt.getAttribute('data-timestamp');var formattedTime=Utils.prettyDate(parseInt(timestamp,10));var callTimeElt=logItemElt.querySelector('.call-time');callTimeElt.textContent=formattedTime;}},becameVisible:function cl_becameVisible(){this.updateHeaders();this.cleanNotifications();},updateHeaders:function cl_updateHeaders(){var headers=this.callLogContainer.getElementsByTagName('header');for(var i=0,l=headers.length;i<l;i++){var current=headers[i];Utils.setHeaderDate(current,parseInt(current.dataset.timestamp));}},updateHeadersContinuously:function cl_updateHeaders(){var updater=this.updateHeaders.bind(this);this._headersInterval=setInterval(updater,60000);},pauseHeaders:function cl_pauseHeaders(){clearInterval(this._headersInterval);},render:function cl_render(){var self=this;var daysToRender=[];var chunk=[];var prevDate;var screenRendered=false;var MAX_GROUPS_FOR_FIRST_RENDER=8;var MAX_GROUPS_TO_BATCH_RENDER=100;var batchGroupCounter=0;CallLogDBManager.getGroupList(function logGroupsRetrieved(cursor){if(!cursor.value){if(chunk.length===0){self.renderEmptyCallLog();self.disableEditModeButton();}else{daysToRender.push(chunk);self.renderSeveralDays(daysToRender);if(!screenRendered){window.performance.mark('firstChunkReady');}
self.enableEditModeButton();self.sticky.refresh();self.updateHeadersContinuously();}
window.performance.measure('callLogReady','callLogStart');return;}
self._empty=false;var currDate=new Date(cursor.value.date);batchGroupCounter++;if(!prevDate||(currDate.getTime()==prevDate.getTime())){chunk.push(cursor.value);}else{daysToRender.push(chunk);chunk=[cursor.value];var renderNow=false;if(batchGroupCounter>=MAX_GROUPS_FOR_FIRST_RENDER&&!screenRendered){renderNow=true;screenRendered=true;window.performance.mark('firstChunkReady');}else if(batchGroupCounter>=MAX_GROUPS_TO_BATCH_RENDER){renderNow=true;}
if(renderNow){self.renderSeveralDays(daysToRender);daysToRender=[];batchGroupCounter=0;}}
prevDate=currDate;cursor.continue();},'lastEntryDate',true,true);},renderSeveralDays:function cl_renderSeveralDays(chunks){for(var i=0,l=chunks.length;i<l;i++){this.renderChunk(chunks[i]);}},renderChunk:function cl_renderChunk(chunk){var callLogSection=this.createSectionForGroup(chunk[0]);var phoneNumbers=[];var logGroupContainer=callLogSection.getElementsByTagName('ol')[0];for(var i=0;i<chunk.length;i++){var current=chunk[i];var logGroupDOM=this.createGroup(current);logGroupContainer.appendChild(logGroupDOM);phoneNumbers.push(current.number);}
this.callLogContainer.appendChild(callLogSection);if(!this._contactCache){this.updateListWithContactInfo(null,null,phoneNumbers,logGroupContainer);}},renderEmptyCallLog:function cl_renderEmptyCallLog(isEmptyMissedCallsGroup){this.disableEditModeButton();if(!isEmptyMissedCallsGroup){this._empty=true;}
var noResultContainer=document.getElementById('no-result-container');noResultContainer.hidden=false;var allCallsMsg1=document.getElementById('no-result-msg1');var allCallsMsg2=document.getElementById('no-result-msg2');var noMissedCallsMsg=document.getElementById('no-result-msg3');allCallsMsg1.hidden=isEmptyMissedCallsGroup||false;allCallsMsg2.hidden=isEmptyMissedCallsGroup||false;noMissedCallsMsg.hidden=!isEmptyMissedCallsGroup;if(!isEmptyMissedCallsGroup){var noResultContainerClone=noResultContainer.cloneNode(true);this.callLogContainer.innerHTML='';this.callLogContainer.appendChild(noResultContainerClone);}},appendGroup:function cl_appendGroup(group){if(!this._initialized){return;}
this.unfilter();this.enableEditModeButton();var logGroupDOM=this.createGroup(group);var dayIndex=group.date;var container=this.callLogContainer;var previousLogGroup=document.getElementById(group.id);if(previousLogGroup){var parent=previousLogGroup.parentNode;parent.removeChild(previousLogGroup);this.insertInSection(logGroupDOM,parent);return logGroupDOM;}
var groupSelector='[data-timestamp="'+dayIndex+'"]';var sectionExists=container.querySelector(groupSelector);if(sectionExists){var section=sectionExists.getElementsByTagName('ol')[0];this.insertInSection(logGroupDOM,section);return logGroupDOM;}
var callLogSection=this.createSectionForGroup(group);var logGroupContainer=callLogSection.getElementsByTagName('ol')[0];logGroupContainer.appendChild(logGroupDOM);var previousSections=container.getElementsByTagName('section');var referenceSection;var previousSectionsLength=previousSections.length;for(var i=0;i<previousSectionsLength;i++){var current=previousSections[i];if(dayIndex>current.dataset.timestamp){referenceSection=current;break;}}
if(referenceSection){container.insertBefore(callLogSection,referenceSection);}else{if(this._empty){var noResultContainer=document.getElementById('no-result-container').cloneNode(true);container.innerHTML='';noResultContainer.hidden=true;container.appendChild(noResultContainer);this._empty=false;}
container.appendChild(callLogSection);}
this.sticky.refresh();return logGroupDOM;},insertInSection:function cl_insertInSect(group,section){var groups=section.getElementsByTagName('li');var groupsLength=groups.length;var referenceGroup;var date=group.dataset.timestamp;for(var i=0;i<groupsLength;i++){var current=groups[i];if(parseInt(date)>parseInt(current.dataset.timestamp)){referenceGroup=current;break;}}
if(referenceGroup){section.insertBefore(group,referenceGroup);}else{section.appendChild(group);}},createGroup:function cl_createGroup(group){var date=group.lastEntryDate;var number=group.number;var type=group.type;var emergency=group.emergency;var voicemail=group.voicemail;var status=group.status||'';var contact=group.contact;var groupDOM=document.createElement('li');groupDOM.classList.add('log-item');groupDOM.dataset.timestamp=date;groupDOM.dataset.phoneNumber=number;groupDOM.dataset.type=type;groupDOM.setAttribute('role','option');groupDOM.setAttribute('aria-selected',false);if(contact&&contact.id){groupDOM.dataset.contactId=contact.id;}
groupDOM.id=group.id;var iconStyle='icon ';switch(type){case'dialing':case'alerting':iconStyle+='icon-outgoing';break;case'incoming':groupDOM.dataset.status=status;if(status==='connected'){iconStyle+='icon-incoming';}else{iconStyle+='icon-missed';groupDOM.classList.add('missed-call');}
break;}
if(typeof group.serviceId!=='undefined'){var serviceClass=(parseInt(group.serviceId)===0)?'first-sim':'second-sim';iconStyle+=' '+serviceClass;}
var label=document.createElement('label');label.setAttribute('aria-hidden',true);label.className='pack-checkbox call-log-selection danger';var input=document.createElement('input');input.setAttribute('type','checkbox');input.value=group.id;var span=document.createElement('span');label.appendChild(input);label.appendChild(span);var main=document.createElement('a');main.setAttribute('role','presentation');var icon=document.createElement('aside');icon.className='icon call-type-icon '+iconStyle;var primInfo=document.createElement('p');primInfo.className='primary-info';var primInfoMain=document.createElement('span');primInfoMain.className='primary-info-main';var bdi=document.createElement('bdi');if(contact&&contact.primaryInfo){bdi.textContent=contact.primaryInfo;}else{if(number){bdi.textContent=number;}else{bdi.setAttribute('data-l10n-id','withheld-number');}}
primInfoMain.appendChild(bdi);primInfo.appendChild(primInfoMain);if(group.retryCount&&group.retryCount>1){var retryCount=document.createElement('span');retryCount.className='retry-count';retryCount.textContent='('+group.retryCount+')';primInfo.appendChild(retryCount);}
var phoneNumberAdditionalInfo='';var phoneNumberTypeL10nId=null;if(contact&&contact.matchingTel){phoneNumberAdditionalInfo=Utils.getPhoneNumberAdditionalInfo(contact.matchingTel);}else if(voicemail||emergency){phoneNumberAdditionalInfo={raw:number};phoneNumberTypeL10nId=voicemail?'voiceMail':(emergency?'emergencyNumber':null);}else{phoneNumberAdditionalInfo={id:'unknown'};}
var addInfo=document.createElement('p');addInfo.className='additional-info';addInfo.setAttribute('aria-hidden','true');var typeAndCarrier=document.createElement('span');typeAndCarrier.className='type-carrier';if(phoneNumberAdditionalInfo){if(phoneNumberAdditionalInfo.raw){typeAndCarrier.removeAttribute('data-l10n-id');typeAndCarrier.textContent=phoneNumberAdditionalInfo.raw;}else{navigator.mozL10n.setAttributes(typeAndCarrier,phoneNumberAdditionalInfo.id,phoneNumberAdditionalInfo.args);}}
addInfo.appendChild(typeAndCarrier);var callTime=document.createElement('span');callTime.className='call-time';callTime.textContent=Utils.prettyDate(date)+' ';addInfo.appendChild(callTime);main.appendChild(icon);main.appendChild(primInfo);main.appendChild(addInfo);if(phoneNumberTypeL10nId){var bdiPrim=primInfoMain.querySelector('bdi');primInfoMain.removeChild(bdiPrim);primInfoMain.setAttribute('data-l10n-id',phoneNumberTypeL10nId);var primElem=primInfoMain.parentNode;var parent=primElem.parentNode;parent.insertBefore(addInfo,primElem.nextElementSibling);}
groupDOM.appendChild(label);groupDOM.appendChild(main);return groupDOM;},createSectionForGroup:function cl_createSectionForGroup(group){var referenceTimestamp=group.date;var groupContainer=document.createElement('section');groupContainer.dataset.timestamp=referenceTimestamp;var header=document.createElement('header');Utils.setHeaderDate(header,referenceTimestamp);header.dataset.timestamp=referenceTimestamp;header.id='header-'+referenceTimestamp;header.dataset.update=true;var ol=document.createElement('ol');ol.classList.add('log-group');ol.setAttribute('role','listbox');ol.setAttribute('aria-multiselectable',true);ol.id='group-container-'+referenceTimestamp;groupContainer.appendChild(header);groupContainer.appendChild(ol);return groupContainer;},enableEditModeButton:function cl_enableEditModeButton(){CallLog.callLogIconEdit.removeAttribute('disabled');},disableEditModeButton:function cl_disableEditModeButton(){CallLog.callLogIconEdit.setAttribute('disabled','disabled');},showEditModeButton:function cl_showEditModeButton(){CallLog.callLogIconEdit.hidden=false;},hideEditModeButton:function cl_hideEditModeButton(){CallLog.callLogIconEdit.hidden=true;},showEditMode:function cl_showEditMode(event){if(this.callLogIconEdit.hasAttribute('disabled')){event.preventDefault();event.stopPropagation();return;}
this.editMode.removeAttribute('hidden');this.headerEditModeText.setAttribute('data-l10n-id','edit');this.deleteButton.setAttribute('disabled','disabled');this.selectAllThreads.removeAttribute('disabled');this.selectAllThreads.setAttribute('data-l10n-id','selectAll');this.deselectAllThreads.setAttribute('disabled','disabled');document.body.classList.add('recents-edit');},hideEditMode:function cl_hideEditMode(){document.body.classList.remove('recents-edit');var cont=this.callLogContainer;var inputs=cont.querySelectorAll('input[type="checkbox"]');var logItems=cont.querySelectorAll('.log-item');var i,l;for(i=0,l=inputs.length;i<l;i++){inputs[i].checked=false;}
for(i=0,l=logItems.length;i<l;i++){logItems[i].setAttribute('aria-selected',false);}
if(this.callLogContainer.classList.contains('filter')){this.filter();}else{this.unfilter();}
this.editMode.setAttribute('hidden','');},handleEvent:function cl_handleEvent(evt){var logItem=evt.target;if(document.body.classList.contains('recents-edit')){if(logItem.dataset.phoneNumber){var checkbox=logItem.getElementsByTagName('input')[0];var toggleChecked=!checkbox.checked;checkbox.checked=toggleChecked;logItem.setAttribute('aria-selected',toggleChecked);}
this.updateHeaderCount();return;}
var dataset=logItem.dataset;var phoneNumber=dataset.phoneNumber;if(!phoneNumber){return;}
if(evt.type=='click'){if(navigator.mozIccManager&&navigator.mozIccManager.iccIds.length>1){KeypadManager.updatePhoneNumber(phoneNumber);Navigation.showKeypad();}else{SimSettingsHelper.getCardIndexFrom('outgoingCall',function(ci){CallHandler.call(phoneNumber,ci);});}}else{evt.preventDefault();var primaryInfo=logItem.querySelector('.primary-info-main').textContent;LazyLoader.load(['/dialer/js/call_group_menu.js'],function(){CallGroupMenu.show(primaryInfo,phoneNumber,dataset.timestamp,dataset.type,dataset.status);});}},filter:function cl_filter(){this.callLogContainer.classList.add('filter');AccessibilityHelper.setAriaSelected(this.missedFilter.firstElementChild,[this.allFilter.firstElementChild,this.missedFilter.firstElementChild]);this.callLogContainer.setAttribute('aria-labelledby','missed-filter-tab');var containers=this.callLogContainer.getElementsByTagName('ol');var totalMissedCalls=0;for(var i=0,l=containers.length;i<l;i++){var noMissedCalls=containers[i].getElementsByClassName('missed-call');if(noMissedCalls.length===0){containers[i].parentNode.classList.add('groupFiltered');}
totalMissedCalls+=noMissedCalls.length;}
if(totalMissedCalls===0){this.renderEmptyCallLog(true);}else{var noResultContainer=document.getElementById('no-result-container');noResultContainer.hidden=true;this.enableEditModeButton();}},unfilter:function cl_unfilter(){var logItems=this.callLogContainer.querySelectorAll('.log-item');if(logItems.length===0){this.renderEmptyCallLog();}else{var noResultContainer=document.getElementById('no-result-container');noResultContainer.hidden=true;this.enableEditModeButton();}
this.callLogContainer.classList.remove('filter');AccessibilityHelper.setAriaSelected(this.allFilter.firstElementChild,[this.allFilter.firstElementChild,this.missedFilter.firstElementChild]);this.callLogContainer.setAttribute('aria-labelledby','all-filter-tab');var hiddenContainers=document.getElementsByClassName('groupFiltered');for(var i=(hiddenContainers.length-1);i>=0;i--){hiddenContainers[i].classList.remove('groupFiltered');}},updateHeaderCount:function cl_updateHeaderCount(){var selector=this.callLogContainer.classList.contains('filter')?'li.missed-call input[type="checkbox"]:checked':'input[type="checkbox"]:checked';var allSelector=this.callLogContainer.classList.contains('filter')?'li.missed-call input[type="checkbox"]':'input[type="checkbox"]';var selected=this.callLogContainer.querySelectorAll(selector).length;var allInputs=this.callLogContainer.querySelectorAll(allSelector).length;if(selected===0){this.headerEditModeText.setAttribute('data-l10n-id','edit');this.selectAllThreads.removeAttribute('disabled');this.selectAllThreads.setAttribute('data-l10n-id','selectAll');this.deselectAllThreads.setAttribute('disabled','disabled');this.deleteButton.setAttribute('disabled','disabled');return;}
navigator.mozL10n.setAttributes(this.headerEditModeText,'edit-selected',{n:selected});this.deleteButton.removeAttribute('disabled');if(selected===allInputs){this.deselectAllThreads.removeAttribute('disabled');this.selectAllThreads.setAttribute('disabled','disabled');}else{this.selectAllThreads.removeAttribute('disabled');this.selectAllThreads.setAttribute('data-l10n-id','selectAll');this.deselectAllThreads.removeAttribute('disabled');}},selectAll:function cl_selectAll(){var selector=this.callLogContainer.classList.contains('filter')?'li.missed-call input[type="checkbox"]:not(:checked)':'input[type="checkbox"]:not(:checked)';var inputs=this.callLogContainer.querySelectorAll(selector);for(var i=0,l=inputs.length;i<l;i++){inputs[i].checked=true;}
this.updateHeaderCount();},deselectAll:function cl_selectAll(){var selector='input[type="checkbox"]:checked';var inputs=this.callLogContainer.querySelectorAll(selector);for(var i=0,l=inputs.length;i<l;i++){inputs[i].checked=false;}
this.updateHeaderCount();},deleteLogGroups:function cl_deleteLogGroups(){var selector='input[type="checkbox"]:checked';var inputsSelected=this.callLogContainer.querySelectorAll(selector);var self=this;var msg={'id':'delete-n-log?','args':{n:inputsSelected.length}};var yesObject={title:'delete',isDanger:true,callback:function deleteLogGroup(){ConfirmDialog.hide();var logGroupsToDelete=[];for(var i=0,l=inputsSelected.length;i<l;i++){var logGroup=inputsSelected[i].parentNode.parentNode;var olContainer=logGroup.parentNode;olContainer.removeChild(logGroup);if(olContainer.children.length===0){var section=olContainer.parentNode;self.callLogContainer.removeChild(section);}
var dataset=logGroup.dataset;var toDelete={date:parseInt(dataset.timestamp),number:dataset.phoneNumber===null?'':dataset.phoneNumber,type:dataset.type};if(dataset.status){toDelete.status=dataset.status;}
logGroupsToDelete.push(toDelete);}
CallLogDBManager.deleteGroupList(logGroupsToDelete,function(){self.hideEditMode();});}};var noObject={title:'cancel',callback:function onCancel(){ConfirmDialog.hide();}};ConfirmDialog.show(null,msg,noObject,yesObject);},_updateContact:function _updateContact(log,phoneNumber,contactId,updateDb){var self=this;Contacts.findByNumber(phoneNumber,function(contact,matchingTel){if(!contact||!matchingTel){self._removeContact(log,contactId,updateDb);return;}
if(self._contactCache&&updateDb){CallLogDBManager.updateGroupContactInfo(contact,matchingTel,function(result){if(typeof result==='number'&&result>0){self.updateContactInfo(log,contact,matchingTel);}});}else{self.updateContactInfo(log,contact,matchingTel);}});},_removeContact:function _removeContact(log,contactId,updateDb){if(this._contactCache&&updateDb){var self=this;CallLogDBManager.removeGroupContactInfo(contactId,null,function(result){if(typeof result==='number'&&result>0){self.updateContactInfo(log);}});}else{this.updateContactInfo(log);}},updateListWithContactInfo:function cl_updateList(reason,contactId,target){var container=target||this.callLogContainer;if(!container){return;}
var logs=[];switch(reason){case'remove':logs=container.querySelectorAll('li[data-contact-id="'+contactId+'"]');break;default:logs=container.querySelectorAll('.log-item');break;}
for(var i=0,l=logs.length;i<l;i++){var log=logs[i];var logInfo=log.dataset;this._updateContact(log,logInfo.phoneNumber,contactId,i===0);}},updateContactInfo:function cl_updateContactInfo(element,contact,matchingTel){var primInfoCont=element.querySelector('.primary-info-main');var addInfo=element.getElementsByClassName('additional-info')[0];var typeAndCarrier=addInfo.querySelector('.type-carrier');if(!matchingTel){if(element.dataset.contactId){primInfoCont.textContent='';var bdi=document.createElement('bdi');bdi.textContent=element.dataset.phoneNumber;primInfoCont.appendChild(bdi);typeAndCarrier.textContent='';typeAndCarrier.setAttribute('data-l10n-id','unknown');delete element.dataset.contactId;}
return;}
var primaryInfo=Utils.getPhoneNumberPrimaryInfo(matchingTel,contact);if(primaryInfo){primInfoCont.textContent='';var bdiPrim=document.createElement('bdi');bdiPrim.textContent=primaryInfo;primInfoCont.appendChild(bdiPrim);}
var phoneNumberAdditionalInfo=Utils.getPhoneNumberAdditionalInfo(matchingTel);navigator.mozL10n.setAttributes(typeAndCarrier,phoneNumberAdditionalInfo.id,phoneNumberAdditionalInfo.args);if(contact){element.dataset.contactId=contact.id;}},cleanNotifications:function cl_cleanNotifcations(){Notification.get().then(function onSuccess(notifications){for(var i=0;i<notifications.length;i++){if(!notifications[i].tag){notifications[i].close();}}},function onError(reason){console.debug('Call log Notification.get() promise error: '+reason);});},_getGroupFromLog:function cl_getGroupFromLog(log){if(!log){return;}
var data=log.dataset;if(!data||!data.id){return;}
var groupId=data.id.split('-');var group={date:groupId[0],number:groupId[1],type:groupId[2]};if(data.status){group.status=data.status;}
return group;}};navigator.mozContacts.oncontactchange=function oncontactchange(event){var reason=event.reason;var options={filterBy:['id'],filterOp:'equals',filterValue:event.contactID};if(reason==='remove'){CallLog.updateListWithContactInfo('remove',event.contactID);return;}
var request=navigator.mozContacts.find(options);request.onsuccess=function contactRetrieved(e){if(!e.target.result||e.target.result.length===0){console.warn('Call log: No Contact Found: ',event.contactID);return;}
var contact=e.target.result[0];if(!fb.isFbContact(contact)){CallLog.updateListWithContactInfo(reason,event.contactID);return;}
var fbReq=fb.getData(contact);fbReq.onsuccess=function fbContactSuccess(){CallLog.updateListWithContactInfo(reason,event.contactID);};fbReq.onerror=function fbContactError(){console.error('Error while querying FB: ',fbReq.error.name);CallLog.updateListWithContactInfo(reason,event.contactID);};};request.onerror=function errorHandler(e){console.error('Error retrieving contact by ID '+event.contactID);};};

View file

@ -0,0 +1,72 @@
;'use strict';var CallLogDBManager={_db:null,_dbName:'dialerRecents',_dbRecentsStore:'dialerRecents',_dbGroupsStore:'dialerGroups',_dbVersion:6,_maxNumberOfGroups:200,_numberOfGroupsToDelete:30,_asyncReturn:function _asyncReturn(callback,result){if(callback&&callback instanceof Function){callback(result);}},_ensureDB:function ensureDB(callback){if(this._db){callback(null,this._db);return;}
LazyLoader.load(['/shared/js/dialer/utils.js','/shared/js/dialer/contacts.js'],(function(){try{var indexedDB=window.indexedDB||window.webkitIndexedDB||window.mozIndexedDB||window.msIndexedDB;if(!indexedDB){callback('NO_INDEXED_DB_AVAILABLE',null);return;}
var self=this;var request=indexedDB.open(this._dbName,this._dbVersion);request.onsuccess=(function onsuccess(event){this._db=event.target.result;callback(null,this._db);}).bind(this);request.onerror=function onerror(event){callback(event.target.errorCode,null);};request.onblocked=function onblocked(){callback('DB_REQUEST_BLOCKED',null);};request.onupgradeneeded=function onupgradeneeded(event){var db=event.target.result;var txn=event.target.transaction;var currentVersion=event.oldVersion;function update(currentVersion){var next=update.bind(self,currentVersion+1);switch(currentVersion){case 0:self._createSchema(db,next);break;case 1:self._upgradeSchemaVersion2(db,txn,next);break;case 2:self._upgradeSchemaVersion3(next);break;case 3:self._upgradeSchemaVersion4(db,txn,next);break;case 4:self._upgradeSchemaVersion5(next);break;case 5:self._upgradeSchemaVersion6(db,txn,next);break;case 6:break;default:event.target.transaction.abort();break;}
currentVersion++;}
update(currentVersion);};}catch(ex){callback(ex.message,null);}}).bind(this));},_newTxn:function newTxn(txnType,objectStores,callback){if(!objectStores){objectStores=[this._dbGroupsStore];}
if(!Array.isArray(objectStores)){objectStores=[objectStores];}
this._ensureDB(function(error,db){if(error){callback(error);return;}
var txn=db.transaction(objectStores,txnType);var stores;if(objectStores.length===1){stores=txn.objectStore(objectStores[0]);}else{stores=[];for(var i=0;i<objectStores.length;i++){stores.push(txn.objectStore(objectStores[i]));}}
callback(null,txn,stores);});},close:function rbdm_close(){this._db.close();this._db=null;},_createSchema:function createSchema(db,next){var objStore=db.createObjectStore(this._dbRecentsStore,{keyPath:'date'});objStore.createIndex('number','number');next();},_upgradeSchemaVersion2:function upgradeSchemaVersion2(db,transaction,next){db.createObjectStore(this._dbGroupsStore,{keyPath:'id'});var recentsStore=transaction.objectStore(this._dbRecentsStore);recentsStore.createIndex('groupId','groupId');next();},_upgradeSchemaVersion3:function upgradeSchemaVersion3(next){next();},_upgradeSchemaVersion4:function upgradeSchemaVersion4(db,transaction,next){var groupsStore=transaction.objectStore(this._dbGroupsStore);groupsStore.createIndex('number','number');groupsStore.createIndex('contactId','contactId');groupsStore.createIndex('lastEntryDate','lastEntryDate');next();},_upgradeSchemaVersion5:function upgradeSchemaVersion5(next){next();},_upgradeSchemaVersion6:function upgradeSchemaVersion6(db,transaction,next){db.deleteObjectStore(this._dbRecentsStore);next();},_getDayDate:function getDayDate(timestamp){var time=new Date(+timestamp);time.setHours(0,0,0,0);return time.getTime();},_getGroupId:function getGroupId(recentCall){var groupId=[this._getDayDate(recentCall.date),(recentCall.number||''),recentCall.type];if(recentCall.status&&recentCall.type==='incoming'){groupId.push(recentCall.status);}
return groupId;},_getGroupObject:function getGroupObject(group){if(!Array.isArray(group.id)||group.id.length<3){return null;}
var contact;if(group.contactId){contact={id:group.contactId,primaryInfo:group.contactPrimaryInfo,matchingTel:{number:group.id[1],type:group.contactMatchingTelType,carrier:group.contactMatchingTelCarrier},photo:group.contactPhoto};}
return{id:group.id.join('-'),date:group.id[0],number:group.id[1],serviceId:group.serviceId,type:group.id[2],status:group.id[3]||undefined,lastEntryDate:group.lastEntryDate,retryCount:group.retryCount,contact:contact,emergency:group.emergency,voicemail:!!(group.id[1]&&group.voicemail),calls:group.calls};},_keepDbPrettyAndFit:function _keepDbPrettyAndFit(txn,callback){var self=this;var store=txn.objectStore(this._dbGroupsStore);var req=store.count();req.onsuccess=function(){var groupsToDelete=req.result-self._maxNumberOfGroups;if(groupsToDelete>0){groupsToDelete+=self._numberOfGroupsToDelete;var cursorReq=store.index('lastEntryDate').openCursor();cursorReq.onsuccess=function(){var cursor=cursorReq.result;if(!cursor||!groupsToDelete){return;}
groupsToDelete--;self.deleteGroup(null,cursor.value.id);cursor.continue();};}};txn.oncomplete=function(){if(callback&&callback instanceof Function){callback();}};},add:function add(recentCall,callback){if(typeof recentCall!=='object'){callback('INVALID_CALL');return;}
var self=this;this._newTxn('readwrite',this._dbGroupsStore,function(error,txn,groupsStore){if(error){self._asyncReturn(callback,error);return;}
var groupId=self._getGroupId(recentCall);groupsStore.get(groupId).onsuccess=function onsuccess(){var group=this.result;if(group){self._updateExistingGroup(group,recentCall,txn,callback);}else{self._createNewGroup(groupId,recentCall,callback);}};});},_updateExistingGroup:function(group,recentCall,txn,callback){var groupsStore=txn.objectStore(this._dbGroupsStore);var groupObject;var self=this;txn.oncomplete=function(){self._dispatchCallLogDbNewCall(groupObject);self._asyncReturn(callback,groupObject);};if(group.lastEntryDate<=recentCall.date){group.lastEntryDate=recentCall.date;group.serviceId=recentCall.serviceId;group.emergency=recentCall.emergency;group.voicemail=recentCall.voicemail;}
group.calls=group.calls||[];group.calls.unshift({date:recentCall.date,duration:recentCall.duration});group.retryCount++;groupsStore.put(group).onsuccess=function onsuccess(){groupObject=self._getGroupObject(group);};},_createNewGroup:function(groupId,recentCall,callback){var self=this;var group={id:groupId,number:recentCall.number,serviceId:recentCall.serviceId,lastEntryDate:recentCall.date,retryCount:1,emergency:recentCall.emergency,voicemail:recentCall.voicemail,calls:[{date:recentCall.date,duration:recentCall.duration}]};Contacts.findByNumber(recentCall.number,function(contact,matchingTel){if(contact&&contact!==null){group.contactId=contact.id;var photo=ContactPhotoHelper.getThumbnail(contact);if(photo){group.contactPhoto=photo;}
if(matchingTel){var primaryInfo=Utils.getPhoneNumberPrimaryInfo(matchingTel,contact);if(Array.isArray(primaryInfo)){primaryInfo=primaryInfo[0];}
group.contactPrimaryInfo=String(primaryInfo);if(Array.isArray(matchingTel.type)&&matchingTel.type[0]){group.contactMatchingTelType=String(matchingTel.type[0]);}else{group.contactMatchingTelType=String(matchingTel.type);}
if(matchingTel.carrier){group.contactMatchingTelCarrier=String(matchingTel.carrier);}}}
self._newTxn('readwrite',[self._dbGroupsStore],function(error,txn,store){if(error){self._asyncReturn(callback,error);return;}
store.get(groupId).onsuccess=function onsuccess(){if(this.result){return;}
store.add(group).onsuccess=function onsuccess(){var groupObject=self._getGroupObject(group);self._dispatchCallLogDbNewCall(groupObject);self._keepDbPrettyAndFit(txn,function(){self._asyncReturn(callback,groupObject);});};};});});},_dispatchCallLogDbNewCall:function(group){var createOrUpdateEvt=new CustomEvent('CallLogDbNewCall',{detail:{group:group}});window.dispatchEvent(createOrUpdateEvt);},deleteGroup:function deleteGroup(group,groupId,callback){if(!groupId&&(!group||typeof group!=='object'||!group.date||!group.type)){callback('NOT_VALID_GROUP');return;}
var self=this;this._newTxn('readwrite',this._dbGroupsStore,function(error,txn,groupsStore){if(error){self._asyncReturn(callback,error);return;}
if(!groupId){groupId=self._getGroupId(group);}
txn.oncomplete=function(){self._asyncReturn(callback);};groupsStore.delete(groupId);});},deleteGroupList:function deleteGroupList(groupList,callback){var self=this;if(groupList.length>0){var itemToDelete=groupList.pop();if(typeof itemToDelete!=='object'){self._asyncReturn(callback,'INVALID_GROUP_IN_LIST');return;}
var ondeleted=function ondeleted(result){if(result){self._asyncReturn(callback,result);return;}
self.deleteGroupList(groupList,callback);};self.deleteGroup(itemToDelete,null,ondeleted);}else{self._asyncReturn(callback);}},deleteAll:function deleteAll(callback){var self=this;this._newTxn('readwrite',this._dbGroupsStore,function(error,txn,groupsStore){if(error){self._asyncReturn(callback,error);return;}
txn.oncomplete=function(){self._asyncReturn(callback);};groupsStore.clear();});},deleteDb:function deleteDb(callback){var indexedDB=window.indexedDB||window.webkitIndexedDB||window.mozIndexedDB||window.msIndexedDB;if(!indexedDB){callback('NO_INDEXEDDB_AVAILABLE');return;}
if(this._db){this._db.close();}
var self=this;var req=indexedDB.deleteDatabase(this._dbName);req.onsuccess=function onsuccess(){self._asyncReturn(callback);};req.onerror=function onerror(){self._asyncReturn(callback,req.error.name);};},_getList:function getList(storeName,callback,sortedBy,prev,getCursor,limit){var results=[];if(!callback||!(callback instanceof Function)){return;}
var self=this;this._newTxn('readonly',[storeName],function(error,txn,store){if(error){callback(error);return;}
if(!getCursor){txn.oncomplete=function(){callback(results);};}
txn.onerror=function(event){callback(event.target.error);};var cursor=null;var direction=prev?'prev':'next';if(sortedBy&&sortedBy!==null){if(!store.indexNames.contains(sortedBy)&&sortedBy!='id'){callback('INVALID_SORTED_BY_KEY');txn.abort();return;}
cursor=store.index(sortedBy).openCursor(null,direction);}else{cursor=store.openCursor(null,direction);}
cursor.onsuccess=function onsuccess(event){var item=event.target.result;if(getCursor){if(!item){callback({value:null});}else if(storeName===self._dbGroupsStore){callback({value:self._getGroupObject(item.value),continue:function(){return item.continue();}});}else{callback(item);}
return;}else if(item&&(typeof limit==='undefined'||limit>0)){if(storeName===self._dbGroupsStore){results.push(self._getGroupObject(item.value));}else{results.push(item.value);}
if(limit){limit--;}
item.continue();}};});},getGroupList:function getGroupList(callback,sortedBy,prev,getCursor,limit){if(sortedBy==='number'||sortedBy==='type'||sortedBy==='status'){sortedBy=null;}
this._getList(this._dbGroupsStore,callback,sortedBy,prev,getCursor,limit);},getGroupAtPosition:function rdbm_getGroupAtPosition(position,sortedBy,prev,type,callback){if(!callback||!(callback instanceof Function)){return;}
var self=this;this._newTxn('readonly',this._dbGroupsStore,function(error,txn,store){if(error){callback(error);return;}
try{var request=null;var direction=prev?'prev':'next';if(sortedBy&&sortedBy!==null){request=store.index(sortedBy).openCursor(null,direction);}else{request=store.openCursor(null,direction);}
var i=0;request.onsuccess=function(event){var cursor=event.target.result;if(!cursor){callback(null);return;}
var recentGroup=self._getGroupObject(cursor.value);var matched=!type||recentGroup.type.indexOf(type)!=-1;if(matched){i++;}
if(matched&&i==position){callback(recentGroup);}else{cursor.continue();}};request.onerror=function(event){callback(event.target.error.name);};}catch(e){callback(e);}});},getGroup:function(number,date,type,status){var self=this;return new Promise(function(resolve,reject){var recentCall={number:number,date:date,type:type,status:status};var groupId=self._getGroupId(recentCall);self._newTxn('readonly',self._dbGroupsStore,function(error,txn,groupsStore){var group=null;txn.oncomplete=function(){if(group){resolve(self._getGroupObject(group));}else{reject();}};groupsStore.get(groupId).onsuccess=function(){group=this.result;};});});},_updateCacheRevision:function _updateCacheRevision(callback){Contacts.getRevision(function(contactsRevision){if(contactsRevision){window.asyncStorage.setItem('contactCacheRevision',contactsRevision);}
callback();});},updateGroupContactInfo:function updateGroupContactInfo(contact,matchingTel,callback){var self=this;this._newTxn('readwrite',[this._dbGroupsStore],function(error,txn,store){var result;if(error){self._asyncReturn(callback,error);return;}
if(!contact){self._asyncReturn(callback,0);return;}
txn.oncomplete=function(){self._updateCacheRevision(function(){self._asyncReturn(callback,result);});};var count=0;var req=store.index('number').openCursor(IDBKeyRange.only(matchingTel.value));req.onsuccess=function onsuccess(event){var cursor=event.target.result;if(cursor&&cursor.value){var group=cursor.value;group.contactId=contact.id;var photo=ContactPhotoHelper.getThumbnail(contact);if(photo){group.contactPhoto=photo;}
if(matchingTel){var primaryInfo=Utils.getPhoneNumberPrimaryInfo(matchingTel,contact);if(Array.isArray(primaryInfo)&&primaryInfo[0]){primaryInfo=primaryInfo[0];}
group.contactPrimaryInfo=String(primaryInfo);if(Array.isArray(matchingTel.type)&&matchingTel.type[0]){group.contactMatchingTelType=String(matchingTel.type[0]);}
if(matchingTel.carrier){group.contactMatchingTelCarrier=String(matchingTel.carrier);}}
cursor.update(group);self._dispatchCallLogDbNewCall(self._getGroupObject(group));count++;cursor.continue();}else{result=count;}};req.onerror=function onerror(event){result=event.target.error;};});},removeGroupContactInfo:function removeGroupContactInfo(contactId,group,callback){if(!contactId&&!group){this._asyncReturn(callback,0);return;}
var self=this;this._newTxn('readwrite',[this._dbGroupsStore],function(error,txn,store){if(error){self._asyncReturn(callback,error);callback(error);return;}
var count=0;txn.oncomplete=function(){self._updateCacheRevision(function(){self._asyncReturn(callback,count);});};txn.onerror=function(event){self._asyncReturn(callback,event.target.error);};var req;if(contactId){req=store.index('contactId').openCursor(contactId);}else if(group){var groupId=self._getGroupId(group);req=store.openCursor(groupId);}
req.onsuccess=function onsuccess(event){var cursor=event.target.result;if(cursor){var group=cursor.value;if(group.contactId){delete group.contactId;}
if(group.contactPrimaryInfo){delete group.contactPrimaryInfo;}
if(group.contactMatchingTelType){delete group.contactMatchingTelType;}
if(group.contactMatchingTelCarrier){delete group.contactMatchingTelCarrier;}
if(group.contactPhoto){delete group.contactPhoto;}
self._dispatchCallLogDbNewCall(self._getGroupObject(group));cursor.update(group);count++;cursor.continue();}};});},_gatherAllGroups:function _gatherAllGroups(){var self=this;return new Promise(function(resolve,reject){var groups=[];self._newTxn('readonly',[self._dbGroupsStore],function(error,txn,store){if(error){reject(error);return;}
txn.oncomplete=function(){resolve(groups);};txn.onerror=function(event){reject(event.target.error);};var req=store.openCursor();req.onsuccess=function onsuccess(event){var cursor=event.target.result;if(!cursor){return;}
groups.push(cursor.value);cursor.continue();};});});},_updateGroup:function _updateGroup(group){return new Promise(function(resolve,reject){Contacts.findByNumber(group.number,function update(contact,matchingTel){var needsUpdate=false;if(!contact&&!matchingTel){if(group.contactId){needsUpdate=true;delete group.contactId;}
if(group.contactPhoto){needsUpdate=true;delete group.contactPhoto;}
if(group.contactPrimaryInfo){needsUpdate=true;delete group.contactPrimaryInfo;}
if(group.contactMatchingTelType){needsUpdate=true;delete group.contactMatchingTelType;}
if(group.contactMatchingTelCarrier){needsUpdate=true;delete group.contactMatchingTelCarrier;}}else{group.contactId=contact.id;var photo=ContactPhotoHelper.getThumbnail(contact);if(photo&&group.contactPhoto!=photo){group.contactPhoto=photo;needsUpdate=true;}
var primaryInfo=Utils.getPhoneNumberPrimaryInfo(matchingTel,contact);if(Array.isArray(primaryInfo)&&primaryInfo[0]){primaryInfo=primaryInfo[0];}
if(group.contactPrimaryInfo!=String(primaryInfo)){group.contactPrimaryInfo=String(primaryInfo);needsUpdate=true;}
if(Array.isArray(matchingTel.type)&&matchingTel.type[0]&&group.contactMatchingTelType!=String(matchingTel.type[0])){group.contactMatchingTelType=String(matchingTel.type[0]);needsUpdate=true;}
if(matchingTel.carrier&&group.contactMatchingTelCarrier!=String(matchingTel.carrier)){group.contactMatchingTelCarrier=String(matchingTel.carrier);needsUpdate=true;}}
resolve(needsUpdate?group:null);});});},invalidateContactsCache:function invalidateContactsCache(callback){var self=this;this._gatherAllGroups().then(function(groups){var promises=[];for(var i=0;i<groups.length;i++){promises.push(self._updateGroup(groups[i]));}
return Promise.all(promises);}).then(function(updatedGroups){self._newTxn('readwrite',[self._dbGroupsStore],function(error,txn,store){if(error){self._asyncReturn(callback,error);return;}
txn.oncomplete=function onContactsUpdated(){self._updateCacheRevision(function(){self._asyncReturn(callback);});};txn.onerror=function(event){self._asyncReturn(callback,event.target.error);};for(var i=0;i<updatedGroups.length;i++){if(updatedGroups[i]){store.put(updatedGroups[i]);}}});});}};

View file

@ -0,0 +1,30 @@
;'use strict';var NavbarManager={init:function nm_init(){Navigation.show('keypad');document.getElementById('views').addEventListener('click',function(e){var destination=e.target.dataset&&e.target.dataset.destination;if(!destination){return;}
Navigation.show(destination);});},hide:function(){var views=document.getElementById('views');views.classList.add('hide-toolbar');},show:function(){var views=document.getElementById('views');views.classList.remove('hide-toolbar');},};var CallHandler=(function callHandler(){var COMMS_APP_ORIGIN=document.location.protocol+'//'+
document.location.host;var FB_SYNC_ERROR_PARAM='isSyncError';var engineeringModeKey=null;function handleActivity(activity){if(activity.source.name!='dial'){return;}
var number=activity.source.data.number;if(number){KeypadManager.updatePhoneNumber(number,'begin',false);if(Navigation.currentView!='keypad'){Navigation.showKeypad();}}else{if(Navigation.currentView!='contacts'){Navigation.showContacts();}}}
function deserializeParameters(input){var rparams=/([^?=&]+)(?:=([^&]*))?/g;var parsed={};input.replace(rparams,function($0,$1,$2){parsed[$1]=decodeURIComponent($2);});return parsed;}
function handleNotification(evt){if(!evt.clicked){return;}
navigator.mozApps.getSelf().onsuccess=function gotSelf(selfEvt){var app=selfEvt.target.result;app.launch('dialer');var location=document.createElement('a');location.href=evt.imageURL;if(location.search.indexOf(FB_SYNC_ERROR_PARAM)!==-1){Navigation.showContacts();}else if(location.search.indexOf('ussdMessage')!==-1){var params=deserializeParameters(evt.imageURL);Notification.get({tag:evt.tag}).then(function(notifications){for(var i=0;i<notifications.length;i++){notifications[i].close();}});MmiManager.handleMMIReceived(evt.body,true,params.cardIndex);}else{Navigation.showCalllog();}};}
function sendNotification(number,serviceId){LazyLoader.load('/shared/js/dialer/utils.js',function(){Contacts.findByNumber(number,function lookup(contact,matchingTel){var title;if(navigator.mozIccManager.iccIds.length>1){title={id:'missedCallMultiSims',args:{n:serviceId+1}};}else{title='missedCall';}
var body;if(!number){body='from-withheld-number';}else if(contact){var primaryInfo=Utils.getPhoneNumberPrimaryInfo(matchingTel,contact);if(primaryInfo){if(primaryInfo!==matchingTel.value){body={id:'from-contact',args:{contact:primaryInfo.toString()}};}else{body={id:'from-number',args:{number:primaryInfo}};}}else{body='from-withheld-number';}}else{body={id:'from-number',args:{number:number}};}
navigator.mozApps.getSelf().onsuccess=function getSelfCB(evt){var app=evt.target.result;var iconURL=NotificationHelper.getIconURI(app,'dialer');var clickCB=function(){app.launch('dialer');Navigation.showCalllog();};NotificationHelper.send(title,{bodyL10n:body,icon:iconURL}).then(function(notification){notification.addEventListener('click',clickCB);});};});});}
function callEnded(data){var highPriorityWakeLock=navigator.requestWakeLock('high-priority');var number=data.number;var incoming=data.direction==='incoming';if(incoming&&!data.duration&&!data.hangUpLocal){sendNotification(number,data.serviceId);}
Voicemail.check(number,data.serviceId).then(function(isVoicemailNumber){var entry={date:Date.now()-parseInt(data.duration),duration:data.duration,type:incoming?'incoming':'dialing',number:number,serviceId:data.serviceId,emergency:data.emergency||false,voicemail:isVoicemailNumber,status:(incoming&&data.duration>0)?'connected':null};CallLogDBManager.add(entry,function(logEntry){CallLog.appendGroup(logEntry);if(!data.secondNumber){highPriorityWakeLock.unlock();return;}
_addSecondCdmaCall(data,isVoicemailNumber,highPriorityWakeLock);});});}
function _addSecondCdmaCall(data,isVoicemailNumber,wakeLock){var entryCdmaSecond={date:Date.now()-parseInt(data.duration),duration:data.duration,type:'incoming',number:data.secondNumber,serviceId:data.serviceId,emergency:false,voicemail:isVoicemailNumber,status:'connected'};CallLogDBManager.add(entryCdmaSecond,function(logGroupCdmaSecondCall){CallLog.appendGroup(logGroupCdmaSecondCall);wakeLock.unlock();});}
function handleMessage(evt){if(evt.origin!==COMMS_APP_ORIGIN){return;}
var data=evt.data;if(!data.type){return;}
if(data.type==='contactsiframe'){handleContactsIframeRequest(data.message);}else if(data.type==='hide-navbar'){NavbarManager.hide();}else if(data.type==='show-navbar'){NavbarManager.show();}}
window.addEventListener('message',handleMessage);function handleContactsIframeRequest(message){switch(message){case'back':var contactsIframe=document.getElementById('iframe-contacts');contactsIframe.contentWindow.history.pushState(null,null,'/contacts/index.html');Navigation.showCalllog();break;}}
function btCommandHandler(message){var command=message.command;var isAtd=command.startsWith('ATD');if(command!=='BLDN'&&!isAtd){return;}
if(isAtd&&command[3]!=='>'){var phoneNumber=command.substring(3);LazyLoader.load(['/shared/js/sim_settings_helper.js'],function(){SimSettingsHelper.getCardIndexFrom('outgoingCall',function(defaultCardIndex){if(defaultCardIndex===SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE){LazyLoader.load(['/shared/js/component_utils.js','/shared/elements/gaia_sim_picker/script.js'],function(){var simPicker=document.getElementById('sim-picker');simPicker.getOrPick(defaultCardIndex,phoneNumber,function(ci){CallHandler.call(phoneNumber,ci);});navigator.mozApps.getSelf().onsuccess=function(selfEvt){var app=selfEvt.target.result;app.launch('dialer');};});}else{CallHandler.call(phoneNumber,defaultCardIndex);}});});return;}
var position=isAtd?parseInt(command.substring(4),10):1;CallLogDBManager.getGroupAtPosition(position,'lastEntryDate',true,'dialing',function(result){if(result&&(typeof result==='object')&&result.number){LazyLoader.load(['/shared/js/sim_settings_helper.js'],function(){SimSettingsHelper.getCardIndexFrom('outgoingCall',function(ci){if(ci===undefined||ci===null||ci==SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE){ci=result.serviceId;}
CallHandler.call(result.number,ci);});});}else{console.log('Could not get the group at: '+position+'. Error: '+result);}});}
function call(number,cardIndex){if(engineeringModeKey&&number===engineeringModeKey){var activity=new MozActivity({name:'internal-system-engineering-mode'});activity.onerror=function(){console.log('Could not launch engineering mode');};return;}
if(MmiManager.isImei(number)){MmiManager.showImei();KeypadManager.updatePhoneNumber('','begin',true);SuggestionBar.clear();return;}
var connected,disconnected;connected=disconnected=function clearPhoneView(){KeypadManager.updatePhoneNumber('','begin',true);};var oncall=function(){SuggestionBar.hideOverlay();SuggestionBar.clear();};LazyLoader.load(['/dialer/js/telephony_helper.js'],function(){TelephonyHelper.call(number,cardIndex,oncall,connected,disconnected).catch(function(){KeypadManager.updatePhoneNumber(number,'begin',false);});});}
function onUssdReceived(evt){var lock=null;var safetyId;function releaseWakeLock(){if(lock){lock.unlock();lock=null;clearTimeout(safetyId);}}
if(document.hidden){lock=navigator.requestWakeLock('high-priority');safetyId=setTimeout(releaseWakeLock,30000);document.addEventListener('visibilitychange',releaseWakeLock);}
if(!document.hidden||evt.session){MmiManager.handleMMIReceived(evt.message,evt.session,evt.serviceId);}else if(evt.message){MmiManager.sendNotification(evt.message,evt.serviceId).then(releaseWakeLock);}else{releaseWakeLock();}}
function init(){LazyLoader.load(['/shared/js/mobile_operator.js','/dialer/js/mmi.js','/dialer/js/mmi_ui.js','/shared/style/progress_activity.css','/dialer/style/mmi.css'],function(){if(window.navigator.mozSetMessageHandler){window.navigator.mozSetMessageHandler('telephony-call-ended',callEnded);window.navigator.mozSetMessageHandler('activity',handleActivity);window.navigator.mozSetMessageHandler('notification',handleNotification);window.navigator.mozSetMessageHandler('bluetooth-dialer-command',btCommandHandler);window.navigator.mozSetMessageHandler('ussd-received',onUssdReceived);}});LazyLoader.load('/shared/js/settings_listener.js',function(){SettingsListener.observe('engineering-mode.key',null,function(value){engineeringModeKey=value||null;});});}
return{init:init,call:call};})();window.onresize=function(e){if(window.innerHeight<440){NavbarManager.hide();}else{NavbarManager.show();}};

View file

@ -0,0 +1,3 @@
;'use strict';function onLoadDialer(){window.performance.mark('navigationLoaded');window.performance.mark('visuallyLoaded');window.removeEventListener('load',onLoadDialer);if(navigator.mozAudioChannelManager){navigator.mozAudioChannelManager.volumeControlChannel='notification';}
KeypadManager.init(false);window.performance.mark('contentInteractive');NavbarManager.init();window.performance.mark('navigationInteractive');setTimeout(function nextTick(){var lazyPanels=['confirmation-message','edit-mode','sim-picker'];var lazyPanelsElements=lazyPanels.map(function toElement(id){return document.getElementById(id);});LazyLoader.load(lazyPanelsElements);CallHandler.init();navigator.mozL10n.once(function loadLazyFilesSet(){LazyLoader.load(['/shared/js/fb/fb_request.js','/shared/js/fb/fb_data_reader.js','/shared/js/fb/fb_reader_utils.js','/shared/style/confirm.css','/shared/js/confirm.js','/shared/elements/config.js','/shared/elements/gaia-header/dist/gaia-header.js','/shared/style/edit_mode.css'],function fileSetLoaded(){window.performance.mark('fullyLoaded');});});});}
window.addEventListener('load',onLoadDialer);

View file

@ -0,0 +1,475 @@
/* globals LazyLoader, MmiUI, MobileOperator, NotificationHelper,
Promise */
/* exported MmiManager */
'use strict';
// As defined in 3GPP TS 22.030 version 10.0.0 Release 10 standard
// USSD code used to query call barring supplementary service status
const CALL_BARRING_STATUS_MMI_CODE = '*#33#';
// USSD code used to query call waiting supplementary service status
const CALL_WAITING_STATUS_MMI_CODE = '*#43#';
var MmiManager = {
_: null,
_conn: null,
_ready: false,
// In some cases, the RIL doesn't provide the expected order of events
// while sending an MMI that triggers an interactive USSD request (specially
// while roaming), which should be DOMRequest.onsuccess (or .onerror) +
// ussdreceived. If the first event received is the ussdreceived one, we take
// the DOMRequest.onsuccess received subsequenty as a closure of the USSD
// session.
_pendingRequest: null,
_session: null,
init: function mm_init() {
if (this._ready) {
return Promise.resolve();
}
this._ = navigator.mozL10n.get;
var self = this;
var lazyFiles = ['/shared/js/icc_helper.js',
'/shared/style/input_areas.css',
'/shared/js/mobile_operator.js'];
return new Promise(function(resolve, reject) {
LazyLoader.load(lazyFiles, function() {
MmiUI.init();
self._ready = true;
resolve();
});
});
},
/**
* Handle dialing an MMI code by starting the UI and following up on the
* promise in the MMICall object returned by Telephony.dial().
*
* @param {Object} conn The MozMobileConnection on which the MMI code was
* dialed on.
* @param {String} message The dialed MMI code.
* @param {Promise} promise The MMICall promise.
* @returns {Promise} A promise that is resolved once the MMI message
* response has been handled.
*/
handleDialing: function mm_handleDialing(conn, message, promise) {
var self = this;
this._conn = conn;
this._pendingRequest = promise;
return this.init().then(function() {
self.openUI();
/* TODO we are creating this callback instead of just following up on
* the promise because we need to pass the original mmi code sent. This
* should be removed when bug 889737 and bug 1049651 are landed as it
* should be possible to get it in the callback. */
return promise.then(function(result) {
if (result.success) {
self.notifySuccess(result, message);
} else {
self.notifyError(result, message);
}
});
});
},
// Passing the sent MMI code because the message displayed to the user
// could be different depending on the MMI code.
notifySuccess: function mm_notifySuccess(mmiResult, sentMMI) {
// Helper function to compose an informative message about a successful
// request to query the call forwarding status.
var processCf = (function processCf(result) {
var voice, data, fax, sms, sync, async, packet, pad;
for (var i = 0; i < result.length; i++) {
if (!result[i].active) {
continue;
}
for (var serviceClassMask = 1;
serviceClassMask <= this._conn.ICC_SERVICE_CLASS_MAX;
serviceClassMask <<= 1) {
if ((serviceClassMask & result[i].serviceClass) !== 0) {
switch (serviceClassMask) {
case this._conn.ICC_SERVICE_CLASS_VOICE:
voice = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_DATA:
data = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_FAX:
fax = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_SMS:
sms = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_DATA_SYNC:
sync = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_DATA_ASYNC:
async = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_PACKET:
packet = result[i].number;
break;
case this._conn.ICC_SERVICE_CLASS_PAD:
pad = result[i].number;
break;
default:
return this._('call-forwarding-error');
}
}
}
}
var inactive = this._('call-forwarding-inactive');
var msg = [
this._('call-forwarding-status'),
this._('call-forwarding-voice', { voice: voice || inactive }),
this._('call-forwarding-data', { data: data || inactive }),
this._('call-forwarding-fax', { fax: fax || inactive }),
this._('call-forwarding-sms', { sms: sms || inactive }),
this._('call-forwarding-sync', { sync: sync || inactive }),
this._('call-forwarding-async', { async: async || inactive }),
this._('call-forwarding-packet', { packet: packet || inactive }),
this._('call-forwarding-pad', { pad: pad || inactive })
].join('\n');
return msg;
}).bind(this);
var ci = this.cardIndexForConnection(this._conn);
var message = null;
var title = null;
var error = null;
// We always expect an MMIResult object even for USSD requests.
if (!mmiResult) {
MmiUI.error(this._('GenericFailure'));
return;
}
if (mmiResult.serviceCode) {
title = this.prependSimNumber(this._(mmiResult.serviceCode), ci);
}
var additionalInformation = mmiResult.additionalInformation;
switch (mmiResult.serviceCode) {
case 'scCall':
/* Call control, no need to show a result page as the command will
* already have been executed (hang up a call, etc...). */
MmiUI.cancel();
return;
case 'scUssd':
// Bail out if there is nothing to show or if we got the .onsuccess
// event after the .onussdevent.
if (!mmiResult.statusMessage || this._pendingRequest === null) {
return;
}
message = mmiResult.statusMessage;
break;
case 'scPin':
case 'scPin2':
case 'scPuk':
case 'scPuk2':
if (mmiResult.statusMessage) {
message = this._(mmiResult.statusMessage);
}
break;
case 'scCallForwarding':
if (mmiResult.statusMessage) {
message = this._(mmiResult.statusMessage);
// Call forwarding requests via MMI codes might return an array of
// nsIDOMMozMobileCFInfo objects. In that case we serialize that array
// into a single string that can be shown on the screen.
if (additionalInformation) {
message = processCf(additionalInformation);
}
} else {
error = this._('GenericFailure');
}
break;
case 'scCallBarring':
case 'scCallWaiting':
message = this._(mmiResult.statusMessage);
// If we are just querying the status of the service, we show a
// different message, so the user knows she hasn't change anything
if (sentMMI === CALL_BARRING_STATUS_MMI_CODE ||
sentMMI === CALL_WAITING_STATUS_MMI_CODE) {
if (mmiResult.statusMessage === 'smServiceEnabled') {
message = this._('ServiceIsEnabled');
} else if (mmiResult.statusMessage === 'smServiceDisabled') {
message = this._('ServiceIsDisabled');
} else if (mmiResult.statusMessage === 'smServiceEnabledFor') {
message = this._('ServiceIsEnabledFor');
}
}
// Call barring and call waiting requests via MMI codes might return an
// array of strings indicating the service it is enabled for or just
// the disabled status message.
if (mmiResult.statusMessage === 'smServiceEnabledFor' &&
additionalInformation &&
Array.isArray(additionalInformation)) {
for (var i = 0, l = additionalInformation.length; i < l; i++) {
message += '\n' + this._(additionalInformation[i]);
}
}
break;
default:
// This would allow carriers and others to implement custom MMI codes
// with title and statusMessage only.
if (mmiResult.statusMessage) {
message = this._(mmiResult.statusMessage) || mmiResult.statusMessage;
}
break;
}
if (!error) {
MmiUI.success(message, title);
} else {
MmiUI.error(error, title);
}
},
notifyError: function mm_notifyError(mmiError) {
var title = null;
var ci = this.cardIndexForConnection(this._conn);
if (mmiError.serviceCode) {
title = this.prependSimNumber(this._(mmiError.serviceCode), ci);
}
var error = mmiError.statusMessage ? this._(mmiError.statusMessage)
: this._('GenericFailure');
switch (mmiError.serviceCode) {
case 'scPin':
case 'scPin2':
case 'scPuk':
case 'scPuk2':
// If the error is related with an incorrect old PIN, we get the
// number of remainings attempts.
if (mmiError.additionalInformation &&
(mmiError.statusMessage === 'emMmiErrorPasswordIncorrect' ||
mmiError.statusMessage === 'emMmiErrorBadPin' ||
mmiError.statusMessage === 'emMmiErrorBadPuk')) {
error += '\n' + this._('emMmiErrorPinPukAttempts', {
n: mmiError.additionalInformation
});
}
break;
}
MmiUI.error(error, title);
},
openUI: function mm_openUI() {
this.init().then(function() {
MmiUI.loading();
});
},
/**
* Create a notification/message string by prepending the SIM number if the
* phone has more than one SIM card.
*
* @param text {String} The message text.
* @param cardIndex {Integer} The SIM card slot index.
* @return {String} Either the original string alone or with the SIM number
* prepended to it.
*/
prependSimNumber: function mm_prependSimNumber(text, cardIndex) {
if (window.navigator.mozIccManager &&
window.navigator.mozIccManager.iccIds.length > 1) {
var simName = this._('sim-number', { n: +cardIndex + 1 });
text = this._(
'mmi-notification-title-with-sim',
{ sim: simName, title: text }
);
}
return text;
},
/**
* Handles an MMI/USSD message. Pops up the MMI UI and displays the message.
*
* @param {String} message An MMI/USSD message.
* @param {Object} session The object representing the USSD session.
* @param {Integer} cardIndex The index of the SIM card on which this message
* was received.
*/
handleMMIReceived: function mm_handleMMIReceived(message, session, cardIndex)
{
var self = this;
return this.init().then(function() {
self._pendingRequest = null;
self._session = session;
// Do not notify the UI if no message to show.
if (!message && session) {
return;
}
var conn = navigator.mozMobileConnections[cardIndex || 0];
var operator = MobileOperator.userFacingInfo(conn).operator;
var title = self.prependSimNumber(operator || '', cardIndex);
MmiUI.received(session, message, title);
});
},
/**
* Sends a notification for the specified message, returns a promise that is
* resolved once the operation is finished.
*
* @param {String} message An MMI/USSD message.
* @param {Integer} cardIndex The index of the SIM card on which this message
* was received.
* @return {Promise} A promise that is resolved once the operation is
* finished.
*/
sendNotification: function mm_sendNotification(message, cardIndex) {
var self = this;
return new Promise(function(resolve, reject) {
var request = window.navigator.mozApps.getSelf();
request.onsuccess = function(evt) {
var app = evt.target.result;
self.init().then(function() {
LazyLoader.load('/shared/js/notification_helper.js', function() {
var iconURL = NotificationHelper.getIconURI(app, 'dialer');
var clickCB = function(evt) {
self.handleMMIReceived(message, /* session */ null, cardIndex);
};
var conn = navigator.mozMobileConnections[cardIndex || 0];
var operator = MobileOperator.userFacingInfo(conn).operator;
var titleL10n = self.prependSimNumber(
operator ? operator : '', cardIndex);
/* XXX: Bug 1033254 - We put the |ussd-message=1| parameter in the
* URL string to distinguish this notification from the others.
* This should be thorought the application possibly by using the
* tag field. */
NotificationHelper.send(titleL10n, {
body: message,
icon: iconURL + '?ussdMessage=1&cardIndex=' + cardIndex,
tag: Date.now()
}).then(notification => {
notification.addEventListener('click', clickCB);
resolve();
});
});
});
};
request.onerror = function(error) {
reject(error);
};
});
},
reply: function mm_reply(message) {
var self = this;
return this._session.send(message).then(function(request) {
self._pendingRequest = request;
});
},
cancel: function mm_cancel() {
this._pendingRequest = null;
if (this._session) {
this._session.cancel();
this._session = null;
}
},
cardIndexForConnection: function mm_cardIndexForConnection(conn) {
for (var i = 0; i < navigator.mozMobileConnections.length; i++) {
if (conn === navigator.mozMobileConnections[i]) {
return i;
}
}
return 0;
},
/**
* Retrieves the IMEI code for the specified SIM card slot.
*
* @param {Integer} cardIndex The index of the SIM card slot.
* @returns {Promise} A promise that resolves to the IMEI code for the slot
* upon successful completion or rejects upon failure.
*/
_getImeiForCard: function mm_getImeiForCard(cardIndex) {
return navigator.mozTelephony.dial('*#06#', cardIndex).then(
function(mmiCall) {
return mmiCall.result.then(function(result) {
/* We always expect the IMEI, so if we got a successful completion
* without the IMEI value, we throw an error message. */
if ((result === null) || (result.serviceCode !== 'scImei') ||
(result.statusMessage === null)) {
return Promise.reject(
new Error('Could not retrieve the IMEI code for SIM' +
cardIndex));
}
return result.statusMessage;
});
}
);
},
/**
* Returns true if the specified number is the MMI code used to request the
* IMEI codes of the phone's SIM slots (*#06#).
*
* @param {String} number A phone number or MMI code.
* @returns {Boolean} true if the passed number is the MMI IMEI request code
* and false in all other cases.
*/
isImei: function mm_isImei(number) {
return (number === '*#06#');
},
/**
* Sends the necessary MMI messages to retrieve IMEI codes for all SIM slots
* and displays the resulting codes on the screen.
*
* @returns {Promise} A promise that is resolved when the operation has been
* completed.
*/
showImei: function mm_showImei() {
var self = this;
return new Promise(function(resolve, reject) {
self.init().then(function() {
var promises = [];
for (var i = 0; i < navigator.mozMobileConnections.length; i++) {
promises.push(self._getImeiForCard(i));
}
self.openUI();
Promise.all(promises).then(function(imeis) {
MmiUI.success(imeis.join('\n'), self._('scImei'));
resolve();
}, function(reason) {
MmiUI.error(self._('GenericFailure'));
reject(reason);
});
});
});
}
};

View file

@ -0,0 +1,153 @@
/* globals MmiManager */
/* exported MmiUI */
'use strict';
var MmiUI = {
_: null,
headerTitleNode: null,
headerNode: null,
cancelNode: null,
sendNode: null,
messageNode: null,
responseTextNode: null,
responseTextResetNode: null,
mmiScreen: null,
loadingOverlay: null,
init: function mui_init() {
this._ = navigator.mozL10n.get;
this.headerTitleNode = document.getElementById('header-title');
this.headerNode = document.getElementById('mmi-header');
this.cancelNode = document.getElementById('cancel');
this.sendNode = document.getElementById('send');
this.messageNode = document.getElementById('message');
this.responseTextNode = document.getElementById('response-text');
this.responseTextResetNode = document.getElementById('response-text-reset');
this.mmiScreen = document.getElementById('mmi-screen');
this.loadingOverlay = document.getElementById('loading-overlay');
this.headerNode.addEventListener('action', this.closeWindow.bind(this));
this.cancelNode.addEventListener('click', this.cancel.bind(this));
this.sendNode.addEventListener('click', this.reply.bind(this));
this.responseTextResetNode.addEventListener('click',
this.resetResponse.bind(this));
this.responseTextNode.addEventListener('input',
this.responseUpdated.bind(this));
},
showWindow: function mui_showWindow() {
this.mmiScreen.hidden = false;
},
closeWindow: function mui_closeWindow() {
MmiManager.cancel();
this.resetResponse();
this.mmiScreen.hidden = true;
},
cancel: function mui_cancel() {
this.hideLoading();
this.closeWindow();
},
showMessage: function mui_showMessage(message, header) {
this.showWindow();
this.hideLoading();
this.responseTextNode.removeAttribute('disabled');
this.messageNode.textContent = message;
if (header && header.length) {
this.headerTitleNode.textContent = header;
} else {
this.headerTitleNode.textContent = '';
}
// Make sure the app is displayed
navigator.mozApps.getSelf().onsuccess = function getSelfCB(evt) {
evt.target.result.launch('dialer');
};
},
showLoading: function mui_showLoading() {
this.loadingOverlay.classList.remove('hide');
this.loadingOverlay.classList.remove('fade-out');
this.loadingOverlay.classList.add('fade-in');
this.responseTextNode.setAttribute('disabled', 'disabled');
this.sendNode.setAttribute('disabled', 'disabled');
},
hideLoading: function mui_hideLoading() {
var self = this;
this.loadingOverlay.classList.remove('fade-in');
this.loadingOverlay.addEventListener('animationend',
function mui_fadeOut(ev) {
self.loadingOverlay.removeEventListener('animationend', mui_fadeOut);
self.loadingOverlay.classList.add('hide');
});
this.loadingOverlay.classList.add('fade-out');
},
showResponseForm: function mui_showForm() {
this.mmiScreen.classList.add('responseForm');
this.sendNode.classList.remove('hide');
},
hideResponseForm: function mui_hideForm() {
this.mmiScreen.classList.remove('responseForm');
this.sendNode.classList.add('hide');
},
resetResponse: function mui_resetResponse() {
this.responseTextNode.value = '';
this.sendNode.setAttribute('disabled', 'disabled');
},
responseUpdated: function mui_responseUpdated() {
if (this.responseTextNode.value.length <= 0) {
this.sendNode.setAttribute('disabled', 'disabled');
} else {
this.sendNode.removeAttribute('disabled');
}
},
reply: function mui_reply() {
this.showLoading();
MmiManager.reply(this.responseTextNode.value);
this.resetResponse();
},
success: function mui_success(message, title) {
message = message || this._('mmi-successfully-sent');
this.hideResponseForm();
this.showMessage(message, title);
},
error: function mui_error(error, title) {
error = error || this._('mmi-error');
this.hideResponseForm();
this.showMessage(error, title);
},
received: function mui_received(session, message, title) {
if (!session) {
this.hideResponseForm();
if (message === null) {
message = this._('mmi-session-expired');
}
} else {
this.showResponseForm();
}
this.showMessage(message, title);
},
loading: function mui_loading() {
this.showLoading();
},
};

View file

@ -0,0 +1,10 @@
;(function(exports){'use strict';var selectors={recents:'recents-panel',contacts:'iframe-contacts-container',keypad:'keypad-panel'};var currentDestination=null;function checkContactsTab(){var contactsIframe=document.getElementById('iframe-contacts');if(!contactsIframe){return;}
var index=contactsIframe.src.indexOf('#add-parameters');if(index!=-1){contactsIframe.src=contactsIframe.src.substr(0,index);}}
function update(destination){var recent=document.getElementById('option-recents');var contacts=document.getElementById('option-contacts');var keypad=document.getElementById('option-keypad');var tabs=[recent,contacts,keypad];recent.classList.remove('toolbar-option-selected');contacts.classList.remove('toolbar-option-selected');keypad.classList.remove('toolbar-option-selected');switch(destination){case'recents':checkContactsTab();recent.classList.add('toolbar-option-selected');AccessibilityHelper.setAriaSelected(recent,tabs);CallLog.init();break;case'contacts':var frame=document.getElementById('iframe-contacts');if(!frame){var view=document.getElementById('iframe-contacts-container');frame=document.createElement('iframe');frame.src='/contacts/index.html';frame.id='iframe-contacts';frame.setAttribute('frameBorder','no');frame.classList.add('grid-wrapper');view.appendChild(frame);}
contacts.classList.add('toolbar-option-selected');AccessibilityHelper.setAriaSelected(contacts,tabs);var forceHashChange=new Date().getTime();frame.src='/contacts/index.html#home?forceHashChange='+
forceHashChange;break;case'keypad':checkContactsTab();keypad.classList.add('toolbar-option-selected');AccessibilityHelper.setAriaSelected(keypad,tabs);break;}}
exports.Navigation={get currentView(){return currentDestination;},show:function(destination){var selector=selectors[destination];if(!selector){throw new Error('Navigation: Invalid destination: '+destination);}
var view=document.getElementById(selector);if(!view){throw new Error('Navigation: Panel does not exist: '+destination);}
if(currentDestination&&currentDestination===destination){return;}
view.style.visibility='visible';if(currentDestination){var currentSelector=selectors[currentDestination];document.getElementById(currentSelector).style.visibility='hidden';}
currentDestination=destination;update(destination);},showKeypad:function(){this.show('keypad');},showContacts:function(){this.show('contacts');},showCalllog:function(){this.show('recents');}};}(window));

View file

@ -0,0 +1,409 @@
/* globals CallHandler, Contacts, fb, KeypadManager, LazyLoader,
SimplePhoneMatcher, SimSettingsHelper, Utils */
// Suggestion_bar.js will be loaded on init of KeypadManager through
// lazy loader. So we call its init() directly at the end of file.
'use strict';
var SuggestionBar = {
MIN_DIGIT_TO_SHOW: 3,
SKIP_FOR_COUNTRYCODE: 2,
MAX_ITEMS: 50,
_phoneNumber: null,
_contactList: null,
_loaded: false,
_pendingFbRequest: null,
_hasMatchingFbContacts: false,
_hasMatchingLocalContacts: false,
// Visual Elements
bar: null,
barSuggestionItem: null,
countTag: null,
list: null,
overlay: null,
overlayCancel: null,
template: null,
init: function sb_init() {
// When the DOM is absent (in the call screen) we don't need
// to initialize the module.
this.overlay = document.getElementById('contact-list-overlay');
if (!this.overlay) {
return;
}
this.bar = document.getElementById('suggestion-bar');
this.barSuggestionItem = this.bar.querySelector('.js-suggestion-item');
this.countTag = document.getElementById('suggestion-count');
this.template = document.getElementById('contact-in-overlay-template');
this.overlay.addEventListener('click', this);
this.bar.addEventListener('click', this);
this.countTag.addEventListener('click', this.showOverlay.bind(this));
KeypadManager.onValueChanged = this.update.bind(this);
},
handleEvent: function sb_handleEvent(event) {
var node = event.target;
if (!node.classList.contains('js-suggestion-item')) {
return;
}
event.stopPropagation();
var telTag = node.querySelector('.js-tel');
KeypadManager.updatePhoneNumber(telTag.textContent, 'begin', false);
// In the multi-SIM case, we just autocomplete the phone number without
// making the call. The call button is responsible for SIM selection
// behavior.
if (!navigator.mozIccManager ||
navigator.mozIccManager.iccIds.length < 2) {
LazyLoader.load('/shared/js/sim_settings_helper.js', function() {
SimSettingsHelper.getCardIndexFrom('outgoingCall', function(ci) {
CallHandler.call(KeypadManager.phoneNumber(), ci);
});
});
} else {
this.hideOverlay();
}
},
update: function sb_update(number) {
this._phoneNumber = number;
// when first letter of number is "+", we'll change minimum search criteria
// from MIN_DIGIT_TO_SHOW to MIN_DIGIT_TO_SHOW + SKIP_FOR_COUNTRYCODE.
var min = this.MIN_DIGIT_TO_SHOW +
((number.charAt(0) === '+') ? this.SKIP_FOR_COUNTRYCODE : 0);
if (number.length < min) {
this.clear(true);
return;
}
if (this._loaded) {
this._updateByContacts();
return;
}
var self = this;
LazyLoader.load(['/shared/js/async_storage.js',
'/shared/js/dialer/contacts.js',
'/shared/js/simple_phone_matcher.js',
this.barSuggestionItem,
this.template],
function callback() {
self._loaded = true;
self._updateByContacts();
});
},
_renderBar: function sb_renderBar() {
var self = this;
self.bar.classList.remove('hide');
// Create matching index table for reference
self._allMatched = self._getAllMatched(self._contactList);
var totalMatchNum = self._allMatched.totalMatchNum;
var contact = self._contactList[0];
var firstMatch = self._allMatched.allMatches[0][0];
var shouldHideSuggestionBar = false;
// In a multi-SIM setup, tapping on a suggestion in the settings bar doesn't
// place a call, it just fills in the phone number. In this case, we should
// hide the suggestions bar to not confuse the user into thinking that
// tapping it again will place the call.
if (totalMatchNum === 1 &&
contact.tel[firstMatch].value == self._phoneNumber &&
navigator.mozIccManager &&
navigator.mozIccManager.iccIds.length > 1) {
shouldHideSuggestionBar = true;
}
// Don't show any suggestions if we have too many. The user should narrow it
// down further by entering more digits.
if (totalMatchNum > self.MAX_ITEMS) {
shouldHideSuggestionBar = true;
}
if (shouldHideSuggestionBar) {
self.clear();
return 0;
}
self.countTag.textContent = totalMatchNum;
var hasMoreThanOneMatch = (totalMatchNum > 1);
self.countTag.hidden = !hasMoreThanOneMatch;
self.countTag.classList.toggle('more', hasMoreThanOneMatch);
var node = self.barSuggestionItem;
self._fillContacts(contact, firstMatch, node);
self.bar.dataset.lastId = contact.id || contact.uid;
return totalMatchNum;
},
_checkIfCleared: function sb_checkIfCleared() {
var out = false;
if (!this._phoneNumber) {
this.bar.dataset.lastId = '';
this.clear(true);
out = true;
}
return out;
},
_searchCallback: function sb_searchCallback(contacts) {
var self = this;
self._pendingFbRequest = null;
if (self._checkIfCleared()) {
return;
}
var totalMatchNum = 0;
if (Array.isArray(contacts) && contacts.length > 0) {
self._contactList = contacts;
totalMatchNum = self._renderBar();
self._hasMatchingLocalContacts = true;
}
else {
// Avoid to clear the list if we have previous FB Contacts
// This will avoid continous reflows while refreshing
if (!self._hasMatchingFbContacts ||
(self._hasMatchingFbContacts && self._hasMatchingLocalContacts)) {
self.clear(false);
}
self._hasMatchingLocalContacts = false;
self._contactList = null;
}
if (totalMatchNum > self.MAX_ITEMS) {
self._hasMatchingFbContacts = false;
return;
}
var req = fb.contacts.search('phone', self._phoneNumber);
self._pendingFbRequest = req;
req.onsuccess = function() {
// Avoid to overlap FB requests
if (self._pendingFbRequest === req) {
self._pendingFbRequest = null;
self._searchCallbackFb(req.result);
}
};
req.onerror = function() {
window.console.error('Error while searching FB Data: ', req.error.name);
self._hasMatchingFbContacts = false;
};
},
_searchCallbackFb: function sb_searchCallbackFb(contacts) {
if (this._checkIfCleared()) {
return;
}
if (!Array.isArray(contacts) || contacts.length === 0) {
this._hasMatchingFbContacts = false;
if (!this._hasMatchingLocalContacts) {
this.clear(true);
}
return;
}
this._hasMatchingFbContacts = true;
this._contactList = (this._contactList || []).concat(contacts);
this._renderBar();
},
_updateByContacts: function sb_updateByContacts() {
// A search is both launched on mozContacts and on Facebook DS
Contacts.findListByNumber(this._phoneNumber, this.MAX_ITEMS + 1,
this._searchCallback.bind(this));
},
_fillContacts: function sb_fillContacts(contact, matchLocal, node) {
// if first letter of query is '+' and first letter of matchedTel isn't '+'
// we use query without country code instead of original query for
// markedNumber.
var matchedTel = contact.tel[matchLocal];
var markedNumber = this._markMatched(matchedTel.value, this._phoneNumber);
this._setItem(node, markedNumber, matchedTel.type,
contact.name[0]);
},
_createItem: function sb_createItem() {
var itemElm = this.template.cloneNode(true);
itemElm.removeAttribute('id');
itemElm.hidden = false;
itemElm.classList.add('ci--action-menu');
this.list.insertBefore(itemElm, this.overlayCancel);
return itemElm;
},
_setItem: function sb_setItem(node, tel, type, name) {
var typeTag = node.querySelector('.js-tel-type');
var telTag = node.querySelector('.js-tel');
var nameTag = node.querySelector('.js-name');
// Set the contact name
nameTag.textContent = name ? name : null;
// Set the number with the highlighted match
while (telTag.firstChild) {
telTag.removeChild(telTag.firstChild);
}
if (tel) {
telTag.appendChild(tel);
}
// Set the phone type
if (type) {
if (Utils.isPhoneType(type)) {
navigator.mozL10n.setAttributes(typeTag, type);
} else {
// No localization found, use the type string as-is
typeTag.removeAttribute('data-l10n-id');
typeTag.textContent = type;
}
} else {
typeTag.textContent = null;
}
},
clear: function sb_clear(isHardClear) {
if (!this._loaded) {
return;
}
this.countTag.textContent = '';
this.countTag.classList.remove('more');
// Clear contents
var node = this.barSuggestionItem;
this._setItem(node);
this._contactList = null;
this.bar.classList.add('hide');
if (isHardClear) {
this._phoneNumber = null;
delete this.bar.dataset.lastId;
}
},
_markMatched: function sb_markMatched(str, substr) {
var fragment = document.createDocumentFragment();
var sanitized = SimplePhoneMatcher.sanitizedNumber(str);
var digitBeforeMatch = sanitized.indexOf(substr);
if (digitBeforeMatch == -1) {
fragment.appendChild(document.createTextNode(str));
return fragment;
}
// Highlight matched number. We need to count formatting character in.
// we already have sanitized match position above. Then we search from
// head of str, count only valid digits to find corresponding start and
// end position in origin str.
var start = NaN;
var end = NaN;
var validDigits = 0;
for (var i = 0; i < str.length; i++) {
if (/^[\d\+*#]$/.test(str[i])) {
validDigits++;
}
if ((validDigits > digitBeforeMatch) && isNaN(start)) {
start = i;
} else if (validDigits >= (digitBeforeMatch + substr.length) &&
isNaN(end)) {
end = i;
}
}
var startStr = str.substr(0, start);
var middleStr = str.substr(start, end - start + 1);
var endStr = str.substr(end + 1);
var startNode = document.createTextNode(startStr);
var middleNode = document.createElement('mark');
middleNode.textContent = middleStr;
middleNode.classList.add('ci__mark');
var endNode = document.createTextNode(endStr);
fragment.appendChild(startNode);
fragment.appendChild(middleNode);
fragment.appendChild(endNode);
return fragment;
},
_initOverlay: function() {
if (this.list) {
return;
}
this.list = document.getElementById('contact-list');
this.overlayCancel = document.getElementById('contact-list-overlay-cancel');
this.overlayCancel.addEventListener('click', this.hideOverlay.bind(this));
},
_clearOverlay: function() {
while (this.list.firstElementChild != this.overlayCancel) {
this.list.firstElementChild.remove();
}
},
showOverlay: function sb_showOverlay() {
var self = this;
LazyLoader.load(this.overlay, function() {
self._initOverlay();
self._clearOverlay();
var title = self.overlay.querySelector('header');
navigator.mozL10n.setAttributes(
title,
'suggestionMatches',
{
n: +self.countTag.textContent,
matchNumber: self._phoneNumber
}
);
for (var i = 0; i < self._contactList.length; i++) {
for (var j = 0; j < self._allMatched.allMatches[i].length; j++) {
var node = self._createItem();
self._fillContacts(self._contactList[i],
self._allMatched.allMatches[i][j], node);
}
}
self.overlay.setAttribute('aria-hidden', false);
self.overlay.classList.add('display');
});
},
_getAllMatched: function sb_getAllMatched(contacts) {
var variants = SimplePhoneMatcher.generateVariants(this._phoneNumber);
var contactTels = contacts.map(function getTels(contact) {
return contact.tel.map(function getNumber(tel) {
return tel.value;
});
});
return SimplePhoneMatcher.bestMatch(variants, contactTels);
},
hideOverlay: function sb_hideOverlay() {
this.overlay.setAttribute('aria-hidden', true);
this.overlay.classList.remove('display');
}
};
SuggestionBar.init();

View file

@ -0,0 +1,225 @@
'use strict';
/* global LazyLoader, ConfirmDialog, MmiManager, TelephonyCall,
TelephonyMessages */
/* exported TelephonyHelper */
var TelephonyHelper = (function() {
// DTMF control digit separator length (ms) as defined in GSM ETSI GSM 02.07
const DTMF_SEPARATOR_PAUSE_DURATION = 3000;
var confirmLoaded = false;
var loadTelephonyMessages = function(callback) {
LazyLoader.load(['/shared/js/dialer/telephony_messages.js'], callback);
};
var call = function t_call(number, cardIndex, oncall, onconnected,
ondisconnected) {
var sanitizedNumber = number.replace(/(\s|-|\.|\(|\))/g, '');
if (!isValid(sanitizedNumber)) {
loadTelephonyMessages(function() {
TelephonyMessages.displayMessage('BadNumber');
});
return Promise.reject();
}
var conn = navigator.mozMobileConnections &&
navigator.mozMobileConnections[cardIndex];
if (!conn || !conn.voice) {
// No voice connection, the call won't make it
loadTelephonyMessages(function() {
TelephonyMessages.displayMessage('NoNetwork');
});
return Promise.reject();
}
return startDial(
cardIndex, conn, sanitizedNumber, oncall, onconnected, ondisconnected
);
};
function getNumberAsDtmfToneGroups(number) {
return number.split(',');
}
function playDtmfToneGroups(dtmfToneGroups, cardIndex) {
var length;
// Remove the dialed number from the beginning of the array.
dtmfToneGroups = dtmfToneGroups.slice(1);
length = dtmfToneGroups.length;
// Remove the latest entries from dtmfToneGroups corresponding to ','
// characters not to play those pauses.
var lastCommaIndex = length - 1;
while (dtmfToneGroups[lastCommaIndex] === '') {
lastCommaIndex--;
}
dtmfToneGroups = dtmfToneGroups.slice(0, ++lastCommaIndex);
length = dtmfToneGroups.length;
var promise = Promise.resolve(),
counter = 0,
pauses;
// Traverse the dtmfToneGroups array.
while (counter < length) {
// Reset the number of pauses before each group of tones.
pauses = 1;
while (dtmfToneGroups[counter] === '') {
// Add a new pause for each '' in the dtmfToneGroups array.
pauses++;
counter++;
}
// Send a new group of tones as well as the pauses to play before it.
promise = promise.then(playDtmfToneGroup.bind(null,
dtmfToneGroups[counter++], pauses, cardIndex));
}
return promise;
}
function playDtmfToneGroup(toneGroup, pauses, cardIndex) {
return navigator.mozTelephony.sendTones(
toneGroup,
DTMF_SEPARATOR_PAUSE_DURATION * pauses,
null /* tone duration */,
cardIndex
);
}
function startDial(cardIndex, conn, sanitizedNumber, oncall, onconnected,
ondisconnected) {
var telephony = navigator.mozTelephony;
if (!telephony) {
return Promise.reject();
}
var emergencyOnly = conn.voice.emergencyCallsOnly;
var hasCard = (conn.iccId !== null);
var callPromise;
var baseNumber = getNumberAsDtmfToneGroups(sanitizedNumber)[0];
if (emergencyOnly) {
loadConfirm(function() {
ConfirmDialog.show(
'connectingEllipsis',
'',
{
title: 'emergencyDialogBtnOk',
callback: function() {
ConfirmDialog.hide();
}
}
);
document.addEventListener('visibilitychange', function hideDialog() {
document.removeEventListener('visibilitychange', hideDialog);
/* In tests this event can happen after the ConfirmDialog mock has
* been removed so check that it's still there. */
ConfirmDialog && ConfirmDialog.hide();
});
});
// If the mobileConnection has a sim card we let gecko take the
// default service, otherwise we force the first slot.
cardIndex = hasCard ? undefined : 0;
callPromise = telephony.dialEmergency(baseNumber);
} else {
callPromise = telephony.dial(baseNumber, cardIndex);
}
callPromise.then(function(obj) {
if (obj instanceof TelephonyCall) {
installHandlers(obj, sanitizedNumber, emergencyOnly, cardIndex,
oncall, onconnected, ondisconnected);
} else {
/* This is an MMICall object, manually invoke the handlers to provide
* feedback to the user, the rest of the UX will be dealt with by the
* MMI manager. */
oncall();
onconnected();
MmiManager.handleDialing(conn, sanitizedNumber, obj.result);
}
}, function(errorName) {
loadTelephonyMessages(function() {
var messageType = emergencyOnly ? TelephonyMessages.NO_NETWORK :
TelephonyMessages.REGULAR_CALL;
TelephonyMessages.handleError(
errorName, sanitizedNumber, messageType);
});
});
return callPromise;
}
function installHandlers(call, number, emergencyOnly, cardIndex,
oncall, onconnected, ondisconnected) {
if (oncall) {
oncall();
}
var dtmfToneGroups = getNumberAsDtmfToneGroups(number);
if (dtmfToneGroups.length > 1) {
call.addEventListener('connected', function dtmfToneGroupPlayer() {
call.removeEventListener('connected', dtmfToneGroupPlayer);
playDtmfToneGroups(dtmfToneGroups, cardIndex);
});
}
call.addEventListener('connected', onconnected);
call.addEventListener('disconnected', function handleDisconnect(evt) {
loadTelephonyMessages(function() {
TelephonyMessages.handleDisconnect(
evt.call.disconnectedReason, number, emergencyOnly
);
});
ondisconnected && ondisconnected();
});
}
var isValid = function t_isValid(sanitizedNumber) {
var validExp = /^(?!,)([0-9#+*,]){1,50}$/;
return validExp.test(sanitizedNumber);
};
var loadConfirm = function t_loadConfirm(cb) {
if (confirmLoaded) {
cb();
return;
}
var confMsg = document.getElementById('confirmation-message');
LazyLoader.load(['/shared/js/confirm.js', confMsg], function() {
confirmLoaded = true;
cb();
});
};
var getInUseSim = function t_getInUseSim() {
var telephony = navigator.mozTelephony;
if (telephony) {
var isInCall = !!telephony.calls.length;
var isInConference = !!telephony.conferenceGroup &&
!!telephony.conferenceGroup.calls.length;
if (isInCall || isInConference) {
return isInCall ?
navigator.mozTelephony.calls[0].serviceId :
navigator.mozTelephony.conferenceGroup.calls[0].serviceId;
}
}
return null;
};
window.TelephonyHelper = TelephonyHelper;
return {
call: call,
getInUseSim: getInUseSim
};
})();