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,34 @@
<element name="call-info-view" extends="form">
<template>
<gaia-header id="call-info-gaia-header" action="back">
<h1><bdi class="ellipsis-dir-fix" id="call-info-title" dir="auto"></bdi></h1>
</gaia-header>
<div class="ci__wrapper">
<div class="ci__calls-wrapper">
<div class="ci__group-info">
<p id="call-info-day" class="ci__grow"></p>
<p id="call-info-direction" class="ci__direction-icon"></p>
</div>
<div id="call-durations">
</div>
</div>
<div class="ci__contact-wrapper">
<div id="contact-detail">
<section id="phone-details-stub"></section>
<section id="email-details-stub"></section>
<ul id="call-info-list-details"></ul>
</div>
<div id="contact-actions">
<button id="call-info-details" class="activity" data-l10n-id="contactDetails" hidden></button>
<button id="call-info-add" class="activity" data-l10n-id="addToExistingContact" hidden></button>
<button id="call-info-create" class="activity" data-l10n-id="createNewContact" hidden></button>
</div>
</div>
</div>
</template>
</element>

View file

@ -0,0 +1,16 @@
<element name="edit-mode" extends="form">
<template>
<section role="region" class="theme-settings">
<gaia-header id="edit-mode-header" action="close">
<h1 id="header-edit-mode-text" data-l10n-id="edit"></h1>
<button id="delete-button" data-l10n-id="delete" disabled>Delete</button>
</gaia-header>
</section>
<menu>
<button id="deselect-all-threads" class="edit-button" disabled data-l10n-id="deselectAll">Deselect all</button>
<button id="select-all-threads" class="edit-button" data-l10n-id="selectAll">Select all</button>
</menu>
</template>
</element>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,402 @@
<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta charset="utf-8">
<meta name="theme-group" content="theme-communications">
<meta name="theme-color" content="var(--header-background)">
<title>Dialer</title>
<!-- Localization -->
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="ar:201601260729, bg:201601260729, bn-BD:201601260729, ca:201601260729, cs:201601260729, da:201601260729, de:201601260729, el:201601260729, en-US:201601260729, es:201601260729, eu:201601260729, fr:201601260729, gl:201601260729, hr:201601260729, hu:201601260729, it:201601260729, ja:201601260729, km:201601260729, ko:201601260729, lt:201601260729, mk:201601260729, ms:201601260729, ne-NP:201601260729, nl:201601260729, pa:201601260729, pl:201601260729, pt-BR:201601260729, ro:201601260729, ru:201601260729, sk:201601260729, sq:201601260729, sr-Cyrl:201601260729, sr-Latn:201601260729, sv-SE:201601260729, tr:201601260729, zh-CN:201601260729, zh-TW:201601260729">
<link rel="localization" href="/locales-obj/dialer.index.{locale}.json">
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="/shared/style/animations.css">
<link rel="stylesheet" type="text/css" href="/shared/style/dialer/dialer.css">
<link rel="stylesheet" type="text/css" href="style/toolbar.css">
<link rel="stylesheet" type="text/css" href="/shared/style/dialer/keypad.css">
<link rel="stylesheet" type="text/css" href="/dialer/style/call_log.css">
<link rel="stylesheet" type="text/css" href="/dialer/style/suggestion.css">
<link rel="stylesheet" type="text/css" href="/shared/style/contacts/contact_in_overlay.css">
<link rel="stylesheet" type="text/css" href="/shared/style/dialer/overlay.css">
<link rel="stylesheet" type="text/css" href="/shared/style/tabs.css">
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css">
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css">
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/bidi-helper.css">
<link rel="stylesheet" type="text/css" href="/shared/style/option_menu.css">
<!-- JS -->
<!-- <script defer="" src="/shared/js/l10n.js"></script> -->
<!-- <script defer="" src="/shared/js/lazy_loader.js"></script> -->
<!-- <script defer="" src="/shared/js/font_size_utils.js"></script> -->
<!-- <script defer="" src="/dialer/js/index.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/contacts.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/dtmf_tone.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/font_size_manager.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/keypad.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/tone_player.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/utils.js"></script> -->
<!-- <script defer="" src="/shared/js/dialer/voicemail.js"></script> -->
<!-- <script defer="" src="/dialer/js/navigation.js"></script> -->
<!-- <script defer="" src="/dialer/js/dialer.js"></script> -->
<!-- <script defer="" src="/dialer/js/call_log.js"></script> -->
<!-- <script defer="" src="/dialer/js/call_log_db.js"></script> -->
<!-- <script defer="" src="/shared/js/accessibility_helper.js"></script> -->
<!-- <script defer="" src="/shared/js/async_storage.js"></script> -->
<!-- <script defer="" src="/shared/js/contact_photo_helper.js"></script> -->
<!-- <script defer="" src="/shared/js/multi_sim_action_button.js"></script> -->
<!-- <script defer="" src="/shared/js/notification_helper.js"></script> -->
<!-- <script defer="" src="/shared/js/simple_phone_matcher.js"></script> --><script src="./gaia_build_defer_index.js" defer="" type="text/javascript"></script>
<!-- Lazy load all the things:
<link rel="stylesheet" type="text/css" href="/shared/style/action_menu.css">
<link rel="stylesheet" type="text/css" href="/shared/style/confirm.css">
<link rel="stylesheet" type="text/css" href="/shared/style/edit_mode.css">
<link rel="stylesheet" type="text/css" href="/shared/style/switches.css">
<link rel="stylesheet" type="text/css" href="/shared/style/lists.css">
<link rel="stylesheet" type="text/css" href="/shared/style/progress_activity.css">
<link rel="stylesheet" type="text/css" href="/shared/style/input_areas.css">
<link rel="stylesheet" type="text/css" href="/dialer/style/mmi.css">
<link rel="stylesheet" type="text/css" href="/shared/style/contacts/contacts_buttons.css">
<link rel="stylesheet" type="text/css" href="/shared/style/contacts.css">
<link rel="stylesheet" type="text/css" href="/dialer/style/buttons.css">
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia_menu/style.css">
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia_sim_picker/style.css">
<script defer src="/shared/elements/gaia_menu/script.js"></script>
<script defer src="/shared/elements/gaia_sim_picker/script.js"></script>
<script defer src="/shared/js/component_utils.js"></script>
<script defer src="/shared/js/mobile_operator.js"></script>
<script defer src="/shared/js/option_menu.js"></script>
<script defer src="/shared/js/settings_listener.js"></script>
<script defer src="/shared/js/confirm.js"></script>
<script defer src="/shared/js/fb/fb_request.js"></script>
<script defer src="/shared/js/fb/fb_data_reader.js"></script>
<script defer src="/shared/js/fb/fb_reader_utils.js"></script>
<script defer src="/shared/js/fb/fb_tel_index.js"></script>
<script defer src="/shared/js/binary_search.js"></script>
<script defer src="/shared/js/sticky_header.js"></script>
<script defer src="/shared/js/sim_settings_helper.js"></script>
<script defer src="/shared/js/dialer/telephony_messages.js"></script>
<script defer src="/shared/js/contacts/contacts_buttons.js"></script>
<script defer src="/shared/js/custom_dialog.js"></script>
<script defer src="/dialer/js/contact_data_manager.js"></script>
<script defer src="/dialer/js/suggestion_bar.js"></script>
<script defer src="/dialer/js/telephony_helper.js"></script>
<script defer src="/dialer/js/mmi.js"></script>
<script defer src="/dialer/js/mmi_ui.js"></script>
<script defer src="/shared/js/icc_helper.js"></script>
<script defer src="/shared/elements/config.js"></script>
<script defer src="/shared/elements/gaia-header/dist/gaia-header.js"></script>
<script defer src="/shared/js/text_normalizer.js"></script>
<script defer src="/shared/js/contacts/utilities/templates.js"></script>
<script defer src="/shared/js/contacts/sms_integration.js"></script>
-->
<meta content="2.5" name="appVersion"></head>
<body role="application" class="skin-comms theme-communications">
<section id="views" role="tablist" class="bb-tablist">
<li role="presentation" class="view hidden" id="call-log-view">
<a aria-label="Recent calls" id="option-recents" role="tab" aria-controls="recents-panel" class="icon icon-recents" data-l10n-id="recents" aria-selected="false" data-destination="recents"></a>
<div id="recents-panel" role="tabpanel" class="bb-tabpanel" aria-labelledby="option-recents">
<section role="region">
<gaia-header>
<h1 data-l10n-id="callLog">Call log</h1>
<button aria-label="Edit call log" id="call-log-icon-edit" data-icon="edit" disabled="" data-l10n-id="callLogEditButton"></button>
</gaia-header>
<nav id="call-log-filter" class="recents-filter-container">
<ul role="tablist" class="bb-tablist" data-type="filter" aria-controls="call-log-container">
<li id="all-filter" role="presentation"><a aria-label="All" id="all-filter-tab" role="tab" aria-selected="true" data-action="all" data-l10n-id="all">All</a></li>
<li id="missed-filter" role="presentation"><a aria-label="Missed" id="missed-filter-tab" role="tab" aria-selected="false" data-action="missed" data-l10n-id="missed">Missed</a></li>
</ul>
</nav>
<div class="view-body">
<article id="call-log-container" role="tabpanel" class="view-body-inner active" data-type="list">
<div id="no-result-container" hidden="">
<div id="no-result-message">
<p id="no-result-msg1" data-l10n-id="no-logs-msg-1">No calls recorded</p>
<p id="no-result-msg2" data-l10n-id="no-logs-msg-4">Start a conversation now</p>
<p id="no-result-msg3" data-l10n-id="no-logs-msg-3" hidden="">No missed calls</p>
</div>
</div>
</article>
<div id="sticky"></div>
</div>
</section>
<section id="call-info-view" role="region" hidden=""><!--
<gaia-header id="call-info-gaia-header" action="back">
<h1><bdi class="ellipsis-dir-fix" id="call-info-title" dir="auto"></bdi></h1>
</gaia-header>
<div class="ci__wrapper">
<div class="ci__calls-wrapper">
<div class="ci__group-info">
<p id="call-info-day" class="ci__grow"></p>
<p id="call-info-direction" class="ci__direction-icon"></p>
</div>
<div id="call-durations">
</div>
</div>
<div class="ci__contact-wrapper">
<div id="contact-detail">
<section id="phone-details-stub"></section>
<section id="email-details-stub"></section>
<ul id="call-info-list-details"></ul>
</div>
<div id="contact-actions">
<button id="call-info-details" class="activity" data-l10n-id="contactDetails" hidden=""></button>
<button id="call-info-add" class="activity" data-l10n-id="addToExistingContact" hidden=""></button>
<button id="call-info-create" class="activity" data-l10n-id="createNewContact" hidden=""></button>
</div>
</div>
</div>
--></section>
</div>
</li>
<li role="presentation" class="view" id="contacts-view">
<a aria-label="Contacts" id="option-contacts" role="tab" aria-controls="iframe-contacts-container" class="icon icon-contacts" data-l10n-id="contacts" aria-selected="false" data-destination="contacts"></a>
<div id="iframe-contacts-container" role="tabpanel" class="bb-tabpanel" aria-labelledby="option-contacts">
</div>
</li>
<li role="presentation" id="keyboard-view">
<a aria-label="Keypad" id="option-keypad" role="tab" aria-controls="keypad-panel" class="icon icon-keypad" data-l10n-id="keypad" aria-selected="true" data-destination="keypad"></a>
<div id="keypad-panel" role="tabpanel" class="bb-tabpanel" aria-labelledby="option-keypad">
<div id="phone-number-view-container" dir="ltr">
<div class="phone-number-view-wrapper-input">
<input aria-label="Phone number" id="phone-number-view" class="phone-number-font font-light textfield" readonly="readonly" data-l10n-id="phoneNumberInput" type="text">
</div>
<button aria-label="Delete" role="key" id="keypad-delete" class="reset-button" data-value="delete" data-l10n-id="keypadDelete">
</button>
</div>
<section id="suggestion-bar" class="suggestion-bar hide">
<div role="button" class="js-suggestion-item contact-item bb-button"><!--
<div class="js-name ci__name ellipsis" dir="auto"></div>
<div class="ci__details">
<span class="js-tel-type ci__tel-type ellipsis" dir="auto"></span>
<span class="js-separator"></span>
<span class="js-tel ellipsis" dir="ltr"></span>
</div>
--></div>
<div role="button" id="suggestion-count" class="suggestion-count more bb-button"></div>
</section>
<button role="button" id="contact-in-overlay-template" class="js-suggestion-item contact-item bb-button" hidden=""><!--
<div class="js-name ci__name ellipsis" dir="auto"></div>
<div class="ci__details">
<span class="js-tel-type ci__tel-type ellipsis" dir="auto"></span>
<span class="js-separator"></span>
<span class="js-tel ellipsis" dir="ltr"></span>
</div>
--></button>
<article id="keyboard-container" dir="ltr">
<section id="keypad">
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="1">
<span>1</span>
<em aria-label="Voicemail" class="keypad-subicon voicemail" data-l10n-id="voiceMailKey"></em>
</div>
<div class="keypad-key" role="key" data-value="2">
<span>2</span>
<em>ABC</em>
</div>
<div class="keypad-key" role="key" data-value="3">
<span>3</span>
<em>DEF</em>
</div>
</div>
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="4">
<span>4</span>
<em>GHI</em>
</div>
<div class="keypad-key" role="key" data-value="5">
<span>5</span>
<em>JKL</em>
</div>
<div class="keypad-key" role="key" data-value="6">
<span>6</span>
<em>MNO</em>
</div>
</div>
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="7">
<span>7</span>
<em>PQRS</em>
</div>
<div class="keypad-key" role="key" data-value="8">
<span>8</span>
<em>TUV</em>
</div>
<div class="keypad-key" role="key" data-value="9">
<span>9</span>
<em>WXYZ</em>
</div>
</div>
<div class="keypad-row">
<div aria-label="Asterisk" class="keypad-key" role="key" data-value="*" data-l10n-id="asterisk">
<span class="asterisk">*</span>
<em>’</em>
</div>
<div class="keypad-key" role="key" data-value="0">
<span>0</span>
<em class="font-size-plus">+</em>
</div>
<div aria-label="Hash sign" class="keypad-key" role="key" data-type="dial" data-value="#" data-l10n-id="hashSign">
<div class="sharp">#</div>
</div>
</div>
</section>
<section id="keypad-callbar">
<button aria-label="Add contact" id="keypad-callbar-add-contact" data-value="add-contact" class="reset-button keypad-callbar-button icon-add-contact disable-when-dialog-animates" data-l10n-id="dialerAddContact" disabled=""></button>
<button aria-label="Make call" id="keypad-callbar-call-action" class="reset-button" data-value="make-call" data-l10n-id="makeCall">
<div class="js-sim-indication"></div>
</button>
</section>
</article>
</div>
</li>
</section>
<form aria-label="Edit call log dialog" id="edit-mode" role="dialog" data-l10n-id="callLogEditDialog" data-type="edit" hidden=""><!--
<section role="region" class="theme-settings">
<gaia-header id="edit-mode-header" action="close">
<h1 id="header-edit-mode-text" data-l10n-id="edit"></h1>
<button id="delete-button" data-l10n-id="delete" disabled="">Delete</button>
</gaia-header>
</section>
<menu>
<button id="deselect-all-threads" class="edit-button" disabled="" data-l10n-id="deselectAll">Deselect all</button>
<button id="select-all-threads" class="edit-button" data-l10n-id="selectAll">Select all</button>
</menu>
--></form>
<form aria-label="Add Contact Dialog" id="add-contact-action-menu" role="dialog" data-l10n-id="dialerAddContactDialog" data-type="action" hidden=""><!--undefined--></form>
<gaia-sim-picker id="sim-picker" tabindex="-1"></gaia-sim-picker>
<form id="confirmation-message" class="hide no-opacity" role="dialog" data-type="confirm"><!--
<section>
<h1>Confirmation title</h1>
<p>Confirmation message</p>
</section>
<menu>
<button>Action 1</button>
<button>Action 2</button>
</menu>
--></form>
<form id="contact-list-overlay" role="dialog" data-type="action" class="overlay" aria-hidden="true"><!--
<header></header>
<menu id="contact-list">
<button id="contact-list-overlay-cancel" data-l10n-id="cancel">Cancel</button>
</menu>
--></form>
<section id="phone-details" hidden=""><!--
<li data-phone="" id="phone-details-template-#i#" data-template="" role="region" aria-label="Phone" data-l10n-id="phoneDetail">
<h2>
<span data-l10n-id="#type_l10n_id#">#type#</span>
<span class="carrier-wrapper" hidden="">
<span data-l10n-id="separator"></span>
<span class="carrier">#carrier#</span>
</span>
</h2>
<div class="item">
<div class="item-body-exp">
<div class="button activity call-action icon icon-call" id="call-or-pick-#i#">
<b aria-label="Call" data-l10n-id="makeCall"><bdi class="ellipsis-dir-fix">#value#</bdi></b>
<span class="js-sim-indication"></span>
</div>
</div>
<div class="item-media pull-right">
<button id="send-sms-button-#i#" class="activity send-sms icon icon-message" data-l10n-id="send-sms-button" aria-label="SMS"></button>
</div>
</div>
</li>
--></section>
<section id="email-details" hidden=""><!--
<li data-mail="" id="email-details-template-#i#" data-template="" role="region" aria-label="Email" data-l10n-id="emailDetail">
<h2 data-l10n-id="#type_l10n_id#">#type#</h2>
<div class="item">
<div class="item-body-exp">
<button id="email-or-pick-#i#" class="activity icon icon-email">
<b aria-label="Email" data-l10n-id="composeEmail" dir="ltr">#value#</b>
</button>
</div>
<div class="item-media pull-right">
<button id="send-sms-to-email-button-#i#" class="activity send-sms icon icon-message" data-l10n-id="send-sms-button" aria-label="SMS"></button>
</div>
</div>
</li>
--></section>
<article id="mmi-screen" role="region" hidden="">
<section role="region">
<gaia-header id="mmi-header" action="close">
<h1 id="header-title">USSD</h1>
<button id="send" data-l10n-id="send" disabled="">Send</button>
</gaia-header>
</section>
<section id="mmi-container" role="region">
<div id="message">
</div>
</section>
<form id="response" role="region">
<p>
<input id="response-text" data-l10n-id="type-your-response" placeholder="Type your response…" required="" disabled="" type="text">
<button id="response-text-reset" type="reset" data-l10n-id="clear-all-text">Clear all text</button>
</p>
</form>
</article>
<form data-type="confirm" role="dialog" id="loading-overlay" class="hide">
<section>
<h1 data-l10n-id="sending">Sending…</h1>
<p role="status">
<progress class="pack-activity" value="0" max="100"></progress>
</p>
</section>
<menu>
<button class="full" id="cancel" data-l10n-id="cancel">Cancel</button>
</menu>
</form>
</body>
</html>

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
};
})();

View file

@ -0,0 +1,332 @@
/**
* FIXME/bug 1060293: This is entirely copied and pasted from
* shared/style/buttons.css because the selectors in there are overly broad and
* had to be made more specific. We should remove this file and use that one
* instead.
*/
/* ----------------------------------
* Buttons
* ---------------------------------- */
.activity::-moz-focus-inner {
border: none;
outline: none;
}
.activity {
font-family: sans-serif;
font-style: italic;
width: 100%;
height: 4rem;
margin: 0 0 1rem;
padding: 0 1.2rem;
box-sizing: border-box;
/*display: inline-block;*/
vertical-align: middle;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
background: #d8d8d8;
border: none;
border-radius: 2rem;
font-weight: normal;
font-size: 1.6rem;
line-height: 4rem;
color: #333;
text-align: center;
text-decoration: none;
outline: none;
}
/* Recommend */
.activity.recommend {
background-color: #00caf2;
color: #fff;
}
/* Danger */
.danger.activity {
background-color: #e51e1e;
color: #fff;
}
/* Pressed */
.activity:active {
background-color: #b2f2ff;
color: #fff;
}
/* Icons */
.activity[data-icon] {
position: relative;
}
.activity[data-icon]:before {
position: absolute;
top: 50%;
margin-top: -1.5rem;
offset-inline-end: 1.5rem;
line-height: 3rem;
}
.activity[data-icon]:active:before {
color: #fff;
}
/* Pressed with dark skin */
.skin-dark .activity:active {
background-color: #00aacc;
}
/* Disabled */
.activity[aria-disabled="true"] {
background-color: rgba(0,0,0,0.05);
color: #c7c7c7;
pointer-events: none;
}
.activity[aria-disabled="true"].recommend {
background-color: #97cbd5;
color: rgba(255,255,255,0.5);
}
.activity[aria-disabled="true"].danger {
background-color: #eb7474;
color: rgba(255,255,255,0.5);
}
/* Disabled with dark background */
.skin-dark .activity[aria-disabled="true"] {
background-color: #565656;
color: rgba(255,255,255,0.4);
}
.skin-dark .activity[aria-disabled="true"].recommend {
background-color: #006579;
}
.skin-dark .activity[aria-disabled="true"].danger {
background-color: #730f0f;
}
.activity[aria-disabled="true"]::-moz-selection {
-moz-user-select: none;
}
/* ----------------------------------
* Buttons inside lists
* ---------------------------------- */
li .activity {
position: relative;
text-align: start;
border: 0.1rem solid #c7c7c7;
background: rgba(0,0,0,0.05);
margin: 0 0 1rem;
overflow: hidden;
border-radius: 0;
/*display: flex;*/
height: auto;
min-height: 4rem;
text-overflow: initial;
white-space: normal;
line-height: 2rem;
padding: 1rem 1.2rem;
}
/* Press */
li .activity:active:after {
opacity: 0;
}
/* Disabled */
li .activity[aria-disabled="true"] {
opacity: 0.5;
}
/* Icons */
li .activity.icon {
padding-inline-end: 4rem;
}
li .activity.icon:before {
content: "";
width: 3rem;
height: 3rem;
position: absolute;
top: 50%;
offset-inline-end: 0.5rem;
margin-top: -1.5rem;
background: transparent no-repeat center center / 100% auto;
pointer-events: none;
}
li .activity.icon-view:active {
background-color: #b2f2ff;
}
li .activity.icon-view:before {
background-size: 3rem;
offset-inline-end: 0;
}
li .activity[data-icon]:before {
offset-inline-end: 1rem;
}
li .activity[data-icon="expand"]:before {
color: #00aac5;
top: unset;
bottom: 0;
}
li .activity[data-icon="expand"][aria-disabled="true"]:before {
color: #A9A9A9;
}
li .activity[data-icon="forward"]:before {
color: #686f72;
}
/* Getting more space for text, as icon is shorter */
li .activity.icon-dialog {
padding-inline-end: 3rem;
}
li .activity.icon-dialog:before {
width: 0;
height: 0;
top: auto;
offset-inline-end: 0.8rem;
bottom: 0.5rem;
border-top: 0.7rem solid transparent;
border-bottom: 0.7rem solid transparent;
border-inline-start: 0.7rem solid #00aac5;
transform: rotate(45deg);
z-index: 1;
}
li .activity[aria-disabled="true"].icon-dialog:before {
border-inline-start: 0.7rem solid #a9a9a9;
}
/* ----------------------------------
* Theme: Dark
* ---------------------------------- */
.skin-dark li .activity {
background-color: #454545;
border: none;
color: #fff;
font-weight: normal;
}
.skin-dark li .activity:active {
background-color: #00aacc;
}
.skin-dark li .activity.icon-view:active {
background-color: #00aacc;
}
.skin-dark li .activity.icon-dialog:active:before {
border-inline-start: 0.7rem solid #b2f2ff;
}
.skin-dark li .activity[aria-disabled="true"] {
background-color: #454545;
opacity: 0.5;
}
/* Inputs inside of .button */
body[role="application"] .activity input {
border: 0;
background: none;
}
/* select */
select {
width: 100%;
box-sizing: border-box;
font-family: sans-serif;
font-style: italic;
font-size: 1.6rem;
color: #333;
margin: 0 0 1rem;
padding: 0;
padding-inline-start: 1.1rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
height: 4rem;
border: 0.1rem solid #c7c7c7;
background: rgba(0,0,0,0.05);
}
.skin-dark select {
background-color: rgba(87, 87, 87, 0.8);
border: none;
color: #fff;
}
/* remove dotted outline */
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #333;
}
.skin-dark select:-moz-focusring {
text-shadow: 0 0 0 #fff;
}
/* Hides dropdown arrow until bug #649849 is fixed */
.activity.icon select,
.activity[data-icon] select {
position: absolute;
top: -0.6rem;
offset-inline-start: 0;
width: calc(100% + 3rem);
height: 100%;
margin: 0;
padding: 0;
padding-inline-end: 5rem;
text-indent: 0;
font-weight: 400;
font-size: 1.7rem;
/*
The select element uses the text color to determine the color of the outline.
We put a text shadow with needed offset and no blur, so that replaces the text.
*/
color: rgba(0,0,0,0);
border: none;
background: none;
}
html[dir="ltr"] .activity.icon select,
html[dir="ltr"] .activity[data-icon] select {
text-shadow: 1rem 0.5rem 0 #333;
}
html[dir="rtl"] .activity.icon select,
html[dir="rtl"] .activity[data-icon] select {
text-shadow: -1rem 0.5rem 0 #333;
}
.activity.icon select option,
.activity[data-icon] select option {
color: #000;
border: 0;
padding: 0.6rem 1.3rem;
text-shadow: 0 0 0 rgba(0,0,0,0);
}
.activity.icon-dialog {
overflow: hidden;
}
html[dir="ltr"] .skin-dark .activity.icon select,
html[dir="ltr"] .skin-dark .activity[data-icon] select {
text-shadow: 1rem 0.5rem 0 #fff;
}
html[dir="rtl"] .skin-dark .activity.icon select,
html[dir="rtl"] .skin-dark .activity[data-icon] select {
text-shadow: -1rem 0.5rem 0 #fff;
}

View file

@ -0,0 +1,78 @@
#call-info-view {
position: absolute;
top: 0;
background: white;
width: 100vw;
height: calc(100vh - 4.5rem);
color: #333;
}
#call-info-view p {
margin: 0;
}
.ci__wrapper {
padding: 1.4rem 1.5rem 0;
}
.ci__calls-wrapper {
padding: 0 0.9rem 1.4rem;
padding-inline-start: 1.5rem;
margin-bottom: 1.6rem;
border-bottom: 1px solid #e7e7e7;
}
.ci__group-info {
display: flex;
font-size: 1.6rem;
font-weight: 500;
}
.ci__grow {
flex-grow: 1;
}
.call-duration {
display: flex;
margin-top: 1rem;
font-size: 1.4rem;
}
.cd__duration {
text-align: end;
}
.ci__direction-icon {
width: 2rem;
height: 2rem;
background-size: cover;
}
.icon-outgoing {
background-image: url(images/call_info/callinfo_ic_outgoing.png);
}
.icon-incoming {
background-image: url(images/call_info/callinfo_ic_incoming.png);
}
.icon-missed {
background-image: url(images/call_info/callinfo_ic_missed.png);
}
/**
* As <gaia-header> does not support <h1 dir="auto"> children, a <bdi> element
* must be nested and specific ellipsis rules must be applied.
*
* Horizontal padding must be applied in order to:
* - not overlap the [action="back"] button
* - keep the title centered
* (the magic 50px value corresponds to the size of the "back" button).
*/
#call-info-title {
display: block;
text-overflow: ellipsis;
overflow: hidden;
padding-left: 50px;
padding-right: 50px;
}

View file

@ -0,0 +1,298 @@
/* Overriding BB styles for headers */
section[role="region"] > header:first-child .icon {
pointer-events: auto;
}
/* Overriding BB styles for edit mode */
form[role="dialog"][data-type="edit"] gaia-header {
pointer-events: auto;
}
#call-log-container[data-type="list"] section {
background: #fff;
}
#sticky {
position: absolute;
/* The sticky element is positioned at the beginning of the list. */
top: 0;
/* The width is not 100% so the scrollbar is visible. */
width: calc(100% - 1.5rem);
/* The height needs to be kept in sync with the header height. */
height: 3.1rem;
/* Those style will affect the bagroundImage: -moz-element. */
background-repeat: no-repeat;
}
html[dir="ltr"] #sticky { background-position: left 1.5rem top 0; }
html[dir="rtl"] #sticky { background-position: right 1.5rem top 0; }
#call-log-container[data-type="list"] header {
background: #fff;
}
/* Override Building Block styles for lists */
#call-log-container[data-type="list"] {
padding: 0;
}
#call-log-container[data-type="list"] header {
margin: 0 1.5rem;
}
#call-log-container[data-type="list"] ol {
padding: 0 1.5rem;
/* Bug 967880 - [Call Log] Background doesn't scroll with the logs causing
* expensive over-invalidation.
*/
background-color: white;
}
#call-log-container[data-type="list"] li {
height: 6rem;
box-sizing: border-box;
}
/* For preventig active status with withheld number */
#call-log-container[data-type="list"] li[data-phone-number=""]:after,
#call-log-container[data-type="list"] li[data-phone-number=""]:active:after {
background-color: transparent;
}
#call-log-container[data-type="list"] li a p {
line-height: 2.4rem;
}
#call-log-container[data-type="list"] li a p:nth-last-of-type(3) {
padding: 0;
}
button[disabled],
input[type="text"][disabled],
a.bb-button[disabled],
a.bb-button[disabled] * {
pointer-events: none;
}
input[type="text"][disabled],
a.bb-button[disabled] {
opacity: 0.3;
}
li, ol, ul {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
ol, ul {
list-style: none;
}
.hide {
display: none;
}
.view-body {
color: black;
height: calc(100% - 9.1rem);
position: absolute;
overflow: hidden;
width: 100%;
}
.view-body-inner {
height: 100%;
overflow: auto;
}
.view-body-inner ol {
overflow: hidden;
}
.icon-container {
height: 100%;
width: 5rem;
}
html[dir="ltr"] .icon-container { float: left; }
html[dir="rtl"] .icon-container { float: right; }
.icon-incoming,
.icon-outgoing,
.icon-missed {
background-size: 3rem;
}
/* .first-sim and .second-sim classes should only affect the style
* if the .dual-sim class is set on the container */
[data-type="list"] aside.icon.icon-incoming {
background-image: url('images/CallLog_30x30_incoming.png');
}
[data-type="list"].dual-sim aside.icon.icon-incoming.first-sim {
background-image: url('images/CallLog_30x30_incoming-first-sim.png');
}
[data-type="list"].dual-sim aside.icon.icon-incoming.second-sim {
background-image: url('images/CallLog_30x30_incoming-second-sim.png');
}
[data-type="list"] aside.icon.icon-outgoing {
background-image: url('images/CallLog_30x30_outgoing.png');
}
[data-type="list"].dual-sim aside.icon.icon-outgoing.first-sim {
background-image: url('images/CallLog_30x30_outgoing-first-sim.png');
}
[data-type="list"].dual-sim aside.icon.icon-outgoing.second-sim {
background-image: url('images/CallLog_30x30_outgoing-second-sim.png');
}
[data-type="list"] aside.icon.icon-missed {
background-image: url('images/CallLog_30x30_missed.png');
}
[data-type="list"].dual-sim aside.icon.icon-missed.first-sim {
background-image: url('images/CallLog_30x30_missed-first-sim.png');
}
[data-type="list"].dual-sim aside.icon.icon-missed.second-sim {
background-image: url('images/CallLog_30x30_missed-second-sim.png');
}
.log-item * {
pointer-events: none;
}
.log-item .primary-info {
display: flex;
}
/* Decrease padding defined by BB styles to compensate increased line-height */
.log-item .primary-info:first-of-type {
padding-top: 0.8rem;
}
.log-item a > p {
width: calc(100% - 4rem);
}
.log-item.missed-call .primary-info {
color: #bc1515;
}
.log-item .primary-info-main {
min-width: 0;
}
.log-item .primary-info-main bdi {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.log-item .retry-count {
margin-inline-start: 0.5rem;
}
.log-item .additional-info {
display: inline-flex;
justify-content: space-between;
margin-top: -0.4rem;
}
.log-item .type-carrier {
overflow: hidden;
text-overflow: ellipsis;
}
.log-item .call-time {
margin-inline-start: 0.5rem;
}
.log-item .pack-checkbox {
visibility: hidden;
}
/* Edit mode state */
.recents-edit form[role="dialog"][data-type="edit"] header,
.recents-edit form[role="dialog"][data-type="edit"] > menu {
transform: translateY(0);
}
html[dir="ltr"] .recents-edit .pack-checkbox.call-log-selection {
transform: translateX(4rem);
}
html[dir="rtl"] .recents-edit .pack-checkbox.call-log-selection {
transform: translateX(-4rem);
}
.recents-edit .log-item a {
visibility: hidden;
}
html[dir="ltr"] .recents-edit .log-item a { transform: translateX(3rem); }
html[dir="rtl"] .recents-edit .log-item a { transform: translateX(-3rem); }
.recents-edit .log-item a > aside,
.recents-edit .log-item a > p {
visibility: visible;
}
.recents-edit .log-item a > p {
width: calc(100% - 6.5rem);
}
.recents-edit .log-item .pack-checkbox {
pointer-events: auto;
visibility: visible;
}
/*
No result container
*/
#no-result-container {
background: url('images/ComLog_200x200_clock.png') fixed no-repeat center / 20rem;
height: 100%;
margin: 0 1.5rem;
}
#no-result-message {
position: relative;
top: 11.5rem;
border-top: .1rem solid rgba(96,96,96,.5);
padding: 1rem 1.5rem;
}
#no-result-message > p {
text-align: start;
line-height: 3rem;
font-size: 2.2rem;
font-weight: 300;
color: #606060;
margin: 0;
}
.recents-edit #edit-mode {
display: block;
}
.recents-edit .recents-filter-container {
display: none;
}
.recents-edit .view-body {
height: calc(100% - 7.6rem);
}
.filter .log-item:not(.missed-call) {
display: none;
}
.filter .groupFiltered {
display: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

View file

@ -0,0 +1,49 @@
#mmi-screen {
position: absolute;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
background: white;
color: black;
z-index: 2;
}
#mmi-screen[hidden] {
display: none;
}
#mmi-container {
flex: 1;
padding: 0 3rem;
overflow: auto;
}
#message {
font-size: 2rem;
padding: 2rem 0;
white-space: pre-line;
word-wrap: break-word;
}
#response {
display: none;
padding: 1.5rem 1.5rem 0.5rem;
border-top: 0.1rem solid #E7E7E7;
}
.responseForm #response {
display: block;
}
#response-text[disabled] {
opacity: 0.8;
}
/*
Loading screen
*/
.hide {
display: none;
}

View file

@ -0,0 +1,33 @@
.suggestion-bar {
display: flex;
margin-top: -4.5rem;
height: 4.5rem;
font-size: 1.7rem;
position: absolute;
width: 100%;
color: #033B37;
}
.suggestion-count {
width: 5.5rem;
box-sizing: border-box;
border-inline-start: 1px solid rgba(255,255,255,0.2);
padding-inline-end: 2rem;
padding-inline-start: 1rem;
line-height: 4.5rem;
text-align: center;
}
.suggestion-count.more {
background-image: url(/dialer/style/images/suggestion/actionicon_dialer_more.png);
background-repeat: no-repeat;
background-size: 3rem;
}
html[dir="ltr"] .suggestion-count.more { background-position: right 0 bottom 0.8rem; }
html[dir="rtl"] .suggestion-count.more { background-position: left 0 bottom 0.8rem; }
.suggestion-bar .contact-item:active,
.suggestion-bar .suggestion-count:active {
background-color: #94eee9;
transition: background-color 0.2s ease;
}

View file

@ -0,0 +1,83 @@
/* Structure for toolbar*/
.icon-recents {
background-image: url("images/recents.png");
}
.icon-contacts {
background-image: url("images/contacts.png");
}
.icon-keypad {
background-image: url("images/dialpad.png");
}
/* Making lazy loading look nice */
#contacts-view .bb-tabpanel,
#recents-view.hidden .bb-tabpanel {
background-color: white;
}
#contacts-view .bb-tabpanel:before,
#recents-view.hidden .bb-tabpanel:before {
position: absolute;
left: 0;
right: 0;
height: 5rem;
background-color: var(--header-background);
content: "";
}
#iframe-contacts-container {
overflow: hidden;
}
#iframe-contacts {
position: absolute;
}
#recents-view.hidden .bb-tabpanel section {
display: none;
}
.no-opacity {
opacity: 0 !important;
}
/* For showing/hiding navbar */
#views:before {
transform: translateX(0);
}
#views.hide-toolbar:before {
transform: translateX(100%);
}
#views > li > .icon {
z-index: 2;
}
#views.hide-toolbar > li {
background: none;
pointer-events: none;
}
#views.hide-toolbar > li:before,
#views.hide-toolbar > li > .icon {
transform: translateY(100%);
visibility: hidden;
}
#views.hide-toolbar #iframe-contacts-container {
height: 100%;
}
#views .bb-tabpanel {
pointer-events: all;
}
#contacts-view .bb-tabpanel {
height: calc(100% - 4.5rem);
z-index: 1;
}