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.
547
assets/webapps/sms.gaiamobile.org/views/conversation/index.html
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
<!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>Messages</title>
|
||||
<!--App Style root.css-->
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/root.css">
|
||||
<!-- Stable Building blocks -->
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/animations.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/action_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/option_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/buttons.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/input_areas.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/status.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/tabs.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css">
|
||||
<!-- App styles -->
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/sms.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/panel.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/edit-mode.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/notification.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/composer.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/conversation/style/message.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/conversation/style/report_view.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/conversation/style/attachment.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/conversation/style/conversation.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/conversation/style/edit-mode.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/new_message/style/new_message.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/new_message/style/recipients.css">
|
||||
<!-- 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/views.conversation.index.{locale}.json">
|
||||
|
||||
|
||||
<!-- <script defer="" src="/mocks/navigator_moz_icc_manager.js"></script> -->
|
||||
<!-- Firefox Desktop shims -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Web Components -->
|
||||
<!-- <script defer="" src="/shared/elements/config.js"></script> -->
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css">
|
||||
<script async="" src="/shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
|
||||
<!--
|
||||
<script defer src="/shared/js/component_utils.js"></script>
|
||||
<script defer src="/shared/elements/gaia_menu/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia_sim_picker/script.js"></script>
|
||||
<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">
|
||||
-->
|
||||
<!-- Shared code -->
|
||||
<!-- <script defer="" src="/shared/js/intl/l20n.js"></script> -->
|
||||
<!-- Should be removed while gecko support navigator.mozHour12 API -->
|
||||
<!-- <script defer="" src="/shared/js/date_time_helper.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/template.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/async_storage.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/lazy_loader.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/contact_photo_helper.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/event_dispatcher.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/utils.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/navigation.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/inter_instance_event_dispatcher.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/time_headers.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/contacts.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/drafts.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/threads.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/settings.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/message_manager.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/app.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/multi_sim_action_button.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/task_runner.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/wbmp.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/smil.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/shared_components.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/link_helper.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/image_utils.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/attachment.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/attachment_renderer.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/subject_composer.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/compose.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/gesture_detector.js"></script> -->
|
||||
<!-- <script defer="" src="/lib/bridge/client.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/activity/activity_client.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/recipients.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/conversation.js"></script> -->
|
||||
<!-- <script defer="" src="/views/conversation/js/startup.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/bootstrap.js"></script> --><script src="./gaia_build_defer_index.js" defer="" type="text/javascript"></script>
|
||||
<!-- Lazy load scripts
|
||||
<script defer src="/shared/js/settings_url.js"></script>
|
||||
<script defer src="/shared/js/mime_mapper.js"></script>
|
||||
<script defer src="/shared/js/mobile_operator.js"></script>
|
||||
<script defer src="/shared/js/settings_listener.js"></script>
|
||||
<script defer src="/shared/js/option_menu.js"></script>
|
||||
<script defer src="/views/shared/js/errors.js"></script>
|
||||
<script defer src="/views/shared/js/shared_components.js"></script>
|
||||
<script defer src="/views/shared/js/dialog.js"></script>
|
||||
<script defer src="/views/shared/js/error_dialog.js"></script>
|
||||
<script defer src="/views/shared/js/waiting_screen.js"></script>
|
||||
<script defer src="/views/shared/js/activity_picker.js"></script>
|
||||
<script defer src="/views/conversation/js/link_action_handler.js"></script>
|
||||
<script defer src="/views/shared/js/contact_renderer.js"></script>
|
||||
<script defer src="/views/shared/js/localization_helper.js"></script>
|
||||
<script defer src="/views/conversation/js/information.js"></script>
|
||||
<script defer src="/services/js/messaging/messaging_client.js"></script>
|
||||
<script defer src="/services/js/moz_mobile_connections/moz_mobile_connections_client.js"></script>
|
||||
-->
|
||||
<meta content="2.5" name="appVersion"></head>
|
||||
|
||||
<body role="application" class="skin-comms theme-communications">
|
||||
<iframe src="/views/shared/shim_host.html" class="shim-host" hidden=""></iframe>
|
||||
<article id="main-wrapper" class="wrapper">
|
||||
<section class="panel panel-ConversationView editable-select-mode" role="region" aria-hidden="true">
|
||||
<gaia-header id="messages-header" class="view-header regular-header" action="back" no-font-fit="">
|
||||
<h1 id="messages-header-text"></h1>
|
||||
<button aria-label="Call" id="messages-call-number-button" data-icon="call" class="hide" data-l10n-id="callButton"></button>
|
||||
<button aria-label="Actions" id="messages-options-button" data-icon="more" data-l10n-id="actionsButton"></button>
|
||||
</gaia-header>
|
||||
<div id="messages-subheader" class="subheader">
|
||||
<section id="messages-to-field">
|
||||
<section data-l10n-id="to" id="to-label">To:</section>
|
||||
<!-- When overflow occurs in the recipients list, scrolling is
|
||||
commonly triggered automatically through focus and keyboard events.
|
||||
In these cases, white space described by CSS `padding-bottom` will
|
||||
not be automatically scrolled into view (because the element will
|
||||
only scroll far enough to bring the cursor into view). A container
|
||||
element is necessary to consistently render this vertical space
|
||||
(instead realized with CSS `margin-bottom`). -->
|
||||
<div id="messages-recipients-list-container" class="bb-editable" role="group" aria-labelledby="to-label">
|
||||
<section id="messages-recipients-list" class="singleline">
|
||||
</section>
|
||||
</div>
|
||||
<menu type="toolbar">
|
||||
<a id="messages-contact-pick-button">
|
||||
<span class="icon icon-user"></span>
|
||||
</a>
|
||||
</menu>
|
||||
</section>
|
||||
<div id="contact-carrier" class="carrier-wrapper"></div>
|
||||
<!-- Notifications in this container will be rendered on top of the
|
||||
Compose field. These notifications should be hidden automatically so
|
||||
they do not prevent the user from inputting text. -->
|
||||
<div class="overlay">
|
||||
<section id="messages-subject-max-length-notice" role="notification" class="hide">
|
||||
<p data-l10n-id="messages-max-subject-length-text">Maximum length of subject reached.</p>
|
||||
</section>
|
||||
<section id="messages-max-length-notice" role="notification" class="hide">
|
||||
<p></p>
|
||||
</section>
|
||||
<section id="messages-sms-counter-notice" role="notification" class="hide">
|
||||
<p></p>
|
||||
</section>
|
||||
<section id="messages-convert-notice" role="notification" class="hide">
|
||||
<p></p>
|
||||
</section>
|
||||
<section id="messages-resize-notice" role="notification" class="hide">
|
||||
<p data-l10n-id="resize-image">Resizing the attached image</p>
|
||||
</section>
|
||||
<section id="messages-new-message-notice" role="notification" class="hide">
|
||||
<div class="text-container">
|
||||
<div data-l10n-id="new-message-notice-text">New message from</div>
|
||||
<div dir="auto" class="contact">Contact</div>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<button class="notice-button" data-l10n-id="new-message-notice-a-text">Read it</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<article id="composer-container" class="view-body">
|
||||
<div class="flex-composer">
|
||||
<div id="messages-container" data-type="list" class="article-list"></div>
|
||||
<form id="messages-compose-form" role="search" class="bottom" data-message-type="sms">
|
||||
<section class="mms-label-line">
|
||||
<section class="subject-composer js-subject-composer hide">
|
||||
<div class="subject-composer-input" role="textbox" aria-multiline="false" contenteditable="true">
|
||||
</div>
|
||||
<div class="subject-composer-placeholder" data-l10n-id="messagesSubjectInput_placeholder">Subject</div>
|
||||
</section>
|
||||
<span class="mms-label" data-l10n-id="mms-label">MMS</span>
|
||||
</section>
|
||||
<section class="content-composer">
|
||||
<div class="messages-attach-container">
|
||||
<button id="messages-attach-button" aria-label="Add attachment" data-l10n-id="attach-button" type="button"></button>
|
||||
</div>
|
||||
<div class="composer-message-input-container">
|
||||
<div title="Message" id="messages-input" name="message" x-inputmode="-moz-sms" data-l10n-id="messagesInput" role="textbox" aria-multiline="true" contenteditable="true">
|
||||
</div>
|
||||
</div>
|
||||
<div class="composer-button-container">
|
||||
<span class="message-counter js-message-counter"></span>
|
||||
<button id="messages-send-button" class="icon" aria-label="Send" data-l10n-id="send-button" type="submit">
|
||||
<div aria-label="using SIM {{ n }}" class="js-sim-indication sim-indication" data-l10n-id="send-button-sim-indication">{{ n }}</div>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
<form role="dialog" id="messages-edit-form" data-type="edit">
|
||||
<section role="region" class="theme-settings">
|
||||
<gaia-header id="messages-edit-header" action="close" no-font-fit="">
|
||||
<h1 id="messages-edit-mode" data-l10n-id="deleteMessages-title">Delete messages</h1>
|
||||
<button id="messages-check-uncheck-all-button" class="edit-button" data-l10n-id="select-all">Select all</button>
|
||||
</gaia-header>
|
||||
</section>
|
||||
<div class="bottom-tab" role="region">
|
||||
<ul class="bb-tablist">
|
||||
<li role="presentation">
|
||||
<button id="messages-delete-button" class="disable-when-dialog-animates" data-action="delete-button"></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
<section id="messages-recipient-suggestions" class="article-list hide" data-type="list">
|
||||
<ul class="contact-list recipient-suggestions-list" role="listbox"></ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section class="panel panel-GroupView" role="region" aria-hidden="true">
|
||||
<gaia-header id="information-group-header" class="header" action="back" no-font-fit="">
|
||||
<h1 data-l10n-id="participant" class="header-text">participant</h1>
|
||||
</gaia-header>
|
||||
<article class="view-body article-list" data-type="list">
|
||||
<ul class="contact-list"></ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel panel-ReportView" role="region" aria-hidden="true">
|
||||
<gaia-header id="information-report-header" class="header" action="close" no-font-fit="">
|
||||
<h1 data-l10n-id="message-report">Message report</h1>
|
||||
</gaia-header>
|
||||
<article class="view-body article-list report-view container" data-type="list">
|
||||
<div class="description">
|
||||
<p class="type"></p>
|
||||
<p class="subject">
|
||||
<span data-l10n-id="message-subject">Subject:</span>
|
||||
<span class="detail"></span>
|
||||
</p>
|
||||
<p class="sent-block">
|
||||
<span class="sent-title"></span>
|
||||
<span class="sent-timestamp l10n-contains-date detail"></span>
|
||||
</p>
|
||||
<p class="received-block">
|
||||
<span data-l10n-id="message-received">Received:</span>
|
||||
<span class="received-timestamp l10n-contains-date detail"></span>
|
||||
</p>
|
||||
<p class="lateness-block">
|
||||
<span data-l10n-id="message-lateness">Lateness:</span>
|
||||
<span class="lateness">
|
||||
<span class="lateness-month detail"></span>
|
||||
<span class="lateness-day detail"></span>
|
||||
<span class="lateness-hour detail"></span>
|
||||
<span class="lateness-minute detail"></span>
|
||||
<span class="lateness-second detail"></span>
|
||||
</span>
|
||||
</p>
|
||||
<p class="size-block">
|
||||
<span data-l10n-id="message-size-was">Message size:</span>
|
||||
<span class="size detail"></span>
|
||||
</p>
|
||||
<p class="sim-info hide">
|
||||
<span data-l10n-id="message-via">Via:</span>
|
||||
<span class="sim-detail detail"></span>
|
||||
</p>
|
||||
</div>
|
||||
<span class="contact-title"></span>
|
||||
<ul class="contact-list"></ul>
|
||||
<button class="resend-btn" data-l10n-id="message-resend">Resend</button>
|
||||
</article>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<article id="loading">
|
||||
<form role="dialog" data-type="confirm" class="loading-container">
|
||||
<section>
|
||||
<p class="loading-header" data-l10n-id="deleting-messages">Deleting…</p>
|
||||
<div role="status">
|
||||
<progress class="small"></progress>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<!--
|
||||
- Templates
|
||||
-->
|
||||
|
||||
<div id="contact-suggestion-tmpl" class="hide">
|
||||
<!--
|
||||
<li role="presentation">
|
||||
<a class="suggestion js-contact-info" role="option"
|
||||
data-number="${number}" data-source="contacts" data-name="${name}"
|
||||
data-type="${type}" data-carrier="${carrier}">
|
||||
${photoHTML}
|
||||
<p class="name"><bdi class="ellipsis-dir-fix">${nameHTML}</bdi></p>
|
||||
<p class="number">${phoneDetailsHTML}</p>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="contact-report-tmpl" class="hide">
|
||||
<!--
|
||||
<li>
|
||||
<div class="js-contact-info">
|
||||
${photoHTML}
|
||||
<p class="name"><bdi class="ellipsis-dir-fix">${nameHTML}</bdi></p>
|
||||
<p class="number">${phoneDetailsHTML}</p>
|
||||
</div>
|
||||
</li>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="contact-suggestion-unknown-tmpl" class="hide">
|
||||
<!--
|
||||
<li role="presentation">
|
||||
<a class="suggestion" role="option"
|
||||
data-number="${number}" data-source="contacts" data-name="${name}"
|
||||
data-type="${type}" data-carrier="${carrier}">
|
||||
${photoHTML}
|
||||
<p class="name">${phoneDetailsHTML}</p>
|
||||
<p class="number">
|
||||
<span data-l10n-id="unknown-contact-title">Unknown Contact</span>
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="contact-prompt-tmpl" class="hide">
|
||||
<!--
|
||||
<div class="contact-header">
|
||||
${photoHTML}
|
||||
<p class="name"><bdi class="ellipsis-dir-fix">${name}</bdi></p>
|
||||
<p class="details">${phoneDetailsHTML}</p>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="contact-photo-tmpl" class="hide">
|
||||
<!--
|
||||
<aside role="img" data-l10n-id="contact-photo" aria-label="Contact photo" class="pack-end">
|
||||
<span class="contact-photo" data-type="img"></span>
|
||||
</aside>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="contact-highlight-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="highlight">${str}</span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-number-tmpl" class="hide">
|
||||
<!--
|
||||
<a class="suggestion js-contact-info" data-number="${number}" data-source="manual" data-name="${number}">
|
||||
<p class="name"><bdi class="ellipsis-dir-fix">${number}</bdi></p>
|
||||
</a>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="information-report-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="icon"></span>
|
||||
<span data-l10n-id="${titleL10n}"></span>
|
||||
<span class="timestamp l10n-contains-date"
|
||||
data-l10n-date="${timestamp}"
|
||||
data-l10n-date-format="${messageL10nDateFormat}"
|
||||
>${reportDateL10n}</span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-not-downloaded-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="not-downloaded-message l10n-contains-date"
|
||||
data-l10n-id="${messageL10nId}"
|
||||
data-l10n-args="${messageL10nArgs}"
|
||||
data-l10n-date="${messageL10nDate}"
|
||||
data-l10n-date-format="${messageL10nDateFormat}"></span>
|
||||
<button type="button" class="download" data-l10n-id="${downloadL10nId}"></button>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-date-group-tmpl">
|
||||
<!--
|
||||
<div id="${id}" class="messages-date-group" data-timestamp="${timestamp}">
|
||||
<header
|
||||
class="messages-date-group-header"
|
||||
data-time-update="${headerTimeUpdate}"
|
||||
data-time="${headerTime}"
|
||||
data-date-only="${headerDateOnly}">
|
||||
</header>
|
||||
<ul class="message-list"></ul>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-message-tmpl" class="hide">
|
||||
<!--
|
||||
<label class="pack-checkbox danger">
|
||||
<input type="checkbox" value="${id}">
|
||||
<span></span>
|
||||
</label>
|
||||
<section class="bubble">
|
||||
<h1 class="message-subject">
|
||||
${subject}
|
||||
</h1>
|
||||
<div class="message-content">
|
||||
<p class="message-content-body" tabindex="-1">${bodyHTML}</p>
|
||||
<div class="message-details">
|
||||
${simInformationHTML}
|
||||
<time data-time-update="true" data-time-only="true" data-time="${timestamp}"></time>
|
||||
${messageStatusHTML}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-message-sim-information-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="message-sim-information"
|
||||
data-l10n-id="sim-id-label"
|
||||
data-l10n-args="${simNumberL10nArgs}">
|
||||
</span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-message-status-tmpl" class="hide">
|
||||
<!--
|
||||
<div class="message-status" data-l10n-id="${statusL10nId}"
|
||||
aria-label="Message status"
|
||||
role="status">
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-late-arrival-notice-tmpl" class="hide">
|
||||
<!--
|
||||
<div class="late-arrival-icon" data-l10n-id="late-notice-label" aria-label="Late arrival"></div>
|
||||
<div class="late-arrival-notice"></div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-recipient-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="${className}" x-inputmode="verbatim"
|
||||
contenteditable="${editable}" role="${role}"
|
||||
data-number="${number}" data-source="${source}" data-name="${name}"
|
||||
data-carrier="${carrier}" data-type="${type}" dir="auto">
|
||||
${name}
|
||||
</span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="attachment-nopreview-tmpl" class="hide">
|
||||
<!--
|
||||
<div class="attachment ${errorClass}" data-attachment-type="${type}">
|
||||
<div class="thumbnail-placeholder ${type}-placeholder">
|
||||
</div>
|
||||
<div class="js-size-indicator size-indicator" data-l10n-id="${sizeL10nId}" data-l10n-args="${sizeL10nArgs}"></div>
|
||||
</div>
|
||||
<div class="file-name" dir="auto"></div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="attachment-preview-tmpl" class="hide">
|
||||
<!--
|
||||
<div class="attachment">
|
||||
<div class="thumbnail"></div>
|
||||
<div class="js-size-indicator size-indicator" data-l10n-id="${sizeL10nId}" data-l10n-args="${sizeL10nArgs}"></div>
|
||||
</div>
|
||||
<div class="file-name" dir="auto"></div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="attachment-draft-tmpl" class="hide">
|
||||
<!--
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<base href="${baseURL}">
|
||||
<link rel="stylesheet" type="text/css" href="views/conversation/style/attachment_draft.css">
|
||||
<link rel="stylesheet" type="text/css" href="views/conversation/style/attachment.css">
|
||||
</head>
|
||||
<body class="attachment-draft ${cssClass}">${baseHTML}</body>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Please, don't change template markup alignment, it's done this way to
|
||||
remove any whitespace symbols between inline elements.
|
||||
-->
|
||||
<div id="phone-details-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="${hasTypeClass} ${hasCarrierClass}"><span
|
||||
dir="auto" class="phone-type"
|
||||
data-l10n-id="${type}">${type}</span><span
|
||||
class="phone-type-separator"
|
||||
data-l10n-id="phone-type-separator"></span><span
|
||||
class="phone-number" dir="ltr">${number}</span><span
|
||||
class="phone-carrier-separator"
|
||||
data-l10n-id="phone-carrier-separator"></span><span
|
||||
class="phone-carrier" dir="auto">${carrier}</span>
|
||||
</span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-group-header-tmpl" class="hide">
|
||||
<!--
|
||||
<span class="group-header-title"><bdi class="ellipsis-dir-fix">${name}</bdi> <bdi dir="ltr">(+${participantCount})</bdi></span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="messages-header-tmpl" class="hide">
|
||||
<!--
|
||||
<bdi class="ellipsis-dir-fix">${name}</bdi>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<article>
|
||||
<gaia-sim-picker id="sim-picker" tabindex="-1"></gaia-sim-picker>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
;(function(exports){'use strict';const ATTACHMENT_FOLDER_PATH='sms-attachments/';function getBaseName(filePath){if(!filePath){throw new Error('Filepath is not defined!');}
|
||||
return filePath.substring(filePath.lastIndexOf('/')+1);}
|
||||
function Attachment(blob,options){options=options||{};this.blob=blob;this.isDraft=!!options.isDraft;this.name=blob.name||options.name||'';if(typeof this.name!=='string'){this.name='';}
|
||||
Object.defineProperty(this,'_renderer',{writable:true});}
|
||||
Attachment.prototype={_getAttachmentRenderer:function(){this._renderer=this._renderer||AttachmentRenderer.for(this);return this._renderer;},get size(){return this.blob.size;},get type(){return Utils.typeFromMimeType(this.blob.type);},render:function(readyCallback){var attachmentRenderer=this._getAttachmentRenderer();attachmentRenderer.render().catch(function(e){console.error('Error occurred while rendering attachment.',e);}).then(readyCallback);return attachmentRenderer.getAttachmentContainer();},updateFileSize:function(){var attachmentRenderer=this._getAttachmentRenderer();attachmentRenderer.updateFileSize();},view:function(options){var l10nPromise=this.name?Promise.resolve(this.name):document.l10n.formatValue('unnamed-attachment');return l10nPromise.then(name=>{var mimetype=MimeMapper.guessTypeFromFileProperties(name,this.blob.type.toLowerCase());var filename=MimeMapper.ensureFilenameMatchesType(name,mimetype);filename=ATTACHMENT_FOLDER_PATH+getBaseName(filename);var activity=new MozActivity({name:'open',data:{type:mimetype,filename:filename,blob:this.blob,allowSave:options&&options.allowSave}});activity.onerror=function(){console.error('error with open activity',this.error.name);if(this.error.name==='NO_PROVIDER'){Utils.alert('attachmentOpenError');}};});}};exports.Attachment=Attachment;}(this));
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
;(function(exports){'use strict';const MAX_THUMBNAIL_GENERATION_SIZE=10*1024*1024;const THUMBNAIL_SIZE=100*window.devicePixelRatio;const RENDERERS={draft:{createAttachmentContainer:function(){var container=document.createElement('iframe');container.setAttribute('sandbox','allow-same-origin');return container;},renderTo:function(data,attachmentContainer){attachmentContainer.classList.add(data.cssClass);var template=Template('attachment-draft-tmpl');var markup=template.interpolate({baseURL:location.protocol+'//'+location.host+'/',baseHTML:data.markup,cssClass:data.cssClass},{safe:['baseHTML']});var blob=new Blob([markup],{type:'text/html'});var blobUrl=window.URL.createObjectURL(blob);attachmentContainer.src=blobUrl;return this._whenLoaded(attachmentContainer).then(function onload(){window.URL.revokeObjectURL(blobUrl);var contentDocument=attachmentContainer.contentDocument;var documentElement=contentDocument.documentElement;documentElement.lang=document.documentElement.getAttribute('lang');documentElement.dir=document.documentElement.getAttribute('dir');document.l10n.translateFragment(documentElement);contentDocument.addEventListener('click',()=>attachmentContainer.click());return contentDocument.body;});},_whenLoaded:function(iframe){var innerDocument=iframe.contentDocument;if(innerDocument&&innerDocument.body&&innerDocument.body.classList.contains('attachment-draft')){return Promise.resolve();}
|
||||
return new Promise((resolve)=>{iframe.addEventListener('load',function onload(){this.removeEventListener('load',onload);resolve();});});},getContentNode:function(attachmentContainer){return this._whenLoaded(attachmentContainer).then(()=>attachmentContainer.contentDocument.documentElement);},setL10nAttributes:function(element,l10nId,l10nArgs){document.l10n.setAttributes(element,l10nId,l10nArgs);document.l10n.translateFragment(element);}},base:{createAttachmentContainer:function(){return document.createElement('div');},renderTo:function(data,attachmentContainer){attachmentContainer.classList.add(data.cssClass);attachmentContainer.innerHTML=data.markup;return Promise.resolve(attachmentContainer);},getContentNode:function(attachmentContainer){return Promise.resolve(attachmentContainer);},setL10nAttributes:function(element,l10nId,l10nArgs){document.l10n.setAttributes(element,l10nId,l10nArgs);}}};var AttachmentRenderer=function(attachment){this._attachment=attachment;this._renderer=attachment.isDraft?RENDERERS.draft:RENDERERS.base;this._attachmentContainer=null;};AttachmentRenderer.prototype.getAttachmentContainer=function(){if(!this._attachmentContainer){this._attachmentContainer=this._createAttachmentContainer();}
|
||||
return this._attachmentContainer;};AttachmentRenderer.prototype.render=function(){var attachmentContainer=this.getAttachmentContainer();var thumbnailPromise=this._attachment.type==='img'&&this._attachment.size<MAX_THUMBNAIL_GENERATION_SIZE?this.getThumbnail():Promise.reject();return thumbnailPromise.then((thumbnail)=>{return{markup:this._getBaseMarkup('attachment-preview-tmpl'),cssClass:'preview',thumbnail:thumbnail};}).catch((error)=>{return{markup:this._getBaseMarkup('attachment-nopreview-tmpl',!!error),cssClass:'nopreview'};}).then((data)=>{return this._renderer.renderTo(data,attachmentContainer).then((contentContainer)=>{var thumbnailNode=contentContainer.querySelector('.thumbnail');if(thumbnailNode){thumbnailNode.style.backgroundImage='url("'+data.thumbnail.url+data.thumbnail.fragment+'")';attachmentContainer.dataset.thumbnail=data.thumbnail.url;}
|
||||
var fileNode=contentContainer.querySelector('.file-name');if(this._attachment.name){fileNode.removeAttribute('data-l10n-id');fileNode.textContent=this._attachment.name.slice(this._attachment.name.lastIndexOf('/')+1);}else{fileNode.setAttribute('data-l10n-id','unnamed-attachment');}});});};AttachmentRenderer.prototype.updateFileSize=function(){var attachmentContainer=this.getAttachmentContainer();return this._renderer.getContentNode(attachmentContainer).then((contentNode)=>{var sizeIndicator=contentNode.querySelector('.js-size-indicator');if(!sizeIndicator){throw new Error('updateFileSize should be called after a render().');}
|
||||
var sizeL10n=Utils.getSizeForL10n(this._attachment.size);this._renderer.setL10nAttributes(sizeIndicator,sizeL10n.l10nId,sizeL10n.l10nArgs);});};AttachmentRenderer.prototype.getThumbnail=function(){var blob=this._attachment.blob;return ImageUtils.getSizeAndType(blob).then(function getSizeResolve(data){var fragment=ImageUtils.Downsample.sizeNoMoreThan(THUMBNAIL_SIZE/Math.min(data.width,data.height));return{url:window.URL.createObjectURL(blob),fragment:fragment};});};AttachmentRenderer.prototype._createAttachmentContainer=function(){var attachmentContainer=this._renderer.createAttachmentContainer();attachmentContainer.classList.add('attachment-container');attachmentContainer.dataset.attachmentType=this._attachment.type;return attachmentContainer;};AttachmentRenderer.prototype._getBaseMarkup=function(templateId,hasError){var sizeL10n=Utils.getSizeForL10n(this._attachment.size);return Template(templateId).interpolate({type:this._attachment.type,errorClass:hasError?'corrupted':'',sizeL10nId:sizeL10n.l10nId,sizeL10nArgs:JSON.stringify(sizeL10n.l10nArgs)});};exports.AttachmentRenderer={for:function(attachment){return new AttachmentRenderer(attachment);}};}(this));
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
;'use strict';var Compose=(function(){const TYPES=['image','audio','video'];const UPDATE_DELAY=500;const MIN_AVAILABLE_CHARS_COUNT=20;var placeholderClass='placeholder';var attachmentClass='attachment-container';var attachments=new Map();var dom={form:null,message:null,sendButton:null,attachButton:null,counter:null};var state={empty:true,maxLength:null,size:null,lastScrollPosition:0,resizing:false,type:'sms',lock:{canSend:null,canEdit:null,forceType:null},segmentInfo:{segments:0,charsAvailableInLastSegment:0}};var subject=null;function getContentFromDOM(domElement){var content=[];var node;for(node=domElement.firstChild;node;node=node.nextSibling){var attachment=attachments.get(node);if(attachment){content.push(attachment);continue;}
|
||||
var last=content.length-1;var text=node.textContent;if(text){text=text.replace(/\u00A0/g,' ');}
|
||||
if(node.nodeName=='BR'){if(node===domElement.lastChild){continue;}
|
||||
text='\n';}
|
||||
if(text.length){if(typeof content[last]==='string'){content[last]+=text;}else{content.push(text);}}}
|
||||
return content;}
|
||||
function onContentChanged(duck){if((duck instanceof Attachment&&duck.type==='img')||(duck&&duck.containsImage)){return imageAttachmentsHandling();}
|
||||
var isEmptyMessage=!dom.message.textContent.length&&!hasAttachment();if(isEmptyMessage){var brs=dom.message.getElementsByTagName('br');if(brs.length>1){isEmptyMessage=false;}}
|
||||
var placeholding=dom.message.classList.contains(placeholderClass);if(placeholding&&!isEmptyMessage){dom.message.classList.remove(placeholderClass);}
|
||||
if(!placeholding&&isEmptyMessage){dom.message.classList.add(placeholderClass);}
|
||||
state.emptyMessage=isEmptyMessage;Compose.updateEmptyState();Compose.updateType();Compose.updateSendButton();Compose.updateAttachButton();updateSegmentInfoThrottled();Compose.emit('input');}
|
||||
function onSubjectChanged(){Compose.updateEmptyState();Compose.updateType();Compose.updateSendButton();Compose.updateAttachButton();Compose.emit('subject-change');}
|
||||
function onSubjectVisibilityChanged(){if(subject.isVisible()){dom.form.classList.add('subject-composer-visible');}else{dom.message.focus();dom.form.classList.remove('subject-composer-visible');}}
|
||||
function hasAttachment(){return!!dom.message.querySelector('iframe');}
|
||||
function hasSubject(){return subject.isVisible()&&!!subject.getValue();}
|
||||
function composeKeyEvents(e){if(!compose.canEdit()&&e.which!==8){e.preventDefault();}else{state.size=null;}}
|
||||
function insert(item){var fragment=document.createDocumentFragment();if(!item){return null;}
|
||||
state.size=null;if(item.render){var node=item.render();attachments.set(node,item);fragment.appendChild(node);}else if(item.nodeName==='IFRAME'){fragment.appendChild(item);}else if(typeof item==='string'){item.split('\n').forEach(function(line){fragment.appendChild(document.createTextNode(line));fragment.appendChild(document.createElement('br'));});fragment.lastElementChild.remove();}
|
||||
return fragment;}
|
||||
function imageAttachmentsHandling(){if(Compose.size<Settings.mmsSizeLimitation){onContentChanged();return;}
|
||||
var imgNodes=[];attachments.forEach((attachment,node)=>{if(attachment.type==='img'){imgNodes.push(node);}});var done=0;var images=imgNodes.length;var limit=images>2?Settings.mmsSizeLimitation*0.2:Settings.mmsSizeLimitation*0.4;function imageSized(){if(++done===images){state.resizing=false;onContentChanged();}else{resizedImg(imgNodes[done]);}}
|
||||
function resizedImg(node){var item=attachments.get(node);if(item.blob.size<limit){imageSized();}else{Utils.getResizedImgBlob(item.blob,limit,function(resizedBlob){state.size=null;item.blob=resizedBlob;item.updateFileSize();imageSized();});}}
|
||||
state.resizing=true;resizedImg(imgNodes[0]);onContentChanged();}
|
||||
var segmentInfoTimeout=null;function updateSegmentInfoThrottled(){if(hasAttachment()){return resetSegmentInfo();}
|
||||
if(segmentInfoTimeout===null){segmentInfoTimeout=setTimeout(updateSegmentInfo,UPDATE_DELAY);}}
|
||||
function updateSegmentInfo(){segmentInfoTimeout=null;var value=Compose.getText();var segmentInfoPromise=value?MessageManager.getSegmentInfo(value):Promise.reject();segmentInfoPromise.then(function(segmentInfo){state.segmentInfo=segmentInfo;},resetSegmentInfo).then(compose.updateType.bind(Compose)).then(compose.emit.bind(compose,'segmentinfochange'));}
|
||||
function resetSegmentInfo(){state.segmentInfo={segments:0,charsAvailableInLastSegment:0};}
|
||||
function disposeAttachmentNode(attachmentNode){var thumbnailURL=attachmentNode.dataset.thumbnail;if(thumbnailURL){window.URL.revokeObjectURL(thumbnailURL);}
|
||||
attachments.delete(attachmentNode);}
|
||||
var compose={init:function composeInit(formId){dom.form=document.getElementById(formId);dom.message=document.getElementById('messages-input');dom.sendButton=document.getElementById('messages-send-button');dom.attachButton=document.getElementById('messages-attach-button');dom.counter=dom.form.querySelector('.js-message-counter');dom.messagesAttach=dom.form.querySelector('.messages-attach-container');dom.composerButton=dom.form.querySelector('.composer-button-container');subject=new SubjectComposer(dom.form.querySelector('.js-subject-composer'));subject.on('change',onSubjectChanged);subject.on('visibility-change',onSubjectChanged);subject.on('visibility-change',onSubjectVisibilityChanged);dom.message.addEventListener('input',onContentChanged);dom.message.addEventListener('keydown',composeKeyEvents);dom.message.addEventListener('keypress',composeKeyEvents);dom.message.addEventListener('click',this.onAttachmentClick.bind(this));dom.attachButton.addEventListener('click',this.onAttachClick.bind(this));this.offAll();this.clear();this.on('type',this.onTypeChange.bind(this));this.on('type',this.updateMessageCounter.bind(this));this.on('segmentinfochange',this.updateMessageCounter.bind(this));var onInteracted=this.emit.bind(this,'interact');dom.message.addEventListener('focus',onInteracted);dom.message.addEventListener('click',onInteracted);dom.messagesAttach.addEventListener('click',onInteracted);dom.composerButton.addEventListener('click',onInteracted);subject.on('focus',onInteracted);return this;},getContent:function(){return getContentFromDOM(dom.message);},getSubject:function(){return subject.isVisible()?subject.getValue():null;},setSubject:function(value){return subject.setValue(value);},isSubjectMaxLength:function(){return subject.getValue().length>=subject.getMaxLength();},showSubject:function(){subject.show();subject.focus();},hideSubject:function(){subject.hide();},fromDraft:function(draft){this.clear();if(!draft){return;}
|
||||
if(draft.subject){this.setSubject(draft.subject);subject.show();}
|
||||
if(!draft.content){return;}
|
||||
if(!Array.isArray(draft.content)){throw new Error('If present, the `content` property must be an array.');}
|
||||
draft.content.forEach(function(fragment){if(fragment.blob){fragment=new Attachment(fragment.blob,{name:fragment.name,isDraft:true});}
|
||||
Compose.append(fragment);},Compose);},getText:function(){var out=this.getContent().filter(function(elem){return(typeof elem==='string');});return out.join('');},isEmpty:function(){return state.empty;},setupLock({canSend,canEdit,forceType}){state.lock.canSend=canSend;state.lock.canEdit=canEdit;state.lock.forceType=forceType;},refresh(){this.updateType();this.updateSendButton();this.updateAttachButton();},scrollToTarget:function(target){var targetRect=target.getBoundingClientRect();var containerRect=dom.message.getBoundingClientRect();var offset=(targetRect.top+targetRect.height/2)-
|
||||
(containerRect.top+containerRect.height/2);dom.message.scrollTop+=offset;},scrollMessageContent:function(){if(document.activeElement===dom.message){var range=window.getSelection().getRangeAt(0).cloneRange();if(range.collapsed){range.insertNode(document.createElement('span'));this.scrollToTarget(range);range.deleteContents();}else{this.scrollToTarget(range);}
|
||||
state.lastScrollPosition=dom.message.scrollTop;}else{dom.message.scrollTop=state.lastScrollPosition;}},prepend:function(item){var fragment=insert(item);if(!fragment){return this;}
|
||||
if(dom.message.firstChild&&dom.message.firstChild.nodeName==='BR'){dom.message.insertBefore(fragment,dom.message.childNodes[1]);}else{dom.message.insertBefore(fragment,dom.message.childNodes[0]);}
|
||||
onContentChanged(item);return this;},append:function(item,options){options=options||{};if(Array.isArray(item)){var containsImage=false;item.forEach(function(content){if(content.type==='img'){containsImage=true;}
|
||||
this.append(content,{ignoreChange:true});},this);onContentChanged({containsImage:containsImage});}else{var fragment=insert(item);if(!fragment){return this;}
|
||||
if(document.activeElement===dom.message){var range=window.getSelection().getRangeAt(0);var firstNodes=fragment.firstChild;range.deleteContents();range.insertNode(fragment);this.scrollToTarget(range);dom.message.focus();range.setStartAfter(firstNodes);}else{dom.message.insertBefore(fragment,dom.message.lastChild);this.scrollToTarget(dom.message.lastChild);}
|
||||
if(!options.ignoreChange){onContentChanged(item);}}
|
||||
return this;},clear:function(){state.type='sms';this.onTypeChange();attachments.forEach((attachment,node)=>disposeAttachmentNode(node));dom.message.innerHTML='<br>';subject.reset();state.resizing=false;state.size=0;resetSegmentInfo();segmentInfoTimeout=null;onContentChanged();return this;},focus:function(){dom.message.focus();var selection=window.getSelection();var range=document.createRange();var lastChild=dom.message.lastChild;if(lastChild.tagName==='BR'){range.setStartBefore(lastChild);}else{range.setStartAfter(lastChild);}
|
||||
selection.removeAllRanges();selection.addRange(range);return this;},updateType:function(){var newType=this.getType();if(newType!==state.type){state.type=newType;this.emit('type');}},getType(){var typeForcedByLock=state.lock.forceType&&state.lock.forceType();if(typeForcedByLock){return typeForcedByLock;}
|
||||
var isTextTooLong=state.segmentInfo.segments>Settings.maxConcatenatedMessages;return isTextTooLong||hasSubject()||hasAttachment()?'mms':'sms';},updateEmptyState:function(){state.empty=state.emptyMessage&&!hasSubject();},updateSendButton:function(){dom.sendButton.disabled=!this.canSend();},updateAttachButton:function(){dom.attachButton.disabled=!this.canEdit();},canSend(){var isSendAllowedByLock=!state.lock.canSend||state.lock.canSend();var sizeExceedsLimit=this.type==='mms'&&Settings.mmsSizeLimitation&&this.size>Settings.mmsSizeLimitation;return isSendAllowedByLock&&!sizeExceedsLimit&&!this.isEmpty()&&!this.isResizing;},canEdit(){var isEditAllowedByLock=!state.lock.canEdit||state.lock.canEdit();var sizeExceedsOrEqualToLimit=this.type==='mms'&&Settings.mmsSizeLimitation&&this.size>=Settings.mmsSizeLimitation;return isEditAllowedByLock&&!sizeExceedsOrEqualToLimit;},_onAttachmentRequestError:function c_onAttachmentRequestError(err){var errId=err instanceof DOMError?err.name:err.message;if(errId==='file too large'){Utils.alert({id:'attached-files-too-large',args:{n:1,mmsSize:(Settings.mmsSizeLimitation/1024).toFixed(0)}});}else if(errId!=='ActivityCanceled'&&errId!=='pick cancelled'){console.warn('Unhandled error: ',err);}},onAttachClick:function c_onAttachClick(event){this.requestAttachment().then(this.append.bind(this),this._onAttachmentRequestError);},onAttachmentClick:function c_onAttachmentClick(event){if(!event.target.classList.contains(attachmentClass)||state.resizing){return;}
|
||||
var currentAttachmentDOM=event.target;var currentAttachment=attachments.get(event.target);var name=currentAttachment.name;var blob=currentAttachment.blob;var options={header:name.substr(name.lastIndexOf('/')+1)};var mimeFirstPart=blob.type.substr(0,blob.type.indexOf('/'));var fileType=(TYPES.indexOf(mimeFirstPart)>-1)?mimeFirstPart:'other';var onView=()=>{currentAttachment.view();};function onRemove(){dom.message.removeChild(currentAttachmentDOM);disposeAttachmentNode(currentAttachmentDOM);state.size=null;onContentChanged();}
|
||||
var onReplace=()=>{this.requestAttachment().then((newAttachment)=>{var fragment=insert(newAttachment);dom.message.insertBefore(fragment,currentAttachmentDOM);dom.message.removeChild(currentAttachmentDOM);disposeAttachmentNode(currentAttachmentDOM);onContentChanged(newAttachment);},this._onAttachmentRequestError);};options.items=[{l10nId:`view-attachment-${fileType}`,method:onView},{l10nId:`remove-attachment-${fileType}`,method:onRemove},{l10nId:`replace-attachment-${fileType}`,method:onReplace},{l10nId:'cancel'}];new OptionMenu(options).show();},onTypeChange:function c_onTypeChange(){if(this.type==='sms'){dom.message.setAttribute('x-inputmode','-moz-sms');}else{dom.message.removeAttribute('x-inputmode');}
|
||||
dom.form.dataset.messageType=this.type;},updateMessageCounter:function c_updateMessageCounter(){var counterValue='';if(this.type==='sms'){var segments=state.segmentInfo.segments;var availableChars=state.segmentInfo.charsAvailableInLastSegment;if(segments&&(segments>1||availableChars<=MIN_AVAILABLE_CHARS_COUNT)){counterValue=availableChars+'/'+segments;}}
|
||||
if(counterValue!==dom.counter.textContent){dom.counter.textContent=counterValue;}},requestAttachment:function(){var activityData={type:['image/*','audio/*','video/*','text/vcard']};var activity;var defer=Utils.Promise.defer();if(Settings.mmsSizeLimitation){activityData.maxFileSizeBytes=Settings.mmsSizeLimitation;}
|
||||
activity=new MozActivity({name:'pick',data:activityData});activity.onsuccess=function(){var result=activity.result;if(Settings.mmsSizeLimitation&&result.blob.size>Settings.mmsSizeLimitation&&Utils.typeFromMimeType(result.blob.type)!=='img'){defer.reject(new Error('file too large'));}
|
||||
defer.resolve(new Attachment(result.blob,{name:result.name,isDraft:true}));};activity.onerror=()=>{defer.reject(activity.error);};return defer.promise;}};Object.defineProperty(compose,'type',{get:function composeGetType(){return state.type;}});Object.defineProperty(compose,'size',{get:function composeGetSize(){if(state.size===null){state.size=this.getContent().reduce(function(sum,content){if(typeof content==='string'){return sum+content.length;}else{return sum+content.size;}},0);}
|
||||
return state.size;}});Object.defineProperty(compose,'segmentInfo',{get:function composeGetSegmentInfo(){return state.segmentInfo;}});Object.defineProperty(compose,'isResizing',{get:function composeGetResizeState(){return state.resizing;}});Object.defineProperty(compose,'isSubjectVisible',{get:function composeIsSubjectVisible(){return subject.isVisible();}});return EventDispatcher.mixin(compose,['input','type','segmentinfochange','interact','subject-change']);}());
|
||||
|
|
@ -0,0 +1,582 @@
|
|||
/*global ContactRenderer,
|
||||
Contacts,
|
||||
ConversationView,
|
||||
MessageManager,
|
||||
MobileOperator,
|
||||
Navigation,
|
||||
Settings,
|
||||
Template,
|
||||
Threads,
|
||||
Utils
|
||||
*/
|
||||
|
||||
/*exported Information */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const SECOND = 1000;
|
||||
const MINUTE = 60 * SECOND;
|
||||
const HOUR = 60 * MINUTE;
|
||||
const DAY = 24 * HOUR;
|
||||
const MONTH = 30 * DAY;
|
||||
const LATENESS_DISPLAY_LOWER_LIMIT = 5 * MINUTE;
|
||||
|
||||
/*
|
||||
Information view module is a subpanel belongs to TheadUI panel. This
|
||||
module provide some default method for thiw view module:
|
||||
|
||||
show: Reuse the conversation view for information view. Hiding unrelated icon,
|
||||
reset the header, and render container for showing the view.
|
||||
|
||||
reset: Hide the view container, clean up the contact list and resume the
|
||||
icons/header for ConversationView.
|
||||
|
||||
renderContactList(participants, options): Rendering group-view style contact
|
||||
list inside the page. Participants is an array of contact number and option
|
||||
is for setting additional information for rendering other lock for contact
|
||||
information.
|
||||
*/
|
||||
var TMPL = function createTemplate(tmpls) {
|
||||
for (var key in tmpls) {
|
||||
tmpls[key] = Template(tmpls[key]);
|
||||
}
|
||||
return tmpls;
|
||||
}({
|
||||
number: 'messages-number-tmpl',
|
||||
report: 'information-report-tmpl'
|
||||
});
|
||||
|
||||
/*
|
||||
Summarized single status based on delivery and read status for mms.
|
||||
The 1st level properties represents delivery status and 2nd level properties
|
||||
represents read status.
|
||||
*/
|
||||
const REPORT_MAP = {
|
||||
'not-applicable': {
|
||||
'not-applicable': 'not-applicable',
|
||||
'pending' : 'pending',
|
||||
'success' : 'read',
|
||||
'error' : 'error'
|
||||
},
|
||||
'pending' : {
|
||||
'not-applicable': 'pending',
|
||||
'pending': 'pending',
|
||||
'success' : 'read', // should not possible
|
||||
'error' : 'error'
|
||||
},
|
||||
'success' : {
|
||||
'not-applicable': 'delivered',
|
||||
'pending': 'delivered',
|
||||
'success' : 'read',
|
||||
'error' : 'error'
|
||||
},
|
||||
'error' : {
|
||||
'not-applicable': 'error',
|
||||
'pending': 'error',
|
||||
'success' : 'error', // should not possible
|
||||
'error' : 'error' // should not possible
|
||||
}
|
||||
};
|
||||
|
||||
// Register the message events we wanted for report view refresh
|
||||
const MESSAGE_EVENTS = [
|
||||
'message-failed-to-send',
|
||||
'message-delivered',
|
||||
'message-read',
|
||||
'message-sent',
|
||||
'message-sending'
|
||||
];
|
||||
|
||||
const reportDateTimeFormatOptions = {
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
};
|
||||
|
||||
function completeLocaleFormat(timestamp) {
|
||||
var options = Object.assign(
|
||||
{ hour12: navigator.mozHour12 },
|
||||
reportDateTimeFormatOptions);
|
||||
|
||||
return new Date(+timestamp).toLocaleString(navigator.languages, options);
|
||||
}
|
||||
|
||||
function l10nContainsDateSetup(element, timestamp) {
|
||||
element.dataset.l10nDate = timestamp;
|
||||
element.dataset.l10nDateFormat = JSON.stringify(reportDateTimeFormatOptions);
|
||||
element.textContent = completeLocaleFormat(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate report Div contains delivery report and read report for showing
|
||||
* report information within contact list
|
||||
*/
|
||||
function createReportDiv(reports) {
|
||||
var reportDiv = document.createElement('div');
|
||||
reportDiv.className = 'network-status';
|
||||
var data = {
|
||||
titleL10n: '',
|
||||
reportDateL10n: '',
|
||||
messageL10nDateFormat: JSON.stringify(reportDateTimeFormatOptions),
|
||||
timestamp: ''
|
||||
};
|
||||
var status;
|
||||
var deliveryStatus = reports.deliveryStatus;
|
||||
var readStatus = reports.readStatus;
|
||||
|
||||
if (!readStatus) { // sms
|
||||
status = deliveryStatus === 'success' ?
|
||||
'delivered' :
|
||||
deliveryStatus;
|
||||
} else if (deliveryStatus === 'rejected') {
|
||||
// Status = 'rejected' when receiver is not allowed to download any mms
|
||||
status = 'rejected';
|
||||
} else if (deliveryStatus in REPORT_MAP) {
|
||||
status = REPORT_MAP[deliveryStatus][readStatus];
|
||||
} else {
|
||||
console.error('Invalid message report status: ' + deliveryStatus);
|
||||
return reportDiv;
|
||||
}
|
||||
reportDiv.dataset.deliveryStatus = status;
|
||||
|
||||
switch (status) {
|
||||
case 'not-applicable':
|
||||
return reportDiv;
|
||||
case 'delivered':
|
||||
data.timestamp = '' + reports.deliveryTimestamp;
|
||||
data.reportDateL10n = completeLocaleFormat(reports.deliveryTimestamp);
|
||||
break;
|
||||
case 'read':
|
||||
data.timestamp = '' + reports.readTimestamp;
|
||||
data.reportDateL10n = completeLocaleFormat(reports.readTimestamp);
|
||||
break;
|
||||
}
|
||||
data.titleL10n = 'report-status-' + status;
|
||||
reportDiv.innerHTML = TMPL.report.interpolate(data);
|
||||
|
||||
return reportDiv;
|
||||
}
|
||||
|
||||
function showSimInfo(element, iccId) {
|
||||
var iccManager = navigator.mozIccManager;
|
||||
// Hide the element when single SIM or no iccManager/mobileConnections
|
||||
if (!(Settings.hasSeveralSim() && iccId && iccManager)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var simInfoElement = element.querySelector('.sim-detail');
|
||||
var simId = Settings.getServiceIdByIccId(iccId);
|
||||
|
||||
if (simId === null) {
|
||||
document.l10n.setAttributes(
|
||||
simInfoElement,
|
||||
'dsds-unknown-sim'
|
||||
);
|
||||
} else {
|
||||
var operator = getOperatorByServiceId(simId);
|
||||
var icc = iccManager.getIccById(iccId);
|
||||
var number = icc && icc.iccInfo.msisdn;
|
||||
|
||||
var data = {};
|
||||
var l10nId;
|
||||
|
||||
var info = [operator, number].filter(function(value) {
|
||||
return value;
|
||||
});
|
||||
|
||||
var detailString = info.join(', ');
|
||||
l10nId = info.length ? 'sim-detail' : 'sim-id-label';
|
||||
data = { id: simId + 1, detailString: detailString };
|
||||
document.l10n.setAttributes(
|
||||
simInfoElement,
|
||||
l10nId,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
element.classList.remove('hide');
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming message: return array of sender number string;
|
||||
* Outgoing message: return array of object(number and report div block).
|
||||
*/
|
||||
function createListWithMsgInfo(message) {
|
||||
var list = [];
|
||||
if (message.delivery === 'received' ||
|
||||
message.delivery === 'not-downloaded') { // received message
|
||||
if (message.type === 'mms') {
|
||||
if (message.receivers) {
|
||||
list.push(...message.receivers);
|
||||
}
|
||||
if (!list.includes(message.sender)) {
|
||||
list.push(message.sender);
|
||||
}
|
||||
} else {
|
||||
list.push(message.sender);
|
||||
}
|
||||
} else if (message.type === 'mms') { // sent mms message
|
||||
message.deliveryInfo.forEach(function(info) {
|
||||
list.push({number: info.receiver,
|
||||
infoBlock: createReportDiv(info)});
|
||||
});
|
||||
} else { // sent sms message
|
||||
var info = {};
|
||||
info.deliveryStatus = message.deliveryStatus;
|
||||
info.deliveryTimestamp = message.deliveryTimestamp;
|
||||
list.push({number: message.receiver,
|
||||
infoBlock: createReportDiv(info)});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return operator name depending on the iccId.
|
||||
* Will return the empty string in a single SIM scenario.
|
||||
*/
|
||||
function getOperatorByServiceId(index) {
|
||||
var conn = navigator.mozMobileConnections[index];
|
||||
return MobileOperator.userFacingInfo(conn).operator;
|
||||
}
|
||||
|
||||
var VIEWS = {
|
||||
group: {
|
||||
name: 'group-view',
|
||||
selector: '.panel-GroupView',
|
||||
contactFlavor: 'group-view',
|
||||
|
||||
render: function renderGroup() {
|
||||
var participants = Threads.get(this.id).participants;
|
||||
this.renderContactList(participants);
|
||||
document.l10n.setAttributes(
|
||||
this.headerText, 'participant', { n:participants.length }
|
||||
);
|
||||
},
|
||||
|
||||
setEventListener: function setEventListener() {
|
||||
this.contactList.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
var target = event.target;
|
||||
|
||||
ConversationView.promptContact({
|
||||
number: target.dataset.number
|
||||
}).then(
|
||||
() => Navigation.toPanel('thread', { id: this.id })
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
elements: ['contact-list', 'header', 'header-text']
|
||||
},
|
||||
report: {
|
||||
name: 'report-view',
|
||||
selector: '.panel-ReportView',
|
||||
contactFlavor: 'report-view',
|
||||
|
||||
init: function() {
|
||||
this.onStatusChanged = this.onStatusChanged.bind(this);
|
||||
},
|
||||
|
||||
beforeEnter: function() {
|
||||
this.constructor.prototype.beforeEnter.apply(this, arguments);
|
||||
|
||||
MESSAGE_EVENTS.forEach((event) => {
|
||||
MessageManager.on(event, this.onStatusChanged);
|
||||
});
|
||||
},
|
||||
|
||||
afterLeave: function() {
|
||||
this.constructor.prototype.afterLeave.apply(this, arguments);
|
||||
|
||||
MESSAGE_EVENTS.forEach((event) => {
|
||||
MessageManager.off(event, this.onStatusChanged);
|
||||
});
|
||||
},
|
||||
|
||||
render: function renderReport() {
|
||||
var setL10nAttributes = document.l10n.setAttributes;
|
||||
var request = MessageManager.getMessage(this.id);
|
||||
|
||||
// Hide these dynamic fields to avoid incorrect info displayed at first
|
||||
this.subject.classList.add('hide');
|
||||
this.sizeBlock.classList.add('hide');
|
||||
|
||||
request.onsuccess = (function() {
|
||||
var message = request.result;
|
||||
var type = message.type;
|
||||
var delivery = message.delivery;
|
||||
|
||||
var isIncoming = delivery === 'received' ||
|
||||
delivery === 'not-downloaded';
|
||||
|
||||
// Fill in the description/status/size
|
||||
if (type === 'sms') {
|
||||
setL10nAttributes(this.type, 'message-type-sms');
|
||||
} else { //mms
|
||||
setL10nAttributes(this.type, 'message-type-mms');
|
||||
// subject text content
|
||||
var subject = message.subject;
|
||||
|
||||
this.subject.classList.toggle('hide', !subject);
|
||||
if (subject) {
|
||||
this.subject.querySelector('.detail').textContent = subject;
|
||||
}
|
||||
|
||||
// Message total size show/hide
|
||||
if (message.attachments && message.attachments.length > 0) {
|
||||
var size = message.attachments.reduce(function(size, attachment) {
|
||||
return (size += attachment.content.size);
|
||||
}, 0);
|
||||
var params = Utils.getSizeForL10n(size);
|
||||
setL10nAttributes(this.size, params.l10nId, params.l10nArgs);
|
||||
this.sizeBlock.classList.remove('hide');
|
||||
}
|
||||
}
|
||||
this.container.dataset.delivery = delivery;
|
||||
this.container.classList.toggle('incoming-message', isIncoming);
|
||||
this.container.classList.toggle('outgoing-message', !isIncoming);
|
||||
|
||||
// If incoming message is migrated from the database where sentTimestamp
|
||||
// hadn't been supported yet then we won't have valid value for it.
|
||||
this.container.classList.toggle(
|
||||
'no-valid-sent-timestamp',
|
||||
isIncoming && !message.sentTimestamp
|
||||
);
|
||||
|
||||
setL10nAttributes(
|
||||
this.contactTitle,
|
||||
isIncoming ? 'report-from-title' : 'report-to-title'
|
||||
);
|
||||
|
||||
if (isIncoming) {
|
||||
l10nContainsDateSetup(this.receivedTimestamp, message.timestamp);
|
||||
l10nContainsDateSetup(this.sentTimestamp, message.sentTimestamp);
|
||||
setL10nAttributes(this.sentTitle, 'message-sent');
|
||||
this.renderLateArrivalMsg(message);
|
||||
|
||||
} else {
|
||||
if (delivery === 'sending' || delivery === 'sent') {
|
||||
setL10nAttributes(this.sentTitle, 'message-' + delivery);
|
||||
}
|
||||
if (delivery === 'error' || delivery === 'sent') {
|
||||
l10nContainsDateSetup(this.sentTimestamp, message.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
//show sim information for dual sim device
|
||||
showSimInfo(this.simInfo, message.iccId);
|
||||
|
||||
// Filled in the contact list. Only outgoing message contains detailed
|
||||
// report information.
|
||||
this.renderContactList(createListWithMsgInfo(message));
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
renderLateArrivalMsg: function report_renderLateArrivalMsg(message) {
|
||||
var latenessBlock = this.panel.querySelector('.lateness-block');
|
||||
var lateness = message.timestamp - message.sentTimestamp;
|
||||
var showLatenessNotice = message.timestamp &&
|
||||
message.sentTimestamp &&
|
||||
lateness >= LATENESS_DISPLAY_LOWER_LIMIT;
|
||||
|
||||
if (showLatenessNotice) {
|
||||
var units = [
|
||||
{ unit: 'month', unitVal: MONTH },
|
||||
{ unit: 'day', unitVal: DAY },
|
||||
{ unit: 'hour', unitVal: HOUR },
|
||||
{ unit: 'minute', unitVal: MINUTE },
|
||||
{ unit: 'second', unitVal: SECOND }
|
||||
];
|
||||
|
||||
units.forEach(({unit, unitVal}) => {
|
||||
var output;
|
||||
var unitElement = this.panel.querySelector(`.lateness-${unit}`);
|
||||
|
||||
if (lateness >= unitVal) {
|
||||
// Compute integer value for the current unit output
|
||||
// and get the rest for the next smaller unit
|
||||
output = Math.floor(lateness / unitVal);
|
||||
lateness = lateness % unitVal;
|
||||
|
||||
document.l10n.setAttributes(
|
||||
unitElement,
|
||||
`duration-${unit}-narrow`,
|
||||
{ value: output }
|
||||
);
|
||||
unitElement.classList.remove('hide');
|
||||
} else {
|
||||
unitElement.classList.add('hide');
|
||||
}
|
||||
});
|
||||
latenessBlock.classList.remove('hide');
|
||||
} else {
|
||||
latenessBlock.classList.add('hide');
|
||||
}
|
||||
},
|
||||
|
||||
// Set this flag to true only when resend is triggered.
|
||||
messageResending: false,
|
||||
|
||||
setEventListener: function report_setEventListener() {
|
||||
this.resendBtn.addEventListener('click', () => {
|
||||
this.messageResending = true;
|
||||
ConversationView.resendMessage(this.id);
|
||||
});
|
||||
},
|
||||
|
||||
isReportForMessage: function report_isReportForMessage(id) {
|
||||
return Navigation.isCurrentPanel('report-view', { id: id }) ||
|
||||
(Navigation.isCurrentPanel('report-view') &&
|
||||
this.id === id);
|
||||
},
|
||||
|
||||
onStatusChanged: function report_onStatusChanged(e) {
|
||||
// If we got sending status change in report view after resend clicked
|
||||
// (messageResending is true), we should change report panel id, reset
|
||||
// messageResending flag and refresh for new message report.
|
||||
if (e.message.delivery === 'sending' && this.messageResending) {
|
||||
this.id = e.message.id;
|
||||
this.messageResending = false;
|
||||
}
|
||||
|
||||
this.isReportForMessage(e.message.id) && this.refresh();
|
||||
},
|
||||
|
||||
elements: ['contact-list', 'size', 'size-block', 'type', 'sent-title',
|
||||
'sent-timestamp', 'received-timestamp', 'subject', 'sim-info',
|
||||
'contact-title', 'resend-btn', 'header', 'container'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var Information = function(type) {
|
||||
Object.assign(this, VIEWS[type]);
|
||||
|
||||
if (this.init) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.panel = document.querySelector(this.selector);
|
||||
|
||||
this.elements.forEach(function(name) {
|
||||
this[Utils.camelCase(name)] = this.panel.querySelector('.' + name);
|
||||
}, this);
|
||||
|
||||
this.header.addEventListener(
|
||||
'action', this.backOrClose.bind(this)
|
||||
);
|
||||
|
||||
this.setEventListener && this.setEventListener();
|
||||
this.reset();
|
||||
};
|
||||
|
||||
Information.prototype = {
|
||||
constructor: Information,
|
||||
|
||||
beforeEnter: function(args) {
|
||||
this.id = +args.id;
|
||||
this.header.removeAttribute('no-font-fit');
|
||||
this.show();
|
||||
},
|
||||
|
||||
afterLeave: function() {
|
||||
this.reset();
|
||||
this.id = null;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.render();
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
if (Navigation.isCurrentPanel(this.name)) {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
// Remove all LIs
|
||||
if (this.contactList) {
|
||||
this.contactList.textContent = '';
|
||||
}
|
||||
},
|
||||
|
||||
backOrClose: function() {
|
||||
Navigation.back();
|
||||
},
|
||||
|
||||
// Incrementing ID for each rendering request to avoid possible race when next
|
||||
// renderContactList request earlier than list item appended to contact list
|
||||
// ul. Ignoring the rendering/appending request if the rendering ID doesn't
|
||||
// match the latest rendering ID.
|
||||
renderingId: 0,
|
||||
|
||||
// Param participants could be:
|
||||
// - Array of contact number string or
|
||||
// - Array of object({ number: number, infoBlock: infoBlock })
|
||||
// for rendering the contact list.
|
||||
renderContactList: function(participants) {
|
||||
var ul = this.contactList;
|
||||
var renderer = ContactRenderer.flavor(this.contactFlavor);
|
||||
var currentRenderingId = ++this.renderingId;
|
||||
|
||||
ul.textContent = '';
|
||||
participants.forEach((participant) => {
|
||||
var number, infoBlock, selector;
|
||||
|
||||
if (typeof participant === 'object') {
|
||||
number = participant.number;
|
||||
infoBlock = participant.infoBlock;
|
||||
selector = '.js-contact-info';
|
||||
} else {
|
||||
number = participant;
|
||||
}
|
||||
Contacts.findByAddress(number).then((contacts) => {
|
||||
// If the current rendering ID doesn't match the latest ID, skip current
|
||||
// one and only render for ID which matches latest request ID.
|
||||
if (currentRenderingId !== this.renderingId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (contacts.length > 0) {
|
||||
renderer.render({
|
||||
contact: contacts[0],
|
||||
input: number,
|
||||
infoBlock: infoBlock,
|
||||
infoBlockParentSelector: selector,
|
||||
target: ul
|
||||
});
|
||||
} else {
|
||||
var li = document.createElement('li');
|
||||
li.role = 'presentation';
|
||||
li.innerHTML = TMPL.number.interpolate({
|
||||
number: number
|
||||
});
|
||||
|
||||
var parentBlock = li.querySelector(selector);
|
||||
if (parentBlock && infoBlock) {
|
||||
parentBlock.appendChild(infoBlock);
|
||||
}
|
||||
ul.appendChild(li);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Information.initDefaultViews = function() {
|
||||
// Create group / report information view
|
||||
exports.GroupView = new Information('group');
|
||||
exports.ReportView = new Information('report');
|
||||
};
|
||||
|
||||
exports.Information = Information;
|
||||
|
||||
// end global closure
|
||||
}(this));
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*global ActivityPicker, ConversationView */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
/*
|
||||
Centralized event handling for various
|
||||
data-actions url, email, phone in a message
|
||||
*/
|
||||
|
||||
var LinkActionHandler = {
|
||||
onClick: function lah_onClick(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var dataset = event.target.dataset;
|
||||
var action = dataset.action;
|
||||
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'email-link') {
|
||||
ConversationView.prompt({
|
||||
email: dataset.email,
|
||||
inMessage: true
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'dial-link') {
|
||||
ConversationView.promptContact({
|
||||
number: dataset.dial,
|
||||
inMessage: true
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'url-link') {
|
||||
|
||||
var type = action.replace('-link', '');
|
||||
|
||||
ActivityPicker[type](
|
||||
dataset[type], this.reset, this.reset
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
exports.LinkActionHandler = LinkActionHandler;
|
||||
}(this));
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
;(function(){'use strict';var KNOWN_TLDS=['aero','asia','biz','cat','com','coop','info','int','jobs','mobi','museum','name','net','org','post','pro','tel','travel','xxx','edu','gov','mil','nyc','ac','ad','ae','af','ag','ai','al','am','an','ao','aq','ar','as','at','au','aw','ax','az','ba','bb','bd','be','bf','bg','bh','bi','bj','bm','bn','bo','br','bs','bt','bv','no','bw','by','bz','ca','cc','cd','cf','cg','ch','ci','ck','cl','cm','cn','co','cr','cs','cu','cv','cx','cy','cz','dd','de','dj','dk','dm','do','dz','ec','ee','eg','eh','er','es','et','eu','fi','fj','fk','fm','fo','fr','ga','gb','gd','ge','gf','gg','gh','gi','gl','gm','gn','gp','gq','gr','gs','gt','gu','gw','gy','hk','hm','hn','hr','ht','hu','id','ie','il','im','in','io','iq','ir','is','it','je','jm','jo','jp','ke','kg','kh','ki','km','kn','kp','kr','kw','ky','kz','la','lb','lc','li','lk','lr','ls','lt','lu','lv','ly','ma','mc','md','me','mg','mh','mk','ml','mm','mn','mn','mo','mp','mq','mr','ms','mt','mu','mv','mw','mx','my','mz','na','nc','ne','nf','ng','ni','nl','no','np','nr','nu','nz','om','pa','pe','pf','pg','ph','pk','pl','pm','pn','pr','ps','pt','pw','py','qa','re','ro','rs','ru','su','рф','rw','sa','sb','sc','sd','se','sg','sh','si','sj','no','sk','sl','sm','sn','so','sr','ss','st','su','sv','sx','sy','sz','tc','td','tf','tg','th','tj','tk','tl','tp','tm','tn','to','tp','tl','tr','tt','tv','tw','tz','ua','ug','uk','us','gov','uy','uz','va','vc','ve','vg','vi','vn','vu','wf','ws','ye','yt','yu','za','zm','zw'];var ipv4part='(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';var ipv4RegExp=new RegExp('^(?:'+ipv4part+'\\.){3}'+ipv4part+'$');var leadingDigitsRegExp=/^[+0-9]+/;function checkDomain(domain){if(ipv4RegExp.test(domain)){return true;}else{var parts=domain.split('.');var lastPart=parts[parts.length-1];return lastPart.length>1&&!isFinite(lastPart);}}
|
||||
var safeStart=/[\s.,:;\(>\u0080-\uFFFF]/;const MINIMUM_DIGITS_IN_PHONE_NUMBER=5;const LEADING_DIGIT_BREAKPOINT=7;var LINK_TYPES={phone:{regexp:new RegExp(['(?:\\+\\d{1,4}[ \\t.()-]{0,3}|\\()?'+'(?:\\d{1,4}[ \\t.()-]{0,3})?'+'(?:\\d[\\d \\t.()-]{0,12}\\d)'+'\\b'].join(''),'g'),matchFilter:function phoneMatchFilter(phone,link){var onlyDigits=Utils.removeNonDialables(phone);if(onlyDigits.length<MINIMUM_DIGITS_IN_PHONE_NUMBER){return false;}
|
||||
if(onlyDigits.length===MINIMUM_DIGITS_IN_PHONE_NUMBER&&phone.length!==onlyDigits.length){return false;}
|
||||
var leadingDigits=leadingDigitsRegExp.exec(phone);if(leadingDigits&&leadingDigits[0].length>=LEADING_DIGIT_BREAKPOINT){link.end=link.start+leadingDigits[0].length;}
|
||||
return link;},transform:function phoneTransform(phone,link){return'<a dir="ltr" data-dial="'+phone+'" data-action="dial-link">'+phone+'</a>';}},url:{regexp:new RegExp(['(https?://|rtsp://)?','(\\w(?:\\.?[-\\w]){0,255})','(\\.\\w{1,10})','(?::[0-9]{1,5})?','(?:\\.?/','(?:[^\\s.,?:;!]|[.,?:;!](?!\\s|$)){0,2048}',')?'].join(''),'mgi'),matchFilter:function urlMatchFilter(url,linkSpec){var match=linkSpec.match;var scheme,tld;if(!checkDomain(match[2]+match[3])){return false;}
|
||||
scheme=match[1];tld=match[3]&&match[3].slice(1);if(!scheme&&(!isFinite(tld)&&KNOWN_TLDS.indexOf(tld)===-1)){return false;}
|
||||
if(url.slice(-1)===')'&&url.indexOf('(')===-1){linkSpec.end--;}
|
||||
return linkSpec;},transform:function urlTransform(url,linkSpec){var href=url;if(!linkSpec.match[1]){href='http://'+href;}
|
||||
return'<a data-url="'+href+'" data-action="url-link" >'+url+'</a>';}},email:{regexp:/[\w.+-]+@[\w.-]+\.[a-z.]{2,6}/mgi,transform:function emailTransform(email){return['<a data-email="','" data-action="email-link">','</a>'].join(email);}}};var LINK_TYPES_KEYS=Object.keys(LINK_TYPES);function searchForLinks(type,string){var linkSpecs=[];var spec=LINK_TYPES[type];if(!spec){return linkSpecs;}
|
||||
var regexp=spec.regexp;var matchFilter=spec.matchFilter;var match,linkSpec;while((match=regexp.exec(string))){var rest=string.slice(match.index-1);if(match.index&&!safeStart.test(rest.charAt(0))){var nextSafe=safeStart.exec(rest);if(nextSafe){regexp.lastIndex=match.index+nextSafe.index;}
|
||||
continue;}
|
||||
linkSpec={type:type,start:match.index,length:match[0].length,end:match.index+match[0].length,match:match};if(matchFilter){linkSpec=matchFilter(match[0],linkSpec);}
|
||||
if(linkSpec){linkSpecs.push(linkSpec);regexp.lastIndex=linkSpec.end;}}
|
||||
return linkSpecs;}
|
||||
function linkSort(a,b){return(a.start-b.start)||(b.end-a.end)||(LINK_TYPES_KEYS.indexOf(a.type)-LINK_TYPES_KEYS.indexOf(b.type));}
|
||||
function removeOverlapping(linkSpecs){linkSpecs.sort(linkSort);for(var index=0;index<linkSpecs.length-1;index++){var end=linkSpecs[index].end;while(linkSpecs[index+1]&&linkSpecs[index+1].start<end){linkSpecs.splice(index+1,1);}}}
|
||||
var LinkHelper=window.LinkHelper={searchAndLinkUrl:function lh_searchAndLinkUrl(inputText){return LinkHelper.searchAndLinkClickableData(inputText,{url:true});},searchAndLinkEmail:function lh_searchAndLinkEmail(inputText){return LinkHelper.searchAndLinkClickableData(inputText,{email:true});},searchAndLinkPhone:function lh_searchAndLinkPhone(inputText){return LinkHelper.searchAndLinkClickableData(inputText,{phone:true});},searchAndLinkClickableData:function lh_searchAndLinkClickableData(inputText,mode){mode=mode||LINK_TYPES;var linkSpecs=[];var type;for(type in mode){if(mode[type]&&LINK_TYPES[type]){linkSpecs=linkSpecs.concat(searchForLinks(type,inputText));}}
|
||||
removeOverlapping(linkSpecs);var result='';var lastEnd=0;linkSpecs.forEach(function replaceLink(link){result+=inputText.slice(lastEnd,link.start);var replacing=inputText.slice(link.start,link.end);var replaceWith=LINK_TYPES[link.type].transform(replacing,link);result+=replaceWith;lastEnd=link.end;});result+=inputText.slice(lastEnd);return result;}};})();
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
;(function(exports){'use strict';var view=new WeakMap();var priv=new WeakMap();var data=new WeakMap();var events=new WeakMap();var relation=new WeakMap();var rdigit=/^\d/;function Recipient(opts){var number;opts=opts||{};this.name=String(opts.name||opts.number||'');this.number=(opts.number||'')+'';this.email=opts.email||'';this.editable=opts.editable||'true';this.role=this.editable?'textbox':'button';this.source=opts.source||'manual';this.type=opts.type||'';this.carrier=opts.carrier||'';this.className='recipient';this.isEmail=Settings.supportEmailRecipient&&Utils.isEmailAddress(this.number);this.isLookupable=opts.isLookupable||false;this.isInvalid=opts.isInvalid||false;this.isQuestionable=opts.isQuestionable||false;number=this.number[0]==='+'?this.number.slice(1):this.number;if(this.isEmail){this.className+=' email';}else if(this.source==='manual'&&!rdigit.test(number)){this.isQuestionable=true;}
|
||||
if(this.isQuestionable||this.isInvalid){this.className+=' invalid';}}
|
||||
Recipient.prototype.set=function(dict){var length=Recipient.FIELDS.length;var key;for(var i=0;i<length;i++){key=Recipient.FIELDS[i];this[key]=dict[key];}
|
||||
return this;};Recipient.prototype.clone=function(){return new Recipient(this);};Recipient.FIELDS=['name','number','email','editable','source','type','carrier','isQuestionable','isInvalid','isLookupable'];function Recipients(setup){if(!setup){throw new Error('`setup` missing');}
|
||||
if(!setup.template){throw new Error('`setup.template` missing');}
|
||||
if(!setup.outer){throw new Error('`setup.outer` missing');}
|
||||
if(!setup.inner){throw new Error('`setup.inner` missing');}
|
||||
var list=[];Object.defineProperties(this,{length:{get:function(){return list.length;},set:function(value){var oldLength=list.length;list.length=value;if(value<oldLength){this.render();}
|
||||
return value;}},list:{get:function(){return list.slice();}},numbers:{get:function(){return list.reduce(function(unique,recipient){var value=recipient.number||recipient.email;if(!recipient.isInvalid&&unique.indexOf(value)===-1){unique.push(value);}
|
||||
return unique;},[]);}},inputValue:{get:function(){var node=document.getElementById(setup.inner).lastElementChild;return(node&&node.isPlaceholder)?node.textContent.trim():'';}}});data.set(this,list);events.set(this,{add:[],remove:[],modechange:[]});view.set(this,new Recipients.View(this,setup));}
|
||||
Recipients.prototype.on=function(type,handler){var handlers=events.get(this);if(!handlers[type]){throw new Error('Invalid event type: '+type);}
|
||||
handlers[type].push(handler);return this;};Recipients.prototype.off=function(type,handler){var handlers=events.get(this);if(!handlers[type]){throw new Error('Invalid event type: '+type);}
|
||||
if(!handler){handlers[type].length=0;}else{handlers[type].splice(handlers[type].indexOf(handler),1);}
|
||||
return this;};Recipients.prototype.emit=function(type){var handlers=events.get(this);var args=[].slice.call(arguments,1);var handler,stack;if(!handlers[type]){throw new Error('Invalid event type: '+type);}
|
||||
stack=handlers[type].slice();if(stack.length){while((handler=stack.pop())){handler.apply(null,args);}}
|
||||
return this;};Recipients.prototype.add=function(entry){var list=data.get(this);var added;if(entry.number===undefined){throw new Error('recipient entry missing number');}
|
||||
entry.editable=entry.editable||'false';['name','number','email'].forEach(function(field){if(entry[field]){entry[field]=(entry[field]+'').trim();}});list.push(added=new Recipient(entry));this.emit('add',list.length,added.clone());this.render();return this;};Recipients.prototype.update=function(recipOrIndex,entry){var list=data.get(this);var index=typeof recipOrIndex==='number'?recipOrIndex:list.indexOf(recipOrIndex);if(index>-1){list[index].set(new Recipient(entry));}
|
||||
return this.render();};Recipients.prototype.remove=function(recipOrIndex){var list=data.get(this);var index=typeof recipOrIndex==='number'?recipOrIndex:list.indexOf(recipOrIndex);if(index===-1){return this;}
|
||||
list.splice(index,1);this.emit('remove',list.length);return this.render(index);};Recipients.prototype.render=function(){view.get(this).reset();return this;};Recipients.prototype.focus=function(){view.get(this).focus();return this;};Recipients.prototype.visible=function(type,opts){view.get(this).visible(type,opts||{});return this;};Recipients.View=function(owner,setup){var inner=document.getElementById(setup.inner);var outer=document.getElementById(setup.outer);var template=setup.template;var nodes=[];var clone;var outerCss=window.getComputedStyle(outer);priv.set(this,{owner:owner,inner:inner,outer:outer,template:template,active:null,nodes:nodes,state:{isTransitioning:false,visible:'singleline'},minHeight:parseInt(outerCss.getPropertyValue('min-height'),10),mode:'singleline-mode'});clone=inner.cloneNode(true);clone.innerHTML=template.interpolate(new Recipient());clone.firstElementChild.innerHTML='';Object.defineProperties(this,{last:{get:function(){return nodes[nodes.length-1];}},placeholder:{get:function(){var node=clone.firstElementChild.cloneNode(true);node.isPlaceholder=true;return node;}}});this.updateMode=Utils.debounce(this.updateMode,100);['click','keypress','keyup','blur','pan'].forEach(function(type){outer.addEventListener(type,this,false);},this);new GestureDetector(outer).startDetecting();this.reset();};Recipients.View.isFocusable=true;Recipients.View.prototype.reset=function(){this.clear().render();return this;};Recipients.View.prototype.clear=function(){priv.get(this).inner.textContent='';return this;};Recipients.View.prototype.render=function(opts){var view=priv.get(this);var nodes=view.nodes;var inner=view.inner;var template=view.template;var list=view.owner.list;var length=list.length;var html='';opts=opts||{};opts.addPlaceholder=opts.addPlaceholder===undefined?true:opts.addPlaceholder;if(inner.textContent){this.clear();}
|
||||
for(var i=0;i<length;i++){html+=template.interpolate(list[i]);}
|
||||
if(opts.editable){opts.addPlaceholder=false;html+=template.interpolate(opts.editable);}
|
||||
if(html){inner.innerHTML=html;}
|
||||
if(opts.addPlaceholder){inner.appendChild(this.placeholder);}
|
||||
nodes.length=0;if(!list.length){nodes.push.apply(nodes,inner.children[0]);}else{nodes.push.apply(nodes,inner.children);}
|
||||
nodes.forEach(function forEachNode(node,i){node.isPlaceholder=false;node.contentEditable=false;node.setAttribute('role','button');if(i===nodes.length-1){node.isPlaceholder=true;node.contentEditable=true;node.setAttribute('role','textbox');}else{relation.set(node,list[i]);}});if(view.state.visible==='singleline'&&nodes.length){inner.querySelector(':last-child').scrollIntoView(false);}
|
||||
this.updateMode();return this;};Recipients.View.prototype.focus=function(node){var view=priv.get(this);var range=document.createRange();var selection=window.getSelection();if(Recipients.View.isFocusable){if(!node){node=view.inner.lastElementChild;if(!node.isPlaceholder){node=view.inner.appendChild(this.placeholder);}}
|
||||
if(node&&node.isPlaceholder){node.contentEditable=true;node.focus();}
|
||||
range.selectNodeContents(node);range.collapse(false);selection.removeAllRanges();selection.addRange(range);view.inner.scrollTop=view.inner.scrollHeight;}
|
||||
this.updateMode();return this;};Recipients.View.prototype.updateMode=function(){var view=priv.get(this);var mode=view.inner.scrollHeight>view.minHeight?'multiline-mode':'singleline-mode';if(view.mode!==mode){view.mode=mode;if(mode==='singleline-mode'&&view.state.visible==='multiline'){this.visible('singleline',{refocus:this});}
|
||||
view.owner.emit('modechange',mode);}};var rtype=/^(multi|single)line$/;Recipients.View.prototype.visible=function(type,opts){var view=priv.get(this);var state=view.state;var error='visible "type" (multiline or singleline)';opts=opts||{};if(!type){throw new Error('Missing '+error);}
|
||||
if(!rtype.test(type)){throw new Error('Invalid '+error);}
|
||||
if(state.visible!==type){state.isTransitioning=false;}
|
||||
if(state.isTransitioning){return this;}
|
||||
if(state.visible===type){return this;}
|
||||
view.outer.addEventListener('transitionend',function te(){this.removeEventListener('transitionend',te);var last=view.inner.lastElementChild;var previous;if(Navigation.isCurrentPanel('composer')&&state.visible==='singleline'){while(last!==null&&last.isPlaceholder){previous=last.previousElementSibling;if(!last.textContent){last.parentNode.removeChild(last);}
|
||||
last=previous;}
|
||||
if(opts.refocus){opts.refocus.focus();}}else if(state.visible==='multiline'&&last!==null){last.scrollIntoView(true);}
|
||||
state.isTransitioning=false;});state.visible=type;state.isTransitioning=true;view.outer.className=type;view.inner.className=type;return this;};Recipients.View.prototype.handleEvent=function(event){var view=priv.get(this);var owner=view.owner;var isPreventingDefault=false;var isAcceptedRecipient=false;var isEdittingRecipient=false;var isDeletingRecipient=false;var isLookupable=false;var target=event.target;var keyCode=event.keyCode;var editable='false';var lastElement=view.inner.lastElementChild;var typed,recipient,length,previous;Recipients.View.isFocusable=true;if(event.type==='keypress'||event.type==='keyup'){typed=target.textContent.trim();length=typed.length;}
|
||||
switch(event.type){case'pan':if(view.state.visible==='singleline'&&view.inner.scrollHeight>view.minHeight){if(event.detail.absolute.dy>0){this.visible('multiline');}
|
||||
event.preventDefault();event.stopPropagation();return;}
|
||||
break;case'click':if(target.parentNode===view.inner){if(target.isPlaceholder){return;}
|
||||
typed=lastElement&&lastElement.isPlaceholder?lastElement.textContent.trim():'';if(typed){owner.add({name:typed,number:typed,source:'manual'}).focus();}
|
||||
recipient=relation.get(target);if(recipient){if(recipient.source==='contacts'){Recipients.View.prompts.remove(recipient,function(isConfirmed){if(isConfirmed){owner.remove(relation.get(target));this.reset();}
|
||||
Recipients.View.isFocusable=true;this.focus();}.bind(this));}else{isEdittingRecipient=true;typed=recipient.number||recipient.name;editable='true';owner.remove(recipient);target=view.inner.lastElementChild;target.textContent=typed;this.visible('singleline',{refocus:target});}}}else{if(view.state.visible!=='singleline'){this.visible('singleline',{refocus:this});}else{this.focus();}
|
||||
return;}
|
||||
break;case'keyup':if(typed&&(typed[length-1]===';'||typed[length-1]===',')){isAcceptedRecipient=true;isPreventingDefault=true;typed=typed.replace(/[;,]$/,'');target.textContent=typed;}
|
||||
if(!isAcceptedRecipient&&(typed&&typed.length>=1)){this.visible('singleline',{refocus:target});}
|
||||
if(!typed&&keyCode===event.DOM_VK_BACK_SPACE){isPreventingDefault=true;isDeletingRecipient=true;}
|
||||
this.updateMode();break;case'keypress':if(keyCode===event.DOM_VK_BACK_SPACE){previous=target.previousSibling;if(!typed&&previous){recipient=relation.get(previous);if(previous.dataset.source==='contacts'){isPreventingDefault=true;isDeletingRecipient=true;view.owner.remove(recipient);}else if(previous.dataset.source==='manual'){isEdittingRecipient=true;isPreventingDefault=true;target=previous;view.inner.removeChild(view.inner.lastElementChild);data.get(view.owner).pop();view.owner.emit('remove',data.get(view.owner).length);}}}
|
||||
if((keyCode===13||keyCode===event.DOM_VK_ENTER)||(keyCode===event.DOM_VK_TAB)){if(typed){isPreventingDefault=true;if(target.parentNode===view.inner){if(keyCode!==event.DOM_VK_TAB){isAcceptedRecipient=true;isLookupable=true;target.textContent=typed;}}}}
|
||||
break;}
|
||||
if(isAcceptedRecipient){if(typed){view.owner.add({name:typed,number:typed,editable:editable,source:'manual',role:editable?'textbox':'button',isLookupable:isLookupable});this.render().focus();}}
|
||||
if(isDeletingRecipient){this.render().focus();}
|
||||
if(isEdittingRecipient){target.contentEditable=true;target.setAttribute('role','textbox');target.isPlaceholder=true;this.focus(target);}
|
||||
if(isPreventingDefault){event.preventDefault();}};Recipients.View.prompts={remove:function(recipient,callback){callback=typeof callback==='function'?callback:()=>{};var bodyTemplate=SharedComponents.phoneDetails({number:recipient.number,type:recipient.type,carrier:recipient.carrier});var title=document.createElement('bdi');title.textContent=recipient.name||recipient.number;Utils.confirm({raw:bodyTemplate.toDocumentFragment()},{raw:title},{text:'remove',className:'danger'}).then(()=>callback(true),()=>callback(false));Recipients.View.isFocusable=false;}};exports.Recipients=Recipients;}(this));
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
;(function(exports){'use strict';const LAZY_DEPENDENCIES=['/services/js/messaging/messaging_client.js','/services/js/moz_mobile_connections/moz_mobile_connections_client.js','/shared/js/settings_listener.js','/shared/js/mime_mapper.js','/shared/js/option_menu.js','/shared/js/mobile_operator.js','/shared/elements/gaia_sim_picker/script.js','/views/shared/js/waiting_screen.js','/views/shared/js/errors.js','/views/shared/js/dialog.js','/views/shared/js/error_dialog.js','/views/conversation/js/link_action_handler.js','/views/shared/js/contact_renderer.js','/views/shared/js/activity_picker.js','/views/conversation/js/information.js','/views/shared/js/localization_helper.js'];function initLazyDependencies(){return LazyLoader.load(LAZY_DEPENDENCIES).then(()=>{LocalizationHelper.init();TimeHeaders.init();Information.initDefaultViews();Settings.init();MessagingClient.init(App.instanceId);MozMobileConnectionsClient.init(App.instanceId);Navigation.setReady();InterInstanceEventDispatcher.connect();});}
|
||||
function initShims(){exports.InboxView=Object.freeze({markReadUnread:()=>{},updateThread:()=>{}});var deferred=Utils.Promise.defer();MessageManager.getThreads({each:(thread)=>Threads.set(thread.id,thread),done:deferred.resolve});return deferred.promise;}
|
||||
exports.Startup={init(){Utils.initializeShimHost(App.instanceId);MessageManager.init();ConversationView.init();if(Navigation.isDefaultPanel()){ConversationView.once('visually-loaded',initLazyDependencies);}else{initLazyDependencies();}
|
||||
initShims().then(()=>Navigation.init());}};})(window);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
;(function(exports){'use strict';const MAX_LENGTH=40;const privateMembers=new WeakMap();function onKeyDown(e){var priv=privateMembers.get(this);if(e.keyCode===e.DOM_VK_BACK_SPACE){if(!priv.isHoldingBackspace){priv.isEmptyOnBackspace=!this.getValue();priv.isHoldingBackspace=true;}}else{priv.isHoldingBackspace=false;if(this.getValue().length>=MAX_LENGTH||e.keyCode===e.DOM_VK_RETURN){e.preventDefault();e.stopPropagation();}}}
|
||||
function onKeyUp(e){var priv=privateMembers.get(this);if(e.keyCode===e.DOM_VK_BACK_SPACE&&priv.isHoldingBackspace&&priv.isEmptyOnBackspace){this.hide();}
|
||||
priv.isEmptyOnBackspace=false;priv.isHoldingBackspace=false;}
|
||||
function onInput(e){privateMembers.get(this).updateValue(e.target.textContent);}
|
||||
function updatePlaceholder(){var priv=privateMembers.get(this);if(priv.value){priv.placeholder.remove();}else{priv.node.appendChild(priv.placeholder);}}
|
||||
function updateValue(newValue){var priv=privateMembers.get(this);if(newValue){newValue=newValue.replace(/\n\s*|\u00A0/g,' ');}
|
||||
if(priv.value!==newValue){priv.value=newValue;priv.updatePlaceholder();this.emit('change');}
|
||||
return newValue;}
|
||||
var SubjectComposer=function(node){EventDispatcher.mixin(this,['focus','change','visibility-change']);if(!node){throw new Error('Subject node is required');}
|
||||
var priv={isHoldingBackspace:false,isEmptyOnBackspace:false,isVisible:false,value:'',node:node,input:null,placeholder:null,updateValue:updateValue.bind(this),updatePlaceholder:updatePlaceholder.bind(this),};privateMembers.set(this,priv);priv.input=priv.node.querySelector('.subject-composer-input');priv.placeholder=priv.node.querySelector('.subject-composer-placeholder');priv.input.addEventListener('keydown',onKeyDown.bind(this));priv.input.addEventListener('keyup',onKeyUp.bind(this));priv.input.addEventListener('input',onInput.bind(this));priv.input.addEventListener('focus',this.emit.bind(this,'focus'));};SubjectComposer.prototype.show=function ms_show(){this.toggle(true);};SubjectComposer.prototype.hide=function ms_hide(){this.toggle(false);};SubjectComposer.prototype.toggle=function ms_toggle(toggle){var priv=privateMembers.get(this);priv.node.classList.toggle('hide',!toggle);priv.isVisible=toggle;this.emit('visibility-change');};SubjectComposer.prototype.isVisible=function ms_is_visible(){return privateMembers.get(this).isVisible;};SubjectComposer.prototype.focus=function ms_focus(){privateMembers.get(this).input.focus();};SubjectComposer.prototype.getMaxLength=function ms_get_maxLength(){return MAX_LENGTH;};SubjectComposer.prototype.getValue=function ms_get_value(){return privateMembers.get(this).value;};SubjectComposer.prototype.setValue=function ms_set_value(value){var priv=privateMembers.get(this);if(typeof value!=='string'){throw new Error('Value should be a valid string!');}
|
||||
priv.input.textContent=priv.updateValue(value);};SubjectComposer.prototype.reset=function ms_reset(){var priv=privateMembers.get(this);priv.value=priv.input.textContent='';priv.node.classList.add('hide');priv.isVisible=false;priv.updatePlaceholder();};exports.SubjectComposer=SubjectComposer;})(window);
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
/**
|
||||
* Attachment containers are either:
|
||||
* - <div> elements, in the message thread;
|
||||
* - <iframe> elements, in the Compose area (draft attachments).
|
||||
* Both containers have an `attachment-container' class, but only <iframe>
|
||||
* containers have an `attachment-draft' class.
|
||||
*/
|
||||
.attachment-container {
|
||||
position: relative;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
display: block;
|
||||
border: none;
|
||||
height: 10rem;
|
||||
width: 10rem;
|
||||
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.attachment-draft {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* make sure all pointer events are sent to the main container */
|
||||
.attachment-draft *,
|
||||
.attachment-container * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.attachment-container.nopreview { /* 100x100 px placeholder + filename below */
|
||||
height: 12.7rem; /* font-size = 1.7rem + 1rem margin */
|
||||
}
|
||||
|
||||
/* Internal height of the iframe that contains the attachment draft */
|
||||
.attachment-container.nopreview[data-attachment-type="vcard"] {
|
||||
/* 40x40 px placeholder + filename below */
|
||||
height: 6.7rem; /* font-size = 1.7rem + 1rem margin */
|
||||
}
|
||||
|
||||
/* The internal height of the attachment itself */
|
||||
.attachment[data-attachment-type="vcard"] {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Allow to display the filename properly */
|
||||
#messages-input > [data-attachment-type="vcard"] {
|
||||
width: 20rem;
|
||||
}
|
||||
|
||||
.article-list[data-type="list"] .message .attachment-container {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachments that occur at the bounds of an MMS message do not need
|
||||
* additional spacing.
|
||||
*/
|
||||
#messages-container[data-type="list"] .message .attachment-container:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
#messages-container[data-type="list"] .message .attachment-container:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.attachment {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
offset-inline-start: 0;
|
||||
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
.outgoing .attachment {
|
||||
offset-inline-start: auto;
|
||||
offset-inline-end: 0;
|
||||
}
|
||||
|
||||
.preview .attachment {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: #ececec;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.preview .attachment:active,
|
||||
.preview .attachment:hover {
|
||||
background-color: #b2f2ff;
|
||||
}
|
||||
|
||||
.size-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
|
||||
padding: 0 0.4rem;
|
||||
display: none;
|
||||
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #333;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.attachment-draft > :not([data-attachment-type="vcard"]) .size-indicator {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
|
||||
height: 2rem;
|
||||
display: none;
|
||||
|
||||
line-height: 2rem;
|
||||
font-size: 1.6rem;
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* .file-name has a dir="auto". Moreover, sms.css in not included in attachment
|
||||
* iframe, so we cannot leverage the general alignment rules. */
|
||||
html[dir="ltr"] .file-name {
|
||||
text-align: left;
|
||||
left: 1px; /* Avoid issues related to being too much on the left */
|
||||
right: 0;
|
||||
}
|
||||
html[dir="rtl"] .file-name {
|
||||
text-align: right;
|
||||
left: 0;
|
||||
right: 1px;
|
||||
}
|
||||
|
||||
.attachment-draft .file-name {
|
||||
color: #4a4a4a;
|
||||
}
|
||||
|
||||
.nopreview .file-name {
|
||||
display: block;
|
||||
|
||||
unicode-bidi: -moz-plaintext;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: none no-repeat center center / cover;
|
||||
}
|
||||
|
||||
.thumbnail-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
/* BiDi-proof: CSS sprites */
|
||||
background: url('images/attachments.png') no-repeat left top / 10rem;
|
||||
}
|
||||
|
||||
.attachment-container .thumbnail,
|
||||
.attachment-container .thumbnail-placeholder {
|
||||
position: absolute;
|
||||
top: 0
|
||||
}
|
||||
|
||||
.attachment-draft:hover .thumbnail-placeholder:not(.vcard-placeholder),
|
||||
.attachment-draft:active .thumbnail-placeholder:not(.vcard-placeholder),
|
||||
.attachment-container:not([data-attachment-type="vcard"]):hover .thumbnail-placeholder,
|
||||
.attachment-container:not([data-attachment-type="vcard"]):active .thumbnail-placeholder {
|
||||
background-color: #33cccc;
|
||||
background-blend-mode: luminosity;
|
||||
}
|
||||
|
||||
.img-placeholder {
|
||||
background-position: left -10rem; /* BiDi proof: CSS sprites */
|
||||
}
|
||||
|
||||
.audio-placeholder {
|
||||
background-position: left -20rem; /* BiDi proof: CSS sprites */
|
||||
}
|
||||
|
||||
.video-placeholder {
|
||||
background-position: left -30rem; /* BiDi proof: CSS sprites */
|
||||
}
|
||||
|
||||
.vcard-placeholder {
|
||||
background: url('images/attachment-vcard.png') no-repeat;
|
||||
background-size: 4rem;
|
||||
}
|
||||
html[dir="ltr"] .vcard-placeholder { background-position: left top; }
|
||||
html[dir="rtl"] .vcard-placeholder { background-position: right top; }
|
||||
|
||||
.incoming.received .vcard-placeholder {
|
||||
background-image: url('images/attachment-vcard-incoming.png');
|
||||
}
|
||||
|
||||
.attachment.corrupted .thumbnail-placeholder:after {
|
||||
display: block;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
offset-inline-start: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
content: '';
|
||||
background: url('icons/corrupted.png') no-repeat center center / 10rem;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
font-size: 10px;
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
#messages-container {
|
||||
/* We'll set flex-basis with a shrink factor of 0 to do min-height.
|
||||
* formula: flex-basis = a % + b)
|
||||
* a = (gap2 - gap1) / keyboard_height
|
||||
* b = gap2 - (gap2 - gap1) * (buri_height - header) / keyboard_height + subheader + time_header
|
||||
*
|
||||
* with :
|
||||
* gap1 = 4rem
|
||||
* minimal visible part of a thread when the keyboard is displayed, on buri
|
||||
* gap2 = 8rem
|
||||
* minimal visible part of a thread when the keyboard is hidden, on a buri
|
||||
* keyboard_height = 26rem
|
||||
* measured on a buri, standard english keyboard with suggestions
|
||||
* header = 5rem
|
||||
* subheader = 2rem
|
||||
* buri_height = 48rem
|
||||
* time_header = 2rem
|
||||
*
|
||||
* See https://bugzilla.mozilla.org/show_bug.cgi?id=1015867#c12 for more
|
||||
* information
|
||||
*/
|
||||
flex: 1 0 calc(15.4% + 5.38rem);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#messages-new-message-notice {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
padding: 1rem;
|
||||
padding-inline-start: 2rem;
|
||||
padding-inline-end: 1.5rem;
|
||||
overflow: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
line-height: 2rem;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
#messages-new-message-notice a {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#messages-new-message-notice .contact {
|
||||
overflow: hidden;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#messages-new-message-notice > .text-container {
|
||||
flex: auto;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#messages-new-message-notice > .button-container {
|
||||
flex: none;
|
||||
|
||||
padding: 0.2rem;
|
||||
padding-inline-start: 1.5rem;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
/* remove margin for notice-button class of button */
|
||||
.notice-button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.group-header-title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.carrier-wrapper {
|
||||
display: none;
|
||||
|
||||
box-sizing: border-box;
|
||||
height: 2.2rem;
|
||||
padding: 0 1.5rem;
|
||||
|
||||
font-size: 1.4rem;
|
||||
line-height: 2.2rem;
|
||||
text-align: start;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.carrier-wrapper .phone-carrier {
|
||||
color: #008787;
|
||||
}
|
||||
|
||||
.has-carrier .carrier-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#messages-container .messages-date-group-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
|
||||
height: 2.2rem;
|
||||
margin: 0;
|
||||
padding: 0.6rem 3rem;
|
||||
|
||||
font-size: 1.4rem;
|
||||
line-height: 2.2rem;
|
||||
text-transform: uppercase;
|
||||
|
||||
background: rgba(225, 240, 236, 0.85);
|
||||
color: #008787;
|
||||
}
|
||||
|
||||
/* If has carrier change top position */
|
||||
.has-carrier #messages-container .messages-date-group-header {
|
||||
top: 2.2rem;
|
||||
}
|
||||
|
||||
.has-carrier #messages-container > .messages-date-group:first-child > .messages-date-group-header {
|
||||
margin-top: 2.2rem;
|
||||
}
|
||||
|
||||
/* TODO: check whether we need this rule */
|
||||
#messages-container .messages-date-group-header.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.article-list[data-type="list"] .message {
|
||||
z-index: 0; /* bug 970422 */
|
||||
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.article-list[data-type="list"] .message.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#messages-container[data-type="list"] .message.mms .bubble img {
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* transition in and out edit mode, both for LTR and RTL contexts */
|
||||
.message .bubble {
|
||||
transition: transform var(--transition-duration) ease 0s;
|
||||
}
|
||||
|
||||
/* RTL View */
|
||||
#messages-header-text .ellipsis-dir-fix {
|
||||
width: auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* This file contains styles for Conversation view edit mode. They complement
|
||||
* styles from views/shared/style/edit-mode.css that are applied as well.
|
||||
* View edit mode is activated via Options -> Select messages.
|
||||
*/
|
||||
|
||||
|
||||
.edit #messages-container {
|
||||
margin-bottom: 5.5rem;
|
||||
}
|
||||
|
||||
.edit .article-list[data-type="list"] .message {
|
||||
border-color: #e7e7e7;
|
||||
}
|
||||
|
||||
.edit [data-type="list"] .message .bubble {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.edit .message .bubble {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .edit .message.incoming .bubble {
|
||||
transform: translateX(2.5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit .message.incoming .bubble {
|
||||
transform: translateX(-2.5rem);
|
||||
}
|
||||
|
||||
.edit #messages-compose-form {
|
||||
display: none;
|
||||
}
|
||||
|
After Width: | Height: | Size: 584 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 454 B |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 411 B |
|
After Width: | Height: | Size: 354 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 398 B |
|
|
@ -0,0 +1,238 @@
|
|||
.message.incoming {
|
||||
padding-inline-start: 1.5rem;
|
||||
padding-inline-end: 4rem;
|
||||
}
|
||||
|
||||
.message.outgoing {
|
||||
padding-inline-start: 4rem;
|
||||
padding-inline-end: 1.5rem;
|
||||
}
|
||||
|
||||
.message .bubble {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
padding: 1rem 1.5rem;
|
||||
overflow: visible;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.bubble .message-content-body:focus {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
.message.outgoing .bubble {
|
||||
background: #fff;
|
||||
}
|
||||
html[dir="ltr"] .message.outgoing .bubble {
|
||||
float: right;
|
||||
border-radius: 1.5rem 0 1.5rem 1.5rem;
|
||||
}
|
||||
html[dir="rtl"] .message.outgoing .bubble {
|
||||
float: left;
|
||||
border-radius: 0 1.5rem 1.5rem 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.message.outgoing .bubble,
|
||||
.message.outgoing a:active {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.message.incoming .bubble {
|
||||
background: #39c3c2;
|
||||
}
|
||||
html[dir="ltr"] .message.incoming .bubble {
|
||||
float: left;
|
||||
border-radius: 0 1.5rem 1.5rem 1.5rem;
|
||||
}
|
||||
html[dir="rtl"] .message.incoming .bubble {
|
||||
float: right;
|
||||
border-radius: 1.5rem 0 1.5rem 1.5rem;
|
||||
}
|
||||
|
||||
.message.incoming .bubble,
|
||||
.message.incoming a:active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.message p.message-content-body {
|
||||
white-space: pre-wrap;
|
||||
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* BB overrides */
|
||||
.message.outgoing p:first-of-type:last-of-type,
|
||||
.message.incoming p:first-of-type:last-of-type {
|
||||
padding-top: 0.5rem;
|
||||
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.message.incoming p:first-of-type {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.message.sending p:first-of-type {
|
||||
color: #6e6e6e;
|
||||
}
|
||||
|
||||
/* End of BB overrides */
|
||||
|
||||
.outgoing.error .bubble {
|
||||
background: #fae1df;
|
||||
}
|
||||
|
||||
.incoming.error .bubble {
|
||||
background: #ff9898;
|
||||
}
|
||||
|
||||
.message.sending .bubble,
|
||||
.message.sending a,
|
||||
.message.sending a:active {
|
||||
color: #6e6e6e;
|
||||
}
|
||||
|
||||
.message.sending .message-subject,
|
||||
.message.sending .message-content-body {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Message Subject */
|
||||
.message .message-subject {
|
||||
display: none;
|
||||
|
||||
margin-bottom: 1rem;
|
||||
|
||||
font-size: 1.4rem;
|
||||
font-style: italic;
|
||||
line-height: 1.7rem;
|
||||
|
||||
unicode-bidi: -moz-plaintext;
|
||||
}
|
||||
|
||||
.message.has-subject .message-subject {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Message content styles */
|
||||
.message-content a {
|
||||
color: #004eff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.message-content > p {
|
||||
unicode-bidi: -moz-plaintext;
|
||||
}
|
||||
|
||||
.message-details {
|
||||
display: flex;
|
||||
|
||||
margin-top: 0.5rem;
|
||||
|
||||
font-size: 1.4rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.message.sending time,
|
||||
.message.sending .message-sim-information {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message.outgoing .message-details {
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
.message.outgoing.error .message-details {
|
||||
color: #e97f79;
|
||||
}
|
||||
|
||||
.message.incoming .message-details {
|
||||
color: #d1eeee;
|
||||
}
|
||||
|
||||
.message.incoming .late-arrival-notice {
|
||||
color: #026a69;
|
||||
}
|
||||
|
||||
.message.incoming .late-arrival-icon {
|
||||
margin-inline-start: 1rem;
|
||||
margin-inline-end: 0.1rem;
|
||||
min-width: 1.5rem;
|
||||
height: 2rem;
|
||||
|
||||
background: no-repeat center center / 1.5rem;
|
||||
background-image: url('images/icons/late_clock.png');
|
||||
}
|
||||
|
||||
.message.incoming.error .message-details {
|
||||
color: #ffe0dc;
|
||||
}
|
||||
|
||||
.message-sim-information {
|
||||
margin-inline-end: 0.4rem;
|
||||
}
|
||||
|
||||
.message-sim-information::after {
|
||||
content: ' ‧ ';
|
||||
}
|
||||
|
||||
/* Message status aside panel */
|
||||
.message .message-status {
|
||||
height: 2rem;
|
||||
min-width: 3rem;
|
||||
|
||||
background: no-repeat center center / 1.6rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.message.error .message-status {
|
||||
background-image: url('images/icons/exclamation.png');
|
||||
}
|
||||
|
||||
.message.delivered .message-status {
|
||||
background-image: url('images/icons/deliveredtick.png');
|
||||
}
|
||||
|
||||
.message.read .message-status {
|
||||
background-image: url('images/icons/message_read.png');
|
||||
}
|
||||
|
||||
.message.pending .message-status,
|
||||
.message.sending .message-status {
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
/* Message download button */
|
||||
.message .download {
|
||||
display: block;
|
||||
|
||||
min-height: initial;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
/* Eliminating container paragraph line-height */
|
||||
margin: -1.5rem 0 0;
|
||||
|
||||
background: none;
|
||||
color: #fff;
|
||||
border: none;
|
||||
/* We're using border-bottom instead of just underline style as line is too
|
||||
close to the text and doesn't look similar to visual spec. */
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
.message .download:active {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* hide download button via css on expired messages */
|
||||
.message.expired .download {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message.pending .download {
|
||||
border-color: transparent;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/* Message information page for report details*/
|
||||
.report-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.report-view > .description {
|
||||
padding-top: 2rem;
|
||||
padding-inline-end: 1.5rem;
|
||||
padding-bottom: 3rem;
|
||||
padding-inline-start: 3rem;
|
||||
}
|
||||
|
||||
.report-view p,
|
||||
.report-view span {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1rem;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
.report-view p {
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.report-view .description > .type {
|
||||
padding-top: 0;
|
||||
|
||||
font-size: 1.6rem;
|
||||
color: #ff4e00;
|
||||
}
|
||||
|
||||
.report-view .description .detail {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.report-view .description > .type[data-l10n-id="message-type-sms"] {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* Hiding some elements for some states */
|
||||
.report-view[data-delivery="error"] .sent-title,
|
||||
.report-view[data-delivery="sending"] .sent-timestamp,
|
||||
.outgoing-message .received-block,
|
||||
.outgoing-message .lateness-block {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Hide sent timestamp label if we don't have valid value to display */
|
||||
.report-view.no-valid-sent-timestamp .sent-block {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.report-view .contact-title {
|
||||
color: #008787;
|
||||
font-size: 1.6rem;
|
||||
padding-inline-start: 3rem;
|
||||
}
|
||||
|
||||
.report-view > ul.contact-list {
|
||||
/* Showing 4 complete contacts and part of the contact name if list
|
||||
contains more than 4 contacts */
|
||||
position: static;
|
||||
|
||||
margin-top: 0.5rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.report-view > .contact-list > li {
|
||||
height: auto;
|
||||
min-height: 9rem;
|
||||
padding-top: 0.7rem;
|
||||
padding-bottom: 1rem;
|
||||
padding-inline-end: 0;
|
||||
padding-inline-start: 1.5rem;
|
||||
|
||||
border-top: solid 0.1rem #e7e7e7;
|
||||
border-bottom: medium none;
|
||||
}
|
||||
|
||||
.report-view > .contact-list p,
|
||||
.report-view > .contact-list span {
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.6rem;
|
||||
}
|
||||
|
||||
.report-view > .contact-list .name {
|
||||
padding-top: 0;
|
||||
|
||||
font-size: 1.8rem;
|
||||
font-weight: 500;
|
||||
line-height: 2.6rem;
|
||||
}
|
||||
|
||||
.report-view > .contact-list aside {
|
||||
/* margin-top = (li - padding - aside) / 2 = (9rem - 2rem - 5.5rem) / 2 */
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
|
||||
.report-view > .contact-list .number {
|
||||
padding-top: 0.6rem;
|
||||
}
|
||||
|
||||
.report-view .network-status {
|
||||
padding-top: 0.7rem;
|
||||
}
|
||||
|
||||
.report-view .name + .network-status {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.report-view .network-status,
|
||||
.report-view .network-status span {
|
||||
line-height: 1.4rem;
|
||||
}
|
||||
|
||||
.contact-list p.number .phone-carrier {
|
||||
color: #008787;
|
||||
}
|
||||
|
||||
.report-view > .contact-list .icon {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
margin: -0.1rem 0 0.1rem;
|
||||
margin-inline-end: 0.7rem;
|
||||
|
||||
background: no-repeat center center / 1.6rem transparent;
|
||||
}
|
||||
html[dir="ltr"] .report-view > .contact-list .icon { float: left; }
|
||||
html[dir="rtl"] .report-view > .contact-list .icon { float: right; }
|
||||
|
||||
.contact-list [data-delivery-status="delivered"] .icon {
|
||||
background-image: url('images/icons/deliveredtick.png');
|
||||
}
|
||||
|
||||
.contact-list [data-delivery-status="read"] .icon {
|
||||
background-image: url('images/icons/message_read.png');
|
||||
}
|
||||
|
||||
.contact-list [data-delivery-status="error"] .icon {
|
||||
background-image: url('images/icons/exclamation.png');
|
||||
}
|
||||
|
||||
.contact-list [data-delivery-status="not-applicable"],
|
||||
.contact-list [data-delivery-status="pending"] .icon,
|
||||
.contact-list [data-delivery-status="pending"] .timestamp,
|
||||
.contact-list [data-delivery-status="error"] .timestamp,
|
||||
.contact-list [data-delivery-status="rejected"] .icon,
|
||||
.contact-list [data-delivery-status="rejected"] .timestamp{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.report-view .resend-btn {
|
||||
display: none;
|
||||
width: calc(100% - 3rem);
|
||||
|
||||
flex-shrink: 0;
|
||||
margin: 0 1.5rem 2rem;
|
||||
|
||||
background-color: #00caf2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.report-view[data-delivery="error"] .resend-btn {
|
||||
display: block;
|
||||
}
|
||||
183
assets/webapps/sms.gaiamobile.org/views/inbox/index.html
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<!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>Inbox</title>
|
||||
|
||||
<!--App Style root.css-->
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/root.css">
|
||||
|
||||
<!-- Stable Building blocks -->
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/animations.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/action_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/option_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/buttons.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/tabs.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/progress_activity.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/status.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css">
|
||||
|
||||
<!-- App styles -->
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/sms.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/panel.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/edit-mode.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/notification.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/shared/style/common.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/views/inbox/style/inbox.css">
|
||||
<link rel="stylesheet" type="text/css" href="/views/inbox/style/edit-mode.css">
|
||||
|
||||
<!-- 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/views.inbox.index.{locale}.json">
|
||||
|
||||
<!-- <script defer="" src="/mocks/navigator_moz_icc_manager.js"></script> -->
|
||||
<!-- Firefox Desktop shims -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Web Components -->
|
||||
<!-- <script defer="" src="/shared/elements/config.js"></script> -->
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css">
|
||||
<script async="" src="/shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
<!-- Shared code -->
|
||||
<!-- <script defer="" src="/shared/js/intl/l20n.js"></script> -->
|
||||
<!-- Should be removed while gecko support navigator.mozHour12 API -->
|
||||
<!-- <script defer="" src="/shared/js/date_time_helper.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/template.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/async_storage.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/lazy_loader.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/sticky_header.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/js/contact_photo_helper.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/event_dispatcher.js"></script> -->
|
||||
|
||||
<!-- <script defer="" src="/views/shared/js/utils.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/navigation.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/inter_instance_event_dispatcher.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/time_headers.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/contacts.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/drafts.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/threads.js"></script> -->
|
||||
<!-- <script defer="" src="/views/inbox/js/inbox.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/settings.js"></script> -->
|
||||
<!-- <script defer="" src="/lib/bridge/client.js"></script> -->
|
||||
<!-- <script defer="" src="/lib/bridge/plugins/stream/client.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/conversation/conversation_client.js"></script> -->
|
||||
<!-- <script defer="" src="/services/js/message_manager.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/app.js"></script> -->
|
||||
<!-- <script defer="" src="/views/inbox/js/startup.js"></script> -->
|
||||
<!-- <script defer="" src="/views/shared/js/bootstrap.js"></script> --><script src="./gaia_build_defer_index.js" defer="" type="text/javascript"></script>
|
||||
|
||||
<!-- Lazy load scripts
|
||||
<script defer src="/shared/js/option_menu.js"></script>
|
||||
<script defer src="/views/shared/js/dialog.js"></script>
|
||||
<script defer src="/views/shared/js/waiting_screen.js"></script>
|
||||
<script defer src="/views/shared/js/activity_picker.js"></script>
|
||||
-->
|
||||
<meta content="2.5" name="appVersion"></head>
|
||||
|
||||
<body role="application" class="skin-comms theme-communications">
|
||||
<iframe src="/views/shared/shim_host.html" class="shim-host" hidden=""></iframe>
|
||||
<article id="main-wrapper" class="wrapper">
|
||||
<section class="panel panel-InboxView" role="region" aria-hidden="true">
|
||||
<!-- title-start and title-end indicates the button widths respectively
|
||||
at the start and end of the line -->
|
||||
<!-- see https://github.com/gaia-components/gaia-header/ for more information -->
|
||||
<!-- WARNING: the actual value depends on the actual CSS being used in gaia-header -->
|
||||
<gaia-header class="view-header regular-header" title-start="0" title-end="100">
|
||||
<h1 data-l10n-id="messages">Messages</h1>
|
||||
<a aria-label="New message" href="#/composer" id="threads-composer-link" data-icon="compose" data-l10n-id="composeLink"></a>
|
||||
<a aria-label="Actions" id="threads-options-button" data-icon="more" data-l10n-id="actionsButton"></a>
|
||||
<a aria-label="Settings" id="threads-settings-button" data-icon="settings" data-l10n-id="settingsButton"></a>
|
||||
</gaia-header>
|
||||
<article id="threads-container" class="view-body edit-container" data-type="list">
|
||||
</article>
|
||||
|
||||
<div id="threads-no-messages">
|
||||
<div id="no-result-message">
|
||||
<p data-l10n-id="noMessages-title">No messages</p>
|
||||
<p data-l10n-id="noMessages-text-2">Start a conversation now</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="threads-edit-form" role="dialog" data-type="edit">
|
||||
<section role="region" class="theme-settings">
|
||||
<gaia-header id="threads-edit-header" action="close" no-font-fit="">
|
||||
<h1 id="threads-edit-mode" data-l10n-id="selectThreads-title">Select</h1>
|
||||
<button id="threads-check-uncheck-all-button" class="edit-button" data-l10n-id="select-all">Select all</button>
|
||||
</gaia-header>
|
||||
</section>
|
||||
<div class="bottom-tab" role="region">
|
||||
<ul class="bb-tablist">
|
||||
<li role="presentation">
|
||||
<button id="threads-delete-button" class="disable-when-dialog-animates" data-action="delete-button"></button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button id="threads-read-unread-button" data-action="mark-as-read"></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<section id="threads-draft-saved-banner" class="hide" role="status">
|
||||
<p data-l10n-id="message-draft-saved">Message saved as draft</p>
|
||||
</section>
|
||||
|
||||
<div id="sticky"></div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<article id="loading">
|
||||
<form role="dialog" data-type="confirm" class="loading-container">
|
||||
<section>
|
||||
<p class="loading-header" data-l10n-id="deleting-messages">Deleting…</p>
|
||||
<div role="status">
|
||||
<progress class="small"></progress>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<!--
|
||||
- Templates
|
||||
-->
|
||||
|
||||
<div id="messages-thread-tmpl" class="hide">
|
||||
<!--
|
||||
<label class="pack-checkbox negative">
|
||||
<input type="checkbox" value="${id}" data-mode="${mode}">
|
||||
<span></span>
|
||||
</label>
|
||||
<a href="${hash}">
|
||||
<aside role="note" class="icon icon-unread icon-draft js-conversation-state"></aside>
|
||||
<aside class="pack-end threadlist-item-picture"
|
||||
role="img" aria-label="Contact photo"
|
||||
data-l10n-id="contact-photo">
|
||||
<span data-type="img"></span>
|
||||
</aside>
|
||||
<p class="threadlist-item-title"><bdi class="ellipsis-dir-fix"></bdi></p>
|
||||
<p class="summary">
|
||||
<time data-time-update="true" data-time-only="true" data-time="${timestamp}"></time>
|
||||
<span class="mms-icon" data-l10n-id="mms-label">MMS</span>
|
||||
<span dir="auto" class="body-text js-conversation-body ellipsis-dir-fix"></span>
|
||||
</p>
|
||||
</a>
|
||||
-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
53
assets/webapps/sms.gaiamobile.org/views/inbox/js/inbox.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
;;(function(exports){'use strict';const privateMembers=new WeakMap();function createBdiNode(content){var bdi=document.createElement('bdi');bdi.textContent=content;return bdi;}
|
||||
var InboxView={DRAFT_SAVED_DURATION:5000,FIRST_PANEL_THREAD_COUNT:9,timeouts:{onDraftSaved:null},count:0,inEditMode:false,notifyAboutSavedDraftWithId:null,init:function inbox_init(){this.tmpl={thread:Template('messages-thread-tmpl')};['container','no-messages','read-unread-button','check-uncheck-all-button','composer-link','delete-button','edit-header','options-button','settings-button','edit-mode','edit-form','draft-saved-banner'].forEach(function(id){this[Utils.camelCase(id)]=document.getElementById('threads-'+id);},this);this.mainWrapper=document.getElementById('main-wrapper');this.composerLink.addEventListener('click',this.launchComposer.bind(this));this.readUnreadButton.addEventListener('click',()=>{this.markReadUnread(this.selectionHandler.selectedList,this.readUnreadButton.dataset.action==='mark-as-read');});this.deleteButton.addEventListener('click',()=>{this.delete(this.selectionHandler.selectedList);});this.editHeader.addEventListener('action',this.cancelEdit.bind(this));this.optionsButton.addEventListener('click',this.showOptions.bind(this));this.settingsButton.addEventListener('click',function oSettings(){ActivityPicker.openSettings();});this.container.addEventListener('click',this);this.container.addEventListener('contextmenu',this);this.editForm.addEventListener('submit',this);navigator.mozContacts.addEventListener('contactchange',this.updateContactsInfo.bind(this));MessageManager.on('message-sending',this.onMessageSending.bind(this));MessageManager.on('message-received',this.onMessageReceived.bind(this));MessageManager.on('threads-deleted',this.onThreadsDeleted.bind(this));Drafts.on('deleted',this.onDraftDeleted.bind(this));Drafts.on('saved',this.onDraftSaved.bind(this));privateMembers.set(this,{groupThreadTitleMaxLength:(window.innerWidth-100)/10});this.once('fully-loaded',()=>{this.ensureReadAheadSetting();});this.once('visually-loaded',()=>{this.initStickyHeader();});this.sticky=null;},initStickyHeader:function inbox_initStickyHeader(){if(!this.sticky){this.sticky=new StickyHeader(this.container,document.getElementById('sticky'));}},beforeEnter:function inbox_beforeEnter(args={}){if(this.notifyAboutSavedDraftWithId){this.showDraftSavedBanner();this.notifyAboutSavedDraftWithId=null;}},beforeLeave:function inbox_beforeLeave(){this.cancelEdit();},getIdIterator:function inbox_getIdIterator(){return Threads.keys();},setContact:function inbox_setContact(node){var threadId=node.dataset.threadId;var draftId=node.dataset.draftId;var threadOrDraft=draftId?Drafts.byDraftId(+draftId):Threads.get(+threadId);if(!threadOrDraft){throw new Error('Thread node is invalid!');}
|
||||
var threadNumbers=threadOrDraft.participants||threadOrDraft.recipients;var titleContainer=node.querySelector('.threadlist-item-title');var title=titleContainer.firstElementChild;var picture=node.querySelector('.threadlist-item-picture');if(!threadNumbers||!threadNumbers.length){title.setAttribute('data-l10n-id','no-recipient');return;}
|
||||
function*updateThreadNode(number){var contact=yield InboxView.findContact(number,{photoURL:true});var isContact=!!contact.isContact;picture.classList.toggle('has-picture',isContact);picture.classList.toggle('default-picture',isContact&&!contact.photoURL);title.textContent=contact.title||number;var photoUrl=node.dataset.photoUrl;if(photoUrl){window.URL.revokeObjectURL(photoUrl);}
|
||||
if(contact.photoURL){node.dataset.photoUrl=contact.photoURL;}else if(photoUrl){node.dataset.photoUrl='';}
|
||||
if(contact.photoURL){picture.firstElementChild.style.backgroundImage=['url('+contact.photoURL+')','url(/views/inbox/style/images/default_contact_image.png)'].join(', ');}else{picture.firstElementChild.style.backgroundImage=null;}}
|
||||
function*updateGroupThreadNode(numbers,titleMaxLength){var contactTitle,number;var i=0;var threadTitleLength=0;var groupTitle=document.createElement('span');var separatorNode=document.createElement('span');separatorNode.setAttribute('data-l10n-id','thread-participant-separator');picture.firstElementChild.textContent=numbers.length;picture.classList.add('has-picture','group-picture');while(i<numbers.length&&threadTitleLength<titleMaxLength){number=numbers[i++];contactTitle=(yield InboxView.findContact(number)).title||number;if(threadTitleLength>0){groupTitle.appendChild(separatorNode.cloneNode(true));}
|
||||
groupTitle.appendChild(createBdiNode(contactTitle));threadTitleLength+=contactTitle.length;}
|
||||
titleContainer.replaceChild(groupTitle,title);}
|
||||
if(!title.textContent){title.textContent=threadNumbers[0];}
|
||||
if(threadNumbers.length===1){return Utils.Promise.async(updateThreadNode)(threadNumbers[0]);}
|
||||
return Utils.Promise.async(updateGroupThreadNode)(threadNumbers,privateMembers.get(this).groupThreadTitleMaxLength);},findContact:function(number,options){return Contacts.findByAddress(number).then(function(contacts){var details=Utils.getContactDetails(number,contacts,options);if(!details.isContact){Contacts.addUnknown(number);}
|
||||
return details;});},handleEvent:function inbox_handleEvent(event){var parent=event.target.parentNode;var threadId=parent.dataset.threadId;switch(event.type){case'click':if(this.inEditMode){return;}
|
||||
if(threadId){event.preventDefault();var draftId=parent.dataset.draftId;if(draftId){Navigation.toPanel('composer',{draftId:+draftId,});}else{Navigation.toPanel('thread',{id:+threadId});}}
|
||||
break;case'contextmenu':if(this.inEditMode||!threadId){return;}
|
||||
event.preventDefault();event.stopPropagation();var params={type:'action',header:{l10nId:'thread-options'},items:[{l10nId:'delete-thread',method:this.delete.bind(this,[threadId])}]};var thread=Threads.get(+threadId);if(!thread.isDraft){var isRead=thread.unreadCount>0;var l10nKey=isRead?'mark-as-read':'mark-as-unread';params.items.push({l10nId:l10nKey,method:this.markReadUnread.bind(this,[threadId],isRead)});}
|
||||
params.items.push({l10nId:'cancel'});var options=new OptionMenu(params);options.show();break;case'submit':event.preventDefault();break;}},launchComposer:function inbox_launchComposer(e){e.preventDefault();Navigation.toPanel('composer');},updateSelectionStatus:function inbox_updateSelectionStatus(){var selected=this.selectionHandler;if(this.selectionHandler.allSelected()){this.checkUncheckAllButton.setAttribute('data-l10n-id','deselect-all');}else{this.checkUncheckAllButton.setAttribute('data-l10n-id','select-all');}
|
||||
if(selected.selectedCount){this.deleteButton.disabled=false;document.l10n.setAttributes(this.editMode,'selected-threads',{n:selected.selectedCount});var hasUnreadselected=selected.selectedList.some((id)=>{var thread=Threads.get(id);if(thread&&thread.unreadCount){return thread.unreadCount>0;}
|
||||
return false;});var allDraft=selected.selectedList.every((id)=>{return(Threads.get(id).isDraft);});if(allDraft){this.readUnreadButton.disabled=true;}else{if(!hasUnreadselected){this.readUnreadButton.dataset.action='mark-as-unread';}else{this.readUnreadButton.dataset.action='mark-as-read';}
|
||||
this.readUnreadButton.disabled=false;}}else{this.deleteButton.disabled=true;this.readUnreadButton.disabled=true;document.l10n.setAttributes(this.editMode,'selectThreads-title');}},markReadUnread:function inbox_markReadUnread(selected,isRead){selected.forEach((id)=>{var thread=Threads.get(+id);var readStatusMatched=!!thread.unreadCount^isRead;var markable=thread&&!thread.isDraft&&(isRead||!thread.getDraft());if(markable&&!readStatusMatched){thread.unreadCount=isRead?0:1;this.mark(thread.id,isRead?'read':'unread');MessageManager.markThreadRead(thread.id,isRead);}});this.cancelEdit();},removeConversationDOM(conversationId){var li=document.getElementById('thread-'+conversationId);var parent;var photoUrl=li&&li.dataset.photoUrl;if(photoUrl){window.URL.revokeObjectURL(photoUrl);}
|
||||
if(li){parent=li.parentNode;li.remove();}
|
||||
if(parent&&!parent.firstElementChild){parent.previousSibling.remove();parent.remove();this.sticky&&this.sticky.refresh();if(!this.container.querySelector('li')){this.setEmpty(true);}}},delete:function inbox_delete(selected){function performDeletion(){var threadIdsToDelete=[],messageIdsToDelete=[],threadCountToDelete=0;function exitEditMode(){InboxView.cancelEdit();WaitingScreen.hide();}
|
||||
function onAllThreadMessagesRetrieved(){if(!--threadCountToDelete){MessageManager.deleteMessages(messageIdsToDelete);threadIdsToDelete.forEach(function(threadId){InboxView.deleteThread(threadId);});messageIdsToDelete=threadIdsToDelete=null;exitEditMode();}}
|
||||
function onThreadMessageRetrieved(message){messageIdsToDelete.push(message.id);return true;}
|
||||
WaitingScreen.show();threadIdsToDelete=selected.reduce(function(list,value){var threadId=+value;if(Threads.get(threadId).isDraft){InboxView.deleteThread(threadId);}else{list.push(threadId);}
|
||||
return list;},[]);if(!threadIdsToDelete.length){exitEditMode();return;}
|
||||
threadCountToDelete=threadIdsToDelete.length;threadIdsToDelete.forEach(function(threadId){MessageManager.getMessages({filter:{threadId:threadId},each:onThreadMessageRetrieved,end:onAllThreadMessagesRetrieved});});}
|
||||
return Utils.confirm({id:'deleteThreads-confirmation-message',args:{n:selected.length}},null,{text:'delete',className:'danger'}).then(performDeletion.bind(this));},setEmpty:function inbox_setEmpty(empty){var panel=document.querySelector('.panel-InboxView');panel.classList.toggle('threadlist-is-empty',!!empty);},showOptions:function inbox_options(){var params={items:[{l10nId:'selectThreads-label',method:this.startEdit.bind(this)},{l10nId:'settings',method:function oSettings(){ActivityPicker.openSettings();}},{l10nId:'cancel',incomplete:true}]};new OptionMenu(params).show();},startEdit:function inbox_edit(){function editModeSetup(){this.inEditMode=true;this.selectionHandler.cleanForm();this.mainWrapper.classList.toggle('edit');}
|
||||
this.editHeader.removeAttribute('no-font-fit');if(!this.selectionHandler){LazyLoader.load(['/views/shared/js/selection_handler.js','/shared/style/edit_mode.css','/shared/style/switches.css','/views/shared/style/edit-mode.css','/shared/style/tabs.css','/views/inbox/style/edit-mode.css'],()=>{this.selectionHandler=new SelectionHandler({container:this.container,checkUncheckAllButton:this.checkUncheckAllButton,updateSelectionStatus:this.updateSelectionStatus.bind(this),getIdIterator:this.getIdIterator.bind(this),isInEditMode:this.isInEditMode.bind(this)});this.editForm.classList.remove('hide');editModeSetup.call(this);});}else{editModeSetup.call(this);}},isInEditMode:function inbox_isInEditMode(){return this.inEditMode;},cancelEdit:function inbox_cancelEdit(){this.inEditMode=false;this.mainWrapper.classList.remove('edit');},renderDrafts:function inbox_renderDrafts(){return Drafts.request().then(()=>{for(var draft of Drafts.getAll()){if(draft.threadId){if(document.getElementById(`thread-${draft.threadId}`)){this.updateThread(Threads.get(draft.threadId),{conversationDraft:true});}}else{this.setEmpty(false);if(!Threads.has(draft.id)){var thread=Thread.create(draft);Threads.set(draft.id,thread);this.appendThread(thread);}}}
|
||||
this.sticky&&this.sticky.refresh();});},prepareRendering:function inbox_prepareRendering(){this.container.innerHTML='';this.renderDrafts();},startRendering:function inbox_startRenderingThreads(){this.setEmpty(false);},finalizeRendering:function inbox_finalizeRendering(empty){if(empty){this.setEmpty(true);}
|
||||
if(!empty){TimeHeaders.updateAll('header[data-time-update]');}
|
||||
this.sticky&&this.sticky.refresh();},ensureReadAheadSetting:function inbox_ensureReadAheadSettting(){Settings.setReadAheadThreadRetrieval(this.FIRST_PANEL_THREAD_COUNT);},renderThreads:function inbox_renderThreads(){window.performance.mark('willRenderThreads');var hasThreads=false;var firstPanelCount=this.FIRST_PANEL_THREAD_COUNT;this.prepareRendering();function onRenderThread(thread){Threads.set(thread.id,thread);if(!hasThreads){hasThreads=true;this.startRendering();}
|
||||
this.appendThread(thread);if(--firstPanelCount===0){this.emit('visually-loaded');}}
|
||||
function onThreadsRendered(){this.finalizeRendering(!(hasThreads||Drafts.size));if(firstPanelCount>0){this.emit('visually-loaded');}}
|
||||
MessageManager.getThreads({each:onRenderThread.bind(this),end:onThreadsRendered.bind(this),done:()=>Drafts.request().then(()=>this.emit('fully-loaded'))});},updateConversationContent(node,record){var id=record.id;var thread=Threads.get(id);var isDraft=thread.isDraft;var draft=thread.getDraft();var body=record.body;var type=record.lastMessageType;var iconLabel='';if(draft&&draft.timestamp>=record.timestamp){body=draft.content.find((content)=>typeof content==='string');type=draft.type;}
|
||||
node.dataset.lastMessageType=type;if(draft){node.classList.add('draft');if(isDraft){node.dataset.draftId=draft.id;node.classList.add('is-draft');iconLabel='is-draft';}else{node.classList.add('has-draft');iconLabel='has-draft';}}else{node.classList.remove('draft','has-draft','is-draft');}
|
||||
if(record.unreadCount>0){node.classList.add('unread');iconLabel='unread-thread';}else{node.classList.remove('unread');}
|
||||
node.querySelector('.js-conversation-body').textContent=body||'';var stateIndicator=node.querySelector('.js-conversation-state');if(iconLabel){document.l10n.setAttributes(stateIndicator,iconLabel);}else{stateIndicator.removeAttribute('data-l10n-id');}},createThread:function inbox_createThread(record){var li=document.createElement('li');var timestamp=+record.timestamp;var id=record.id;var thread=Threads.get(id);var isDraft=thread.isDraft;var draft=thread.getDraft();li.id='thread-'+id;li.dataset.threadId=id;li.dataset.time=timestamp;li.classList.add('threadlist-item');li.innerHTML=this.tmpl.thread.interpolate({hash:isDraft?'#/composer':'#/thread?id='+id,mode:isDraft?'drafts':'threads',id:String(isDraft?draft.id:id),timestamp:String(timestamp)},{safe:['id']});this.updateConversationContent(li,record);TimeHeaders.update(li.querySelector('time'));return li;},deleteThread:function(threadId){Threads.delete(threadId);this.removeConversationDOM(threadId);Utils.closeNotificationsForThread(threadId);},insertThreadContainer:function inbox_insertThreadContainer(group,timestamp){var headers=InboxView.container.getElementsByTagName('header');var groupFound=false;for(var i=0;i<headers.length;i++){if(timestamp>=headers[i].dataset.time){groupFound=true;InboxView.container.insertBefore(group,headers[i].parentNode);break;}}
|
||||
if(!groupFound){InboxView.container.appendChild(group);}},updateThread(record,options={}){var thread=Thread.create(record,options);var threadUINode=document.getElementById('thread-'+thread.id);var threadUITime=threadUINode?+threadUINode.dataset.time:NaN;var recordTime=+thread.timestamp;Threads.set(thread.id,thread);var newMessageReceived=options.unread;if(newMessageReceived&&threadUITime>recordTime){this.mark(thread.id,'unread');return;}
|
||||
if(options.deleted&&threadUITime===recordTime){return;}
|
||||
if(threadUINode&&options.conversationDraft){this.updateConversationContent(threadUINode,record);return;}
|
||||
if(threadUINode){this.removeConversationDOM(thread.id);}
|
||||
this.setEmpty(false);if(this.appendThread(thread)){this.sticky&&this.sticky.refresh();}},onMessageSending:function inbox_onMessageSending(e){this.updateThread(e.message);},onMessageReceived:function inbox_onMessageReceived(e){var markAsRead=Navigation.isCurrentPanel('thread',{id:e.message.threadId});this.updateThread(e.message,{unread:!markAsRead});},onThreadsDeleted:function inbox_onThreadDeleted(e){e.ids.forEach(function(threadId){if(Threads.has(threadId)){this.deleteThread(threadId);}},this);},appendThread:function inbox_appendThread(thread){var timestamp=+thread.timestamp;var firstThreadInContainer=false;var node=this.createThread(thread);this.setContact(node);var threadsContainerID='threadsContainer_'+
|
||||
Utils.getDayDate(timestamp);var threadsContainer=document.getElementById(threadsContainerID);if(!threadsContainer){var threadsContainerWrapper=InboxView.createThreadContainer(timestamp);threadsContainer=threadsContainerWrapper.childNodes[1];InboxView.insertThreadContainer(threadsContainerWrapper,timestamp);firstThreadInContainer=true;}
|
||||
var threads=threadsContainer.getElementsByTagName('li');var threadFound=false;for(var i=0,l=threads.length;i<l;i++){if(timestamp>threads[i].dataset.time){threadFound=true;threadsContainer.insertBefore(node,threads[i]);break;}}
|
||||
if(!threadFound){threadsContainer.appendChild(node);}
|
||||
if(this.inEditMode){this.selectionHandler.unselect(thread.id);this.updateSelectionStatus();}
|
||||
return firstThreadInContainer;},createThreadContainer:function inbox_createThreadContainer(timestamp){var threadContainer=document.createElement('div');var headerDOM=document.createElement('header');headerDOM.id='header_'+timestamp;headerDOM.dataset.timeUpdate='repeat';headerDOM.dataset.time=timestamp;headerDOM.dataset.dateOnly=true;var threadsContainerDOM=document.createElement('ul');threadsContainerDOM.id='threadsContainer_'+
|
||||
Utils.getDayDate(timestamp);Utils.setHeaderDate({time:timestamp,element:headerDOM});threadContainer.appendChild(headerDOM);threadContainer.appendChild(threadsContainerDOM);return threadContainer;},updateContactsInfo:function inbox_updateContactsInfo(){Contacts.clearUnknown();if(!this.container){return;}
|
||||
var threads=this.container.getElementsByTagName('li');[].forEach.call(threads,this.setContact.bind(this));},mark:function inbox_mark(id,current){var li=document.getElementById('thread-'+id);if(li){li.classList.toggle('unread',current==='unread');}},onDraftDeleted:function inbox_onDraftDeleted(draft){var thread=Threads.get(draft.threadId||draft.id);if(!thread){return;}
|
||||
if(thread.isDraft){this.deleteThread(thread.id);}else{this.updateThread(thread,{conversationDraft:true});}
|
||||
if(draft.id===this.notifyAboutSavedDraftWithId){this.notifyAboutSavedDraftWithId=null;}},onDraftSaved:function inbox_onDraftSaved(draft){if(draft.threadId){this.updateThread(Threads.get(draft.threadId),{conversationDraft:true});}else{this.updateThread(draft);}
|
||||
if(!Navigation.isCurrentPanel('thread-list')){this.notifyAboutSavedDraftWithId=draft.id;}},showDraftSavedBanner:function(){this.draftSavedBanner.classList.remove('hide');clearTimeout(this.timeouts.onDraftSaved);this.timeouts.onDraftSaved=null;this.timeouts.onDraftSaved=setTimeout(()=>this.draftSavedBanner.classList.add('hide'),this.DRAFT_SAVED_DURATION);}};exports.InboxView=EventDispatcher.mixin(InboxView,['visually-loaded','fully-loaded']);}(this));
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;(function(exports){'use strict';const LAZY_DEPENDENCIES=['/shared/js/option_menu.js','/views/shared/js/waiting_screen.js','/views/shared/js/dialog.js','/views/shared/js/error_dialog.js','/views/shared/js/activity_picker.js'];function initLazyDependencies(){return LazyLoader.load(LAZY_DEPENDENCIES).then(()=>{TimeHeaders.init();Settings.init();Navigation.setReady();InterInstanceEventDispatcher.connect();});}
|
||||
exports.Startup={init(){Utils.initializeShimHost(App.instanceId);ConversationClient.init(App.instanceId);MessageManager.init();Navigation.init();Drafts.init();InboxView.init();InboxView.once('visually-loaded',()=>{initLazyDependencies();});InboxView.once('fully-loaded',()=>{App.setReady();});InboxView.renderThreads();}};})(window);
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* This file contains styles for Inbox view edit mode. They complement styles
|
||||
* from views/shared/style/edit-mode.css that are applied as well.
|
||||
* View edit mode is activated via Options -> Select threads.
|
||||
*/
|
||||
|
||||
|
||||
.bb-tablist > li button[data-action="mark-as-read"] {
|
||||
background-image: url("images/icons/ico_in_markasread_30x30.png");
|
||||
}
|
||||
|
||||
.bb-tablist > li button[data-action="mark-as-unread"] {
|
||||
background-image: url("images/icons/ico_in_markasunread_30x30.png");
|
||||
}
|
||||
|
||||
.edit #threads-container[data-type="list"] {
|
||||
height: calc(100% - 10rem);
|
||||
}
|
||||
|
||||
html[dir="ltr"] .edit [data-type="list"] li > a > aside.icon-unread {
|
||||
transform: translateX(5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit [data-type="list"] li > a > aside.icon-unread {
|
||||
transform: translateX(-5rem);
|
||||
}
|
||||
|
||||
.edit #threads-container aside.pack-end {
|
||||
margin-inline-start: 4rem;
|
||||
}
|
||||
|
||||
html[dir="ltr"] .edit #threads-container[data-type="list"] aside.pack-end span {
|
||||
transform: translateX(1.5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit #threads-container[data-type="list"] aside.pack-end span {
|
||||
transform: translateX(-1.5rem);
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 160 B |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 779 B |
|
After Width: | Height: | Size: 131 B |
191
assets/webapps/sms.gaiamobile.org/views/inbox/style/inbox.css
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#sticky {
|
||||
position: absolute;
|
||||
|
||||
/* The sticky element is positioned at the beginning of the list. */
|
||||
top: 5rem;
|
||||
|
||||
/* The width is not 100% so the scrollbar is visible. */
|
||||
width: calc(100% - 2rem);
|
||||
|
||||
/* The height needs to be kept in sync with the header height. */
|
||||
height: 3.1rem;
|
||||
|
||||
/* Leaving 1rem margin for both side to makes scrollbar visible in LTR/RTL */
|
||||
margin: 0 1rem;
|
||||
|
||||
/* The sticky element needs to be above the list. */
|
||||
z-index: 2;
|
||||
|
||||
/* Hide sticky header when content is not ready */
|
||||
visibility: hidden;
|
||||
|
||||
/* Those style will affect the backgroundImage: -moz-element. */
|
||||
background: white no-repeat center top;
|
||||
}
|
||||
|
||||
#sticky.has-content {
|
||||
/* Set sticky header visible only when content is ready */
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#threads-container[data-type="list"] header {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#threads-container[data-type="list"] ul {
|
||||
padding: 0 1.0rem;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#threads-container[data-type="list"] .mms-icon {
|
||||
display: none;
|
||||
|
||||
font-size: 1.5rem;
|
||||
color: #ff6622;
|
||||
}
|
||||
|
||||
#threads-container[data-type="list"] [data-last-message-type="mms"] .mms-icon {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#threads-container[data-type="list"] [data-last-message-type="mms"] .body-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.threadlist-item-picture {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.threadlist-item-picture.has-picture {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.threadlist-item .default-picture > span[data-type=img],
|
||||
.threadlist-item .group-picture > span[data-type=img] {
|
||||
/* Please keep this image in sync with what is used in js/inbox.js */
|
||||
background: url('images/default_contact_image.png') top left; /* BiDi proof: CSS sprites */
|
||||
}
|
||||
|
||||
.threadlist-item:nth-child(even) .default-picture > span[data-type=img],
|
||||
.threadlist-item:nth-child(even) .group-picture > span[data-type=img] {
|
||||
background-position: bottom right; /* BiDi proof: CSS sprites */
|
||||
}
|
||||
|
||||
.threadlist-item:nth-child(3n+1) .default-picture > span[data-type=img],
|
||||
.threadlist-item:nth-child(3n+1) .group-picture > span[data-type=img] {
|
||||
background-position: top right; /* BiDi proof: CSS sprites */
|
||||
}
|
||||
|
||||
.threadlist-item:nth-child(4n+4) .default-picture > span[data-type=img],
|
||||
.threadlist-item:nth-child(4n+4) .group-picture > span[data-type=img] {
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.threadlist-item-picture.group-picture > span[data-type=img] {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
color: #fff;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 967878, 979018 -[Messages] Background doesn't
|
||||
* scroll with the threads causing expensive over-invalidation
|
||||
*/
|
||||
#threads-container > div {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[data-type="list"] aside.icon-unread {
|
||||
margin: 0;
|
||||
width: 1.9rem;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
[data-type="list"] .unread aside.icon-unread {
|
||||
background: url("images/unread.png") no-repeat;
|
||||
background-size: 1rem;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
[data-type="list"] .threadlist-item a * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[data-type="list"] .summary {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[data-type="list"] .draft .icon-draft {
|
||||
background: url("images/draft.png") no-repeat;
|
||||
background-size: 1.2rem;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
[data-type="list"] .is-draft .pack-end {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
[data-type="list"] .is-draft .threadlist-item-title {
|
||||
font-style: italic;
|
||||
color: #5f5f5f;
|
||||
}
|
||||
|
||||
/* Override time margin for flex layout */
|
||||
[data-type="list"] .summary time {
|
||||
display: inline-block;
|
||||
margin-inline-end: 1rem;
|
||||
}
|
||||
|
||||
[data-type="list"] .unread .summary time {
|
||||
color: #1D8399;
|
||||
}
|
||||
|
||||
/* Empty Inbox layout */
|
||||
|
||||
#threads-no-messages {
|
||||
background: url("images/SMS_200x200_bubble.png")
|
||||
no-repeat center;
|
||||
background-size: 20rem;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: calc(100% - 5rem);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#no-result-message {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
|
||||
width: 27rem;
|
||||
padding: 2rem 0.5rem;
|
||||
margin: -8rem auto;
|
||||
border-top: .1rem solid rgba(96,96,96,.5);
|
||||
|
||||
line-height: 3rem;
|
||||
text-align: start;
|
||||
font-weight: 300;
|
||||
font-size: 2.2rem;
|
||||
color: #606060;
|
||||
}
|
||||
|
||||
#threads-no-messages, .threadlist-is-empty #threads-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Using !important to override scoped style in gaia-header component. */
|
||||
#threads-settings-button, .threadlist-is-empty #threads-options-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Using flex to center the content. */
|
||||
.threadlist-is-empty #threads-settings-button {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.threadlist-is-empty #threads-no-messages {
|
||||
display: block;
|
||||
}
|
||||
|
After Width: | Height: | Size: 340 B |
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
Styles for new message
|
||||
*/
|
||||
|
||||
section[role="region"].new > gaia-header h1 {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.new .article-list {
|
||||
z-index: 5;
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.new .flex-composer {
|
||||
position: relative;
|
||||
top: 4rem; /* 4rem is the default size of the recipient panel */
|
||||
|
||||
height: calc(100% - 4rem);
|
||||
}
|
||||
|
||||
.subheader menu {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: 5rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
/* Render this container outside of the document flow so that it does not
|
||||
* effect the height of the containing subheader, and therefore does not effect
|
||||
* the height of the Compose field. */
|
||||
.subheader .overlay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.multiline-recipients-mode.new .flex-composer {
|
||||
top: 5.5rem;
|
||||
|
||||
height: calc(100% - 5.5rem);
|
||||
}
|
||||
|
||||
.new #messages-container {
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
/* TODO: Remove this once panel markup split */
|
||||
#messages-contact-pick-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.new #messages-contact-pick-button {
|
||||
display: block;
|
||||
|
||||
width: 5rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
#messages-contact-pick-button .icon-user {
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
offset-inline-end: 1rem;
|
||||
|
||||
display: block;
|
||||
width: 2.8rem;
|
||||
height: 2.8rem;
|
||||
border-radius: 50%;
|
||||
|
||||
color: #575757;
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
#messages-contact-pick-button .icon-user:after {
|
||||
content: "+";
|
||||
position: absolute;
|
||||
|
||||
display: block;
|
||||
width: 2.8rem;
|
||||
height: 2.8rem;
|
||||
|
||||
line-height: 2.8rem;
|
||||
font-size: 2.1rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#messages-contact-pick-button .icon-user:active {
|
||||
color: #FFF;
|
||||
background-color: #29bbb5;
|
||||
}
|
||||
|
||||
section[role="region"].new .subheader {
|
||||
/* The subheader contains the recipients part and because of the fact that it
|
||||
* has "overflow: visible" we can see it. We should revise it.
|
||||
*/
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/** To be seen below the To: field when we are creating a new message **/
|
||||
.new .overlay {
|
||||
/* recipient's panel default size; be careful as this value depends on To:
|
||||
* field height */
|
||||
top: 4rem;
|
||||
}
|
||||
|
||||
.new.multiline-recipients-mode .overlay {
|
||||
top: 5.5rem;
|
||||
}
|
||||
|
||||
#messages-recipient-suggestions {
|
||||
position: absolute;
|
||||
/* 9rem (5 rem from app header + 4rem from "To" panel heights) */
|
||||
top: 9rem;
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
max-height: calc(100% - 9rem);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recipient-suggestions-list {
|
||||
border-bottom: 0.1rem solid #E7E7E7;
|
||||
}
|
||||
|
||||
.multiline-recipients-mode #messages-recipient-suggestions {
|
||||
top: 10.5rem;
|
||||
|
||||
max-height: calc(100% - 10.5rem);
|
||||
}
|
||||
|
||||
#messages-recipient-suggestions li {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
html[dir="ltr"] [dir] { text-align: left; }
|
||||
html[dir="rtl"] [dir] { text-align: right; }
|
||||
|
||||
#messages-to-field {
|
||||
position: relative;
|
||||
/* To be above .article-list's with z-index = 5 */
|
||||
z-index: 6;
|
||||
|
||||
display: none;
|
||||
min-height: 5.5rem;
|
||||
|
||||
color: black;
|
||||
font-size: 1.465rem;
|
||||
background-color: #f2f2f2;
|
||||
transition: transform 0.2s ease-in-out 0s;
|
||||
transform: translateY(calc(-100% + 4rem));
|
||||
}
|
||||
|
||||
.multiline-recipients-mode #messages-to-field {
|
||||
transform: translateY(calc(-100% + 5.5rem));
|
||||
}
|
||||
|
||||
section[role="region"].new #messages-to-field {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
section[role="region"].new #messages-to-field.multiline {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
#to-label {
|
||||
flex-shrink: 0;
|
||||
|
||||
height: 4rem;
|
||||
padding-inline-start: 1.5rem;
|
||||
padding-inline-end: 0.7rem;
|
||||
|
||||
line-height: 4rem;
|
||||
font-weight: 200;
|
||||
font-size: 1.465rem;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#messages-recipients-list-container {
|
||||
min-height: 4rem;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#messages-recipients-list {
|
||||
box-sizing: border-box;
|
||||
min-height: 4rem;
|
||||
padding-top: 0;
|
||||
padding-inline-end: 0.6rem;
|
||||
padding-bottom: 0.6rem;
|
||||
padding-inline-start: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Used by Recipients.View to display multi or single line.
|
||||
Do not remove.
|
||||
*/
|
||||
#messages-recipients-list.singleline {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#messages-recipients-list.multiline {
|
||||
/* max-height = viewport - header - minimum composer - list margin */
|
||||
max-height: calc(100vh - 5rem - 4rem - 0.6rem);
|
||||
margin-bottom: 0.6rem;
|
||||
padding-bottom: 0;
|
||||
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* BiDi note: all [contenteditable] blocks must carry a `dir="auto"` attribute,
|
||||
* so we cannot use *-inline-{start,end} properties on them, because the
|
||||
* direction of a dir="auto" element (and therefore the position of start/end)
|
||||
* depends on its content. We want our margin/padding to depend on the document
|
||||
* direction, not element's content.
|
||||
*/
|
||||
.recipient[contenteditable] {
|
||||
overflow: hidden;
|
||||
margin: 0.6rem 0 0;
|
||||
|
||||
display: block;
|
||||
|
||||
/* override BB selector */
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
html[dir="ltr"] .recipient[contenteditable] { margin-right: 0.6rem; }
|
||||
html[dir="rtl"] .recipient[contenteditable] { margin-left: 0.6rem; }
|
||||
|
||||
.recipient[contenteditable].email {
|
||||
background: #fff url(images/icons/icon_sms_compose_email.png) no-repeat;
|
||||
background-size: 1.7rem;
|
||||
}
|
||||
/* see BiDi note above */
|
||||
html[dir="ltr"] .recipient[contenteditable].email {
|
||||
padding-left: 3.8rem;
|
||||
background-position: left 1.5rem center;
|
||||
}
|
||||
html[dir="rtl"] .recipient[contenteditable].email {
|
||||
padding-right: 3.8rem;
|
||||
background-position: right 1.5rem center;
|
||||
}
|
||||
|
||||
#messages-recipients-list > .recipient[contenteditable=true] {
|
||||
flex-grow: 1;
|
||||
height: 2.8rem;
|
||||
line-height: 2.8rem;
|
||||
border-radius: 0;
|
||||
|
||||
word-wrap: unset;
|
||||
|
||||
/* Building blocks override (bug xxx) */
|
||||
background: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#messages-recipients-list > .recipient[contenteditable=true]:focus {
|
||||
/**
|
||||
* To workaround bug 1103011, we just collapse editable recipient on focus so
|
||||
* that caret position looks correct for the user, while in fact it is not
|
||||
* true - caret is still in wrong position, but zero-width editable element is
|
||||
* aligned correctly inside parent.
|
||||
*/
|
||||
flex-grow: unset;
|
||||
}
|
||||
|
||||
.recipient[contenteditable=true]:empty:not(:last-of-type) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable padding and give negative margin-end on empty contenteditable
|
||||
* recipients when they do not have focus. This allows the Recipients List
|
||||
* to intelligently collapse when the final row contains no "assimilated"
|
||||
* (i.e. non-contenteditable) recipients. */
|
||||
.recipient[contenteditable=true]:empty {
|
||||
padding: 0;
|
||||
}
|
||||
/**
|
||||
* We don't use margin-inline-end here because of the bug 1103011, as currently
|
||||
* directionality of empty contenteditable isn't inherited from parent, but
|
||||
* always LTR, so margin-inline-end equals to margin-right in both LTR and RTL
|
||||
* modes. So to align it correctly we should override margin depending on text
|
||||
* direction of parent element manually.
|
||||
*/
|
||||
html[dir="ltr"] .recipient[contenteditable=true]:empty {
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
html[dir="rtl"] .recipient[contenteditable=true]:empty {
|
||||
margin-left: -0.5rem;
|
||||
}
|
||||
|
||||
/* see BiDi note above */
|
||||
html[dir="ltr"] .recipient[contenteditable=true]:focus,
|
||||
html[dir="ltr"] .recipient[contenteditable=true]:hover,
|
||||
html[dir="ltr"] .recipient[contenteditable=true]:empty:focus {
|
||||
margin-right: 0.5rem;
|
||||
padding-right: 0.3rem;
|
||||
}
|
||||
html[dir="rtl"] .recipient[contenteditable=true]:focus,
|
||||
html[dir="rtl"] .recipient[contenteditable=true]:hover,
|
||||
html[dir="rtl"] .recipient[contenteditable=true]:empty:focus {
|
||||
margin-left: 0.5rem;
|
||||
padding-left: 0.3rem;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/*global ActivityClient,
|
||||
App,
|
||||
Attachment,
|
||||
Draft,
|
||||
Drafts,
|
||||
MessageManager,
|
||||
Navigation,
|
||||
Settings,
|
||||
Utils
|
||||
*/
|
||||
/*exported ActivityHandler */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Describes available data types that can be associated with the activities.
|
||||
* @enum {string}
|
||||
*/
|
||||
const ActivityDataType = {
|
||||
IMAGE: 'image/*',
|
||||
AUDIO: 'audio/*',
|
||||
VIDEO: 'video/*',
|
||||
URL: 'url',
|
||||
VCARD: 'text/vcard'
|
||||
};
|
||||
|
||||
var ActivityHandler = {
|
||||
init: function() {
|
||||
ActivityClient.init(App.instanceId);
|
||||
|
||||
ActivityClient.on(
|
||||
'new-activity-request', this._onNewActivity.bind(this)
|
||||
);
|
||||
|
||||
ActivityClient.on(
|
||||
'share-activity-request', this._onShareActivity.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
_onNewActivity: function newHandler(activityData) {
|
||||
var viewInfo = {
|
||||
body: activityData.body,
|
||||
number: activityData.target || activityData.number,
|
||||
threadId: null
|
||||
};
|
||||
|
||||
var threadPromise;
|
||||
var focus = '';
|
||||
if (viewInfo.number) {
|
||||
// It's reasonable to focus on composer if we already have some phone
|
||||
// number or even contact to fill recipients input
|
||||
focus = 'composer';
|
||||
|
||||
/* If we have a body, we're always redirecting to the new message view,
|
||||
* because we don't want to overwrite any possible draft from an existing
|
||||
* conversation. */
|
||||
if (!viewInfo.body) {
|
||||
// try to get a thread from number
|
||||
// if no thread, promise is rejected and we try to find a contact
|
||||
threadPromise = MessageManager.findThreadFromNumber(viewInfo.number)
|
||||
.then((threadId) => viewInfo.threadId = threadId)
|
||||
// case no contact and no thread id: gobble the error
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
return (threadPromise || Promise.resolve()).then(
|
||||
() => this.toView(viewInfo, focus)
|
||||
);
|
||||
},
|
||||
|
||||
_onShareActivity: function shareHandler(activityData) {
|
||||
var dataToShare = null;
|
||||
|
||||
switch(activityData.type) {
|
||||
case ActivityDataType.AUDIO:
|
||||
case ActivityDataType.VIDEO:
|
||||
case ActivityDataType.IMAGE:
|
||||
var attachments = activityData.blobs.map(function(blob, idx) {
|
||||
var attachment = new Attachment(blob, {
|
||||
name: activityData.filenames[idx],
|
||||
isDraft: true
|
||||
});
|
||||
|
||||
return attachment;
|
||||
});
|
||||
|
||||
var size = attachments.reduce(function(size, attachment) {
|
||||
if (attachment.type !== 'img') {
|
||||
size += attachment.size;
|
||||
}
|
||||
|
||||
return size;
|
||||
}, 0);
|
||||
|
||||
if (size > Settings.mmsSizeLimitation) {
|
||||
Utils.alert({
|
||||
id: 'attached-files-too-large',
|
||||
args: {
|
||||
n: activityData.blobs.length,
|
||||
mmsSize: (Settings.mmsSizeLimitation / 1024).toFixed(0)
|
||||
}
|
||||
}).then(() => ActivityClient.postResult());
|
||||
return;
|
||||
}
|
||||
|
||||
dataToShare = attachments;
|
||||
break;
|
||||
case ActivityDataType.URL:
|
||||
dataToShare = activityData.url;
|
||||
break;
|
||||
// As for the moment we only allow to share one vcard we treat this case
|
||||
// in an specific block
|
||||
case ActivityDataType.VCARD:
|
||||
dataToShare = new Attachment(activityData.blobs[0], {
|
||||
name: activityData.filenames[0],
|
||||
isDraft: true
|
||||
});
|
||||
break;
|
||||
default:
|
||||
ActivityClient.postError(
|
||||
'Unsupported activity data type: ' + activityData.type
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dataToShare) {
|
||||
ActivityClient.postError('No data to share found!');
|
||||
return;
|
||||
}
|
||||
|
||||
return this.toView({ body: dataToShare });
|
||||
},
|
||||
|
||||
/**
|
||||
* Store a draft for this message, and returns a draftId.
|
||||
*
|
||||
* @param {Object} message Message to save
|
||||
* @param {Number} message.threadId Conversation where this messages is
|
||||
* located.
|
||||
* @param {String} message.number The recipient as a phone number or email
|
||||
* address.
|
||||
* @param {(String|Attachment|Array.<Attachment>)} [message.body] The data to
|
||||
* add to the message's body.
|
||||
* @returns {Promise.<Number>} The new draft id.
|
||||
*/
|
||||
_storeDraftFromMessage(message) {
|
||||
var content = message.body || null;
|
||||
|
||||
var isSimpleContent = !content || typeof content === 'string';
|
||||
var isEmailAddress = message.number && Utils.isEmailAddress(message.number);
|
||||
|
||||
var draft = new Draft({
|
||||
threadId: message.threadId || null,
|
||||
recipients: message.number && [message.number] || null,
|
||||
content: !content || Array.isArray(content) ? content : [content],
|
||||
type: isSimpleContent && !isEmailAddress ? 'sms' : 'mms'
|
||||
});
|
||||
|
||||
return Drafts.request().then(
|
||||
() => Drafts.add(draft).store()
|
||||
).then(() => draft.id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delivers the user to the correct view based on the params provided in the
|
||||
* "message" parameter.
|
||||
* @param {{number: string, body: string, threadId: number}} message It's
|
||||
* either a message object that belongs to a thread, or a message object from
|
||||
* the system. "number" is a string phone number to pre-populate the
|
||||
* recipients list with, "body" is an optional body to preset the compose
|
||||
* input with, "threadId" is an optional threadId corresponding to a new or
|
||||
* existing thread.
|
||||
* @param {string?} focus indicates which of 'composer' or 'recipients' field
|
||||
* we need to focus when we navigate to Thread panel.
|
||||
*/
|
||||
toView: function ah_toView(message, focus) {
|
||||
return Utils.onceDocumentIsVisible().then(() => {
|
||||
// If we have appropriate thread then let's forward user there, otherwise
|
||||
// open new message composer.
|
||||
if (message.threadId) {
|
||||
return Navigation.toPanel('thread', {
|
||||
id: message.threadId,
|
||||
focus
|
||||
});
|
||||
}
|
||||
|
||||
return this._storeDraftFromMessage(message).then(
|
||||
(draftId) => Navigation.toPanel('composer', { draftId, focus })
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*global MozActivity */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
function handleActivity(activity, onsuccess, onerror) {
|
||||
// Note: the MozActivity is intentionally constructed in the caller and
|
||||
// passed down to this function in order to make it possible to pass an
|
||||
// inline option to its constructor in the caller for the sake of static
|
||||
// analysis tools. Please do not change that!
|
||||
|
||||
if (typeof onsuccess === 'function') {
|
||||
activity.onsuccess = onsuccess;
|
||||
}
|
||||
|
||||
activity.onerror = typeof onerror === 'function' ? onerror : function(error) {
|
||||
console.warn('Unhandled error spawning activity; ' + error.message);
|
||||
};
|
||||
}
|
||||
|
||||
var ActivityPicker = {
|
||||
dial: function ap_call(number, onsuccess, onerror) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'dial',
|
||||
data: {
|
||||
type: 'webtelephony/number',
|
||||
number: number
|
||||
}
|
||||
}), onsuccess, onerror);
|
||||
},
|
||||
email: function ap_email(email, onsuccess, onerror) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'mail',
|
||||
URI: 'mailto:' + email
|
||||
}
|
||||
}), onsuccess, onerror);
|
||||
},
|
||||
url: function ap_browse(url, onsuccess, onerror) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: url
|
||||
}
|
||||
}), onsuccess, onerror);
|
||||
},
|
||||
createNewContact: function ap_createNewContact(contactProps) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: contactProps
|
||||
}
|
||||
}));
|
||||
},
|
||||
addToExistingContact: function ap_addToExistingContact(contactProps) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'update',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: contactProps
|
||||
}
|
||||
}));
|
||||
},
|
||||
viewContact: function ap_viewContact(contactProps) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: contactProps
|
||||
}
|
||||
}));
|
||||
},
|
||||
openSettings: function ap_openSettings(onsuccess, onerror) {
|
||||
handleActivity(new MozActivity({
|
||||
name: 'configure',
|
||||
data: {
|
||||
target: 'device',
|
||||
section: 'messaging'
|
||||
}
|
||||
}), onsuccess, onerror);
|
||||
}
|
||||
};
|
||||
|
||||
exports.ActivityPicker = ActivityPicker;
|
||||
|
||||
}(this));
|
||||
3
assets/webapps/sms.gaiamobile.org/views/shared/js/app.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
;;;(function(exports){'use strict';const APPLICATION_READY_CLASS_NAME='js-app-ready';const INSTANCE_ID=Date.now();var app={isReady:function app_isReady(){return document.body.classList.contains(APPLICATION_READY_CLASS_NAME);},setReady:function app_setReady(){if(this.isReady()){throw new Error('Application should be marked as ready only once!');}
|
||||
document.body.classList.add(APPLICATION_READY_CLASS_NAME);if(navigator.mozAudioChannelManager){navigator.mozAudioChannelManager.volumeControlChannel='notification';}},whenReady:function whenReady(){if(this.isReady()){return Promise.resolve();}
|
||||
return new Promise(function(resolve,reject){var bodyClassObserver=new MutationObserver(function(){try{if(this.isReady()){bodyClassObserver.disconnect();resolve();}}catch(e){reject(e);}}.bind(this));bodyClassObserver.observe(document.body,{attributes:true,attributeFilter:['class']});}.bind(this));},get instanceId(){return INSTANCE_ID;}};exports.App=app;})(this);
|
||||
1
assets/webapps/sms.gaiamobile.org/views/shared/js/bootstrap.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
;;(function(){'use strict';Startup.init();})(self);
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
/*global SharedComponents, Template, Utils, Settings */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
var FLAVORS = {
|
||||
/*
|
||||
* renderAll controls whether all phone numbers sohuld be rendered for this
|
||||
* contact
|
||||
*
|
||||
* shouldHighlight controls whether we should generate a markup allowing
|
||||
* highlighting depending on the input
|
||||
*
|
||||
* template "main" will get the following parameters:
|
||||
* - name: the contact's title
|
||||
* - number: the phone number
|
||||
* - nameHTML: same than name if shouldHighlight is false, otherwise contains
|
||||
* markup with the highlighting
|
||||
* - phoneDetailsHTML: phone details label (type, number, carrier), also if
|
||||
* shouldHighlight is true, then number is wrapped into highlighting markup
|
||||
* - photoHTML: result of the "photo" template, if present
|
||||
* - type: phone number type information
|
||||
* - carrier: phone number carrier information
|
||||
*
|
||||
* template "photo" will get the following parameters:
|
||||
* - photoURL: the URL of the contact's first photo
|
||||
*
|
||||
*/
|
||||
suggestion: {
|
||||
renderAll: true,
|
||||
shouldHighlight: true,
|
||||
templates: {
|
||||
main: 'contact-suggestion-tmpl'
|
||||
}
|
||||
},
|
||||
suggestionUnknown: {
|
||||
renderAll: true,
|
||||
shouldHighlight: true,
|
||||
templates: {
|
||||
main: 'contact-suggestion-unknown-tmpl'
|
||||
}
|
||||
},
|
||||
prompt: {
|
||||
renderAll: false,
|
||||
shouldHighlight: false,
|
||||
templates: {
|
||||
main: 'contact-prompt-tmpl',
|
||||
photo: 'contact-photo-tmpl'
|
||||
}
|
||||
},
|
||||
'group-view': {
|
||||
renderAll: false,
|
||||
shouldHighlight: false,
|
||||
templates: {
|
||||
main: 'contact-suggestion-tmpl',
|
||||
photo: 'contact-photo-tmpl'
|
||||
}
|
||||
},
|
||||
'report-view': {
|
||||
renderAll: false,
|
||||
shouldHighlight: false,
|
||||
templates: {
|
||||
main: 'contact-report-tmpl',
|
||||
photo: 'contact-photo-tmpl'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* let's lazy load generic templates */
|
||||
var templates = {};
|
||||
|
||||
function getTemplate(id) {
|
||||
if (!templates[id]) {
|
||||
templates[id] = Template(id);
|
||||
}
|
||||
return templates[id];
|
||||
}
|
||||
|
||||
var highlightTemplateId = 'contact-highlight-tmpl';
|
||||
|
||||
// Warning: always feed this function an already escaped data
|
||||
function highlight(escapedData, regexpArray) {
|
||||
// When rendering a suggestion, we highlight the matched substring.
|
||||
// The approach is to escape the html and the search string, and
|
||||
// then replace on all "words" (whitespace bounded strings) with
|
||||
// the substring run through the highlight template.
|
||||
|
||||
// this variable will allow skipping to the next regexp
|
||||
var matchFound;
|
||||
var template = getTemplate(highlightTemplateId);
|
||||
|
||||
function loopReplaceFn(match) {
|
||||
matchFound = true;
|
||||
// The match is safe, because splitData[i] is derived from
|
||||
// escapedData
|
||||
return template.interpolate({
|
||||
str: match
|
||||
}, {
|
||||
safe: ['str']
|
||||
});
|
||||
}
|
||||
|
||||
var splitData = escapedData.split(/\s+/);
|
||||
|
||||
// For each "word"
|
||||
for (var i = 0; i < splitData.length; i++) {
|
||||
matchFound = false;
|
||||
// Loop over search term regexes
|
||||
for (var k = 0; !matchFound && k < regexpArray.length; k++) {
|
||||
splitData[i] = splitData[i].replace(regexpArray[k], loopReplaceFn);
|
||||
}
|
||||
}
|
||||
return splitData.join(' ');
|
||||
}
|
||||
|
||||
var ContactRenderer = exports.ContactRenderer = function constructor(opts) {
|
||||
if (!opts) {
|
||||
throw new Error('ContactRenderer constructor needs an options object');
|
||||
}
|
||||
|
||||
this.opts = opts;
|
||||
this.templates = {};
|
||||
for (var key in opts.templates) {
|
||||
var id = opts.templates[key];
|
||||
this.templates[key] = Template(id);
|
||||
}
|
||||
|
||||
if (this.templates.photo) {
|
||||
this.opts.renderPhoto = true;
|
||||
}
|
||||
};
|
||||
|
||||
ContactRenderer.prototype = {
|
||||
// Returns true when a contact has been rendered
|
||||
// Returns false when no contact has been rendered
|
||||
render: function cr_render(renderOpts) {
|
||||
/**
|
||||
*
|
||||
* params {
|
||||
* contact:
|
||||
* A contact object.
|
||||
*
|
||||
* input:
|
||||
* Any input value associated with the contact,
|
||||
* possibly from a search or similar operation.
|
||||
*
|
||||
* skip:
|
||||
* An array of phone numbers that should not be rendered
|
||||
*
|
||||
* target:
|
||||
* Parent node to append the rendered node to
|
||||
*
|
||||
* infoBlock:
|
||||
* Node that will be appended to the node selected by
|
||||
* infoBlockParentSelector
|
||||
*
|
||||
* infoBlockParentSelector:
|
||||
* Parent element selector for appending infoBlock as child
|
||||
*
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
var contact = renderOpts.contact;
|
||||
var input = renderOpts.input;
|
||||
input = input && input.trim();
|
||||
var target = renderOpts.target;
|
||||
var shouldHighlight = this.opts.shouldHighlight;
|
||||
var renderAll = this.opts.renderAll;
|
||||
var renderPhoto = this.opts.renderPhoto;
|
||||
var skip = renderOpts.skip || [];
|
||||
var block = renderOpts.infoBlock;
|
||||
var parentSelector = renderOpts.infoBlockParentSelector;
|
||||
|
||||
// we can't do much without a contact
|
||||
if (!contact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't render if there is no phone number
|
||||
// TODO: Add email checking support for MMS
|
||||
var hasTel = contact.tel && contact.tel.length;
|
||||
var hasEmail = contact.email && contact.email.length;
|
||||
|
||||
if (!hasTel && !(Settings.supportEmailRecipient && hasEmail)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We search on the escaped HTML via a regular expression
|
||||
var escaped = Utils.escapeRegex(Template.escape(input));
|
||||
var escsubs = escaped.split(/\s+/);
|
||||
// Build a list of regexes used for highlighting suggestions
|
||||
var regexps = {
|
||||
name: escsubs.map(function(k) {
|
||||
// String matches occur on the beginning of a "word" to
|
||||
// maintain parity with the contact search algorithm which
|
||||
// only considers left aligned exact matches on words
|
||||
return new RegExp('^' + k, 'gi');
|
||||
}),
|
||||
number: escsubs.map(function(k) {
|
||||
// Match any of the search terms with the number
|
||||
return new RegExp(k, 'ig');
|
||||
}),
|
||||
email: escsubs.map(function(k) {
|
||||
// Match any of the search terms with the email
|
||||
return new RegExp('^' + k, 'gi');
|
||||
})
|
||||
};
|
||||
|
||||
var include = renderPhoto ? { photoURL: true } : null;
|
||||
var addresses = [];
|
||||
|
||||
if (contact.tel && contact.tel.length) {
|
||||
addresses = addresses.concat(contact.tel);
|
||||
}
|
||||
if (Settings.supportEmailRecipient &&
|
||||
contact.email && contact.email.length) {
|
||||
addresses = addresses.concat(contact.email);
|
||||
}
|
||||
var details = Utils.getContactDetails(
|
||||
addresses[0].value, contact, include
|
||||
);
|
||||
|
||||
var tempDiv = document.createElement('div');
|
||||
|
||||
addresses.forEach(function(current) {
|
||||
// Only render a contact's tel value entry for the _specified_
|
||||
// input value when not rendering all values. If the tel
|
||||
// record value _doesn't_ match, then continue.
|
||||
//
|
||||
if (!renderAll && !Utils.probablyMatches(current.value, input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If rendering for contact search result suggestions, don't
|
||||
// render contact tel records for values that are already
|
||||
// selected as recipients. This comparison should be safe,
|
||||
// as the value in this.recipients.numbers comes from the same
|
||||
// source that current.value comes from.
|
||||
if (renderAll && skip.indexOf(current.value) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = Utils.getDisplayObject(details.title, current);
|
||||
|
||||
var props = ['name', 'number'];
|
||||
if (Settings.supportEmailRecipient) {
|
||||
props.push('email');
|
||||
}
|
||||
|
||||
props.forEach(function(key) {
|
||||
var escapedData = Template.escape(data[key]);
|
||||
if (shouldHighlight) {
|
||||
escapedData = highlight(escapedData, regexps[key]);
|
||||
}
|
||||
|
||||
data[key + 'HTML'] = escapedData;
|
||||
});
|
||||
|
||||
data.phoneDetailsHTML = SharedComponents.phoneDetails({
|
||||
number: data.numberHTML,
|
||||
type: data.type,
|
||||
carrier: data.carrier
|
||||
}, {
|
||||
safe: ['number']
|
||||
}).toString();
|
||||
|
||||
// Render contact photo only for specific flavor
|
||||
data.photoHTML = renderPhoto && details.photoURL ?
|
||||
this.templates.photo.interpolate() : '';
|
||||
|
||||
// Interpolate HTML template with data and inject.
|
||||
// Known "safe" HTML values will not be re-sanitized.
|
||||
tempDiv.innerHTML = this.templates.main.interpolate(data, {
|
||||
safe: ['nameHTML', 'phoneDetailsHTML', 'srcAttr', 'photoHTML']
|
||||
});
|
||||
|
||||
var element = tempDiv.firstElementChild;
|
||||
var blockParent = element.querySelector(parentSelector);
|
||||
|
||||
if (blockParent) {
|
||||
blockParent.appendChild(block);
|
||||
}
|
||||
|
||||
if (data.photoHTML) {
|
||||
var contactPhoto = element.querySelector('.contact-photo');
|
||||
contactPhoto.style.backgroundImage =
|
||||
'url("' + encodeURI(details.photoURL) + '")';
|
||||
|
||||
Utils.asyncLoadRevokeURL(details.photoURL);
|
||||
}
|
||||
|
||||
target.appendChild(element);
|
||||
|
||||
tempDiv.textContent = '';
|
||||
}, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
ContactRenderer.flavor = function(flavor) {
|
||||
// we could possibly cache instances here, if we need more performance
|
||||
return new ContactRenderer(FLAVORS[flavor]);
|
||||
};
|
||||
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
;;;(function(exports){'use strict';var unknownNumbers=[];var filterFns={contains:function(a,b){a=a.toLowerCase();b=b.toLowerCase();return a.includes(b);},equality:function(a,b){a=a.toLowerCase();b=b.toLowerCase();return a===b;}};function isMatch(contact,criteria,filterFn){var found={};outer:for(var i=0,ilen=criteria.terms.length;i<ilen;i++){var term=criteria.terms[i];for(var j=0,jlen=criteria.fields.length;j<jlen;j++){var field=criteria.fields[j];if(!contact[field]){continue;}
|
||||
for(var k=0,klen=contact[field].length;k<klen;k++){var value=contact[field][k];if(typeof value.value!=='undefined'){value=value.value;}
|
||||
if((found[term]=filterFn(value.trim(),term))){continue outer;}}}}
|
||||
return Object.keys(found).every(function(key){return found[key];});}
|
||||
var rspaces=/\s+/;var Contacts={findBy:function contacts_findBy(filter,callback){var lower=[];var filterValue=(filter.filterValue||'').trim();var terms;if(!navigator.mozContacts||!filterValue.length){return Promise.resolve([]);}
|
||||
terms=filterValue.split(rspaces);filter.filterValue=terms.length===1?terms[0]:terms.reduce(function(initial,term){lower.push(term.toLowerCase());return term.length>initial.length?term:initial;},'');if(filter.filterValue.length<3){filter.filterLimit=10;}
|
||||
lower.splice(lower.indexOf(filter.filterValue.toLowerCase()),1);lower.push.apply(lower,terms);return navigator.mozContacts.find(filter).then((contacts)=>{var results;if(terms.length>1){var fields=['tel','givenName','familyName'];if(Settings.supportEmailRecipient){fields.push('email');}
|
||||
var criteria={fields:fields,terms:lower};results=contacts.filter((contact)=>isMatch(contact,criteria,filterFns.contains));}else{results=contacts;}
|
||||
return results;}).catch((e)=>{console.error('Got an error while looking up contacts.',e.name,e);return[];});},findByString:function contacts_findByString(filterValue){return Promise.all([this.findContactByString(filterValue),this.findByUnknown(filterValue)]).then(([known,unknown])=>{return(known||[]).concat(unknown);});},findContactByString:function contacts_findContactByString(filterValue){var props=['tel','givenName','familyName'];if(Settings.supportEmailRecipient){props.push('email');}
|
||||
return this.findBy({filterBy:props,filterOp:'contains',filterValue:filterValue});},findByUnknown:function contacts_findByUnknown(filterValue){var list=[];for(var i=0,length=unknownNumbers.length;i<length;i++){if(list.length>2){break;}
|
||||
var num=unknownNumbers[i];if(num.includes(filterValue)){var obj={name:[num],tel:[{value:num}],source:'unknown'};list.push(obj);}}
|
||||
return Promise.resolve(list);},findExact:function contacts_findExact(filterValue){return this.findBy({filterBy:['givenName','familyName'],filterOp:'contains',filterValue:filterValue}).then((contacts)=>{var contact=contacts.length?contacts[0]:null;var criteria={fields:['name'],terms:[filterValue]};var isExact=false;if(contact){isExact=isMatch(contact,criteria,filterFns.equality);}
|
||||
return isExact?[contact]:[];});},findByPhoneNumber:function contacts_findByPhone(filterValue){return this.findBy({filterBy:['tel'],filterOp:'match',filterValue:filterValue.replace(/\s+/g,'')}).then((contacts)=>{if(contacts.length){return contacts;}
|
||||
var defer=Utils.Promise.defer();fb.getContactByNumber(filterValue,function fbByPhone(fbContact){defer.resolve(fbContact?[fbContact]:[]);},function error_fbByPhone(err){if(err.name!=='DatastoreNotFound'){console.error('Error while retrieving fb by phone: ',err.name);}
|
||||
defer.resolve([]);});return defer.promise;});},findByAddress:function contacts_findByAddress(fValue){if(Settings.supportEmailRecipient&&Utils.isEmailAddress(fValue)){return this.findExactByEmail(fValue);}else{return this.findByPhoneNumber(fValue);}},findExactByEmail:function contacts_findExactByEmail(fValue){return this.findBy({filterBy:['email'],filterOp:'equals',filterValue:fValue});},addUnknown:function addUnknown(number){var index=unknownNumbers.indexOf(number);if(index===-1){unknownNumbers.push(number);}},clearUnknown:function clearUnknown(){unknownNumbers.length=0;},getunknownLength:function getunknownLength(){return unknownNumbers.length;}};exports.Contacts=Contacts;}(this));
|
||||
165
assets/webapps/sms.gaiamobile.org/views/shared/js/dialog.js
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*global WeakMap */
|
||||
|
||||
/* exported Dialog */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Generic confirm screen. Only 'cancel/default' is mandatory.
|
||||
|
||||
Text fields are localized using commonly defined parameter structure:
|
||||
https://developer.mozilla.org/en-US/Firefox_OS/Developing_Gaia/
|
||||
localization_code_best_practices#Writing_APIs_that_operate_on_L10nIDs + 'raw'
|
||||
can be Node aw well.
|
||||
|
||||
The constructor parameter should follow the following structure:
|
||||
|
||||
{
|
||||
title: { raw: 'Non localizable title' },
|
||||
body: {
|
||||
id: 'localizableStringWithArgument',
|
||||
args: { n: count }
|
||||
},
|
||||
classes: ['specific-class'], // optional
|
||||
options: {
|
||||
// Cancel is a mandatory option. You need to define at least the text.
|
||||
cancel: {
|
||||
text: 'localizableCancelLabel'
|
||||
},
|
||||
confirm: {
|
||||
text: 'localizableRemoveLabel',
|
||||
method: function(params) {
|
||||
fooFunction(params);
|
||||
},
|
||||
params: [arg1, arg2,....],
|
||||
className: 'optionalClassName'
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// helper to localize an element given parameters
|
||||
function createLocalizedElement(tagName, valueL10n) {
|
||||
var element = document.createElement(tagName);
|
||||
|
||||
// if we passed an l10nId, use the l10n `setAttributes' method
|
||||
if (typeof valueL10n === 'string') {
|
||||
element.setAttribute('data-l10n-id', valueL10n);
|
||||
} else if (valueL10n.id) {
|
||||
document.l10n.setAttributes(element, valueL10n.id, valueL10n.args);
|
||||
// if we passed in a HTML Fragment, it is already localized
|
||||
} else if (valueL10n.raw && valueL10n.raw.nodeType) {
|
||||
element.appendChild(valueL10n.raw);
|
||||
// otherwise - stuff text in here...
|
||||
} else {
|
||||
element.textContent = valueL10n.raw;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
var Dialog = function(params) {
|
||||
// We need, at least, one cancel option string
|
||||
if (!params || !params.options || !params.options.cancel) {
|
||||
return;
|
||||
}
|
||||
var handlers = new WeakMap();
|
||||
// Create the structure
|
||||
this.form = document.createElement('form');
|
||||
this.form.dataset.type = 'confirm';
|
||||
this.form.setAttribute('role', 'dialog');
|
||||
this.form.tabIndex = -1;
|
||||
// Pick up option_menu.css styling
|
||||
this.form.dataset.subtype = 'menu';
|
||||
|
||||
if (params.classes) {
|
||||
this.form.classList.add(...params.classes);
|
||||
}
|
||||
|
||||
var infoSection = document.createElement('section');
|
||||
infoSection.appendChild(createLocalizedElement('h1', params.title));
|
||||
infoSection.appendChild(createLocalizedElement('p', params.body));
|
||||
this.form.appendChild(infoSection);
|
||||
|
||||
// Adding options. In this case we have a maximum of 2, with different styles
|
||||
// per button
|
||||
var menu = document.createElement('menu');
|
||||
// Default button (Cancel button). It's mandatory
|
||||
var cancelOption = params.options.cancel;
|
||||
var cancelButton = createLocalizedElement('button', cancelOption.text);
|
||||
handlers.set(cancelButton, cancelOption);
|
||||
|
||||
// If we have only button, let's mark it as recommended action.
|
||||
if (!params.options.confirm) {
|
||||
cancelButton.className = 'recommend';
|
||||
}
|
||||
|
||||
menu.appendChild(cancelButton);
|
||||
|
||||
if (params.options.confirm) {
|
||||
var confirmOption = params.options.confirm;
|
||||
var confirmButton = createLocalizedElement('button', confirmOption.text);
|
||||
confirmButton.className = params.options.confirm.className || 'recommend';
|
||||
handlers.set(confirmButton, confirmOption);
|
||||
menu.appendChild(confirmButton);
|
||||
}
|
||||
|
||||
this.form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
this.form.addEventListener('transitionend', function(event) {
|
||||
var form = event.target;
|
||||
if (!form.classList.contains('visible') && form.parentNode) {
|
||||
form.remove();
|
||||
} else {
|
||||
// Focus form for accessibility
|
||||
form.focus();
|
||||
}
|
||||
|
||||
document.body.classList.remove('dialog-animating');
|
||||
});
|
||||
|
||||
menu.addEventListener('click', function(event) {
|
||||
var action = handlers.get(event.target);
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.method) {
|
||||
// Delegate operation to target method. This allows
|
||||
// for a custom "Cancel" to be provided by calling program
|
||||
action.method.apply(null, action.params || []);
|
||||
}
|
||||
|
||||
// Hide action menu when click is received
|
||||
this.hide();
|
||||
}.bind(this));
|
||||
// Appending the action menu to the form
|
||||
this.form.appendChild(menu);
|
||||
};
|
||||
|
||||
// We prototype functions to show/hide the UI of action-menu
|
||||
Dialog.prototype.show = function() {
|
||||
// Remove the focus to hide the keyboard asap
|
||||
document.activeElement && document.activeElement.blur();
|
||||
|
||||
if (!this.form.parentNode) {
|
||||
document.body.appendChild(this.form);
|
||||
|
||||
// Flush style on form so that the show transition plays once we add
|
||||
// the visible class.
|
||||
this.form.clientTop;
|
||||
}
|
||||
this.form.classList.add('visible');
|
||||
document.body.classList.add('dialog-animating');
|
||||
};
|
||||
|
||||
Dialog.prototype.hide = function() {
|
||||
this.form.classList.remove('visible');
|
||||
};
|
||||
|
||||
exports.Dialog = Dialog;
|
||||
|
||||
// end global closure
|
||||
}(this));
|
||||
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*global Dialog, Settings*/
|
||||
|
||||
/* exported ErrorDialog */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
/*
|
||||
Error confirm screen. Handling all the message error code and corresponding
|
||||
description/behavior.
|
||||
|
||||
Options object can contain the following properties:
|
||||
* recipients: ['foo', 'bar'] - to show the recipient information, used only
|
||||
if errorDescription.showRecipient is equal to "true";
|
||||
* confirmHandler: function() - to be executed if user selects confirm
|
||||
action in the error dialog, used only if errorDescription.executeHandler
|
||||
is equal to "true".
|
||||
}
|
||||
*/
|
||||
function ErrorDialog(errorDescription, options) {
|
||||
var prefix = errorDescription.prefix;
|
||||
|
||||
if (!prefix) {
|
||||
throw new Error('Prefix is required!');
|
||||
}
|
||||
|
||||
var dialogOptions = {
|
||||
cancel: {
|
||||
text: prefix + 'BtnOk'
|
||||
}
|
||||
};
|
||||
|
||||
if (options && options.confirmHandler && errorDescription.executeHandler) {
|
||||
dialogOptions.confirm = {
|
||||
text: prefix + 'Confirm',
|
||||
method: options.confirmHandler
|
||||
};
|
||||
}
|
||||
|
||||
var bodyL10nId = prefix + 'Body';
|
||||
var body = null;
|
||||
var additionalClasses = [];
|
||||
|
||||
if (errorDescription.showRecipient) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
var mainText = document.createElement('span');
|
||||
document.l10n.setAttributes(
|
||||
mainText, bodyL10nId,
|
||||
{ n: options.recipients.length }
|
||||
);
|
||||
fragment.appendChild(mainText);
|
||||
|
||||
var list = options.recipients.reduce((list, recipient) => {
|
||||
var li = document.createElement('li');
|
||||
li.textContent = recipient;
|
||||
|
||||
list.appendChild(li);
|
||||
|
||||
return list;
|
||||
}, document.createElement('ul'));
|
||||
|
||||
fragment.appendChild(list);
|
||||
body = { raw: fragment };
|
||||
additionalClasses.push('error-dialog-show-recipient');
|
||||
} else if (errorDescription.showDsdsStatus) {
|
||||
var messageBodyParams = null;
|
||||
|
||||
var mmsServiceId = Settings.mmsServiceId;
|
||||
if (mmsServiceId !== null) {
|
||||
// mmsServiceId = 0 => default Service is SIM 1
|
||||
// mmsServiceId = 1 => default Service is SIM 2
|
||||
|
||||
switch (mmsServiceId) {
|
||||
case 0:
|
||||
messageBodyParams = {
|
||||
activeSimId: '1',
|
||||
nonActiveSimId: '2'
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
messageBodyParams = {
|
||||
activeSimId: '2',
|
||||
nonActiveSimId: '1'
|
||||
};
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
'MMS ServiceId(' + mmsServiceId + ') is not matched!'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.error('Settings unavailable');
|
||||
}
|
||||
|
||||
body = {
|
||||
id: bodyL10nId,
|
||||
args: messageBodyParams
|
||||
};
|
||||
}
|
||||
|
||||
Dialog.call(this, {
|
||||
title: prefix + 'Title',
|
||||
body: body || bodyL10nId,
|
||||
options: dialogOptions,
|
||||
classes: ['error-dialog-' + prefix, 'error-dialog', ...additionalClasses]
|
||||
});
|
||||
}
|
||||
|
||||
ErrorDialog.prototype = Object.create(Dialog.prototype);
|
||||
|
||||
exports.ErrorDialog = ErrorDialog;
|
||||
}(this));
|
||||
81
assets/webapps/sms.gaiamobile.org/views/shared/js/errors.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* exported Errors */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Maps error code to error description object:
|
||||
* prefix: used as prefix for error message text/title l10nId,
|
||||
* showRecipient: indicates whether recipients info should be included into
|
||||
* error message displayed to the user;
|
||||
* showDsdsStatus: indicates whether DSDS status should be included into
|
||||
* error message displayed to the user;
|
||||
* executeHandler: indicates whether confirmation handler passed to the
|
||||
* error dialog should be executed once user selects confirm action.
|
||||
* @type {Map.<string, Object>}
|
||||
*/
|
||||
const ERRORS = new Map();
|
||||
|
||||
function initErrorsMap() {
|
||||
ERRORS.set('NoSimCardError', {
|
||||
prefix: 'sendMissingSimCard'
|
||||
});
|
||||
|
||||
ERRORS.set('RadioDisabledError', {
|
||||
prefix: 'sendFlightMode'
|
||||
});
|
||||
|
||||
ERRORS.set('FdnCheckError',{
|
||||
prefix: 'fdnError',
|
||||
showRecipient: true
|
||||
});
|
||||
|
||||
ERRORS.set('NonActiveSimCardError', {
|
||||
prefix: 'switchSimToRetrieve',
|
||||
showDsdsStatus: true,
|
||||
executeHandler: true
|
||||
});
|
||||
|
||||
ERRORS.set('NoSignalError', {
|
||||
prefix: 'sendNoSignalError'
|
||||
});
|
||||
|
||||
ERRORS.set('NotFoundError', {
|
||||
prefix: 'sendNotFoundError'
|
||||
});
|
||||
|
||||
ERRORS.set('InvalidAddressError', {
|
||||
prefix: 'sendInvalidAddressError'
|
||||
});
|
||||
|
||||
ERRORS.set('SimNotMatchedError', {
|
||||
prefix: 'simNotMatchedError'
|
||||
});
|
||||
|
||||
ERRORS.set('NonActiveSimCardToSendError', {
|
||||
prefix: 'switchSimToSend',
|
||||
showDsdsStatus: true,
|
||||
executeHandler: true
|
||||
});
|
||||
|
||||
ERRORS.set('RadioDisabledToDownloadError', {
|
||||
prefix: 'radioDisabledToDownload'
|
||||
});
|
||||
|
||||
// The error type below will be represented as "GeneralError" in dialog for
|
||||
// the rest of error codes (InternalError and etc.)
|
||||
ERRORS.set('UnknownError', {
|
||||
prefix: 'sendDefaultError'
|
||||
});
|
||||
}
|
||||
|
||||
exports.Errors = {
|
||||
get: function(errorCode) {
|
||||
if (!ERRORS.size) {
|
||||
initErrorsMap();
|
||||
}
|
||||
|
||||
return ERRORS.get(errorCode) || ERRORS.get('UnknownError');
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
;;;(function(exports){'use strict';var channel=null;const BROADCAST_CHANNEL_NAME='messages-channel';const ALLOWED_EVENTS=['draft-saved','draft-deleted'];function postMessage(name,parameters){if(!channel){throw new Error('Channel is not created!');}
|
||||
if(!name){throw new Error('Name should be defined!');}
|
||||
channel.postMessage(parameters!==undefined?{name:name,parameters:parameters}:{name:name});}
|
||||
const Dispatcher=EventDispatcher.mixin({connect:function(){if(channel){return;}
|
||||
channel=new BroadcastChannel(BROADCAST_CHANNEL_NAME);channel.addEventListener('message',onMessage);exports.addEventListener('unload',()=>this.disconnect());},disconnect:function(){if(!channel){return;}
|
||||
channel.close();channel.removeEventListener('message',onMessage);channel=null;}},ALLOWED_EVENTS);const originalEmitter=Dispatcher.emit.bind(Dispatcher);Dispatcher.emit=function(eventName,parameters){if(ALLOWED_EVENTS.indexOf(eventName)<0){throw new Error('Event "'+eventName+'" is not allowed!');}
|
||||
postMessage(eventName,parameters);};function onMessage(message){try{originalEmitter(message.data.name,message.data.parameters);}catch(error){console.error('Error while emitting event "%s" with parameters "%s"',message.data&&message.data.name,JSON.stringify(message.data&&message.data.parameters),error);}}
|
||||
exports.InterInstanceEventDispatcher=Dispatcher;})(window);
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/* exported LocalizationHelper */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Look for attachment iframes and localize them - l20n.js doesn't do this
|
||||
* XXX: remove once bug 1020130 is fixed
|
||||
*/
|
||||
function localizeAttachmentIFrames() {
|
||||
var attachmentContainers = Array.from(document.querySelectorAll(
|
||||
'iframe.attachment-container'
|
||||
));
|
||||
|
||||
return Promise.all(
|
||||
attachmentContainers.map(localizeAttachmentIFrame)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize an attachment iframe
|
||||
*/
|
||||
function localizeAttachmentIFrame(attachmentContainer) {
|
||||
var doc = attachmentContainer.contentDocument;
|
||||
doc.documentElement.lang = document.documentElement.getAttribute('lang');
|
||||
doc.documentElement.dir = document.documentElement.getAttribute('dir');
|
||||
return document.l10n.translateFragment(doc.body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for time elements with format and re-translate the date message.
|
||||
* More complex because the argument to the l10n string is itself a formatted
|
||||
* date using l10n.
|
||||
* @param {boolean} isTimeFormatChanged Indicates that time format has been
|
||||
* changed and we need to update only elements with time aware formats.
|
||||
*/
|
||||
function localizeDateTime(isTimeFormatChanged) {
|
||||
var dateTimeElements = document.querySelectorAll('.l10n-contains-date');
|
||||
|
||||
Array.forEach(dateTimeElements, function(element) {
|
||||
if (!element.dataset.l10nDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
var formatL10nId = element.dataset.l10nDateFormat;
|
||||
|
||||
if (!formatL10nId) {
|
||||
return;
|
||||
}
|
||||
|
||||
formatL10nId = JSON.parse(formatL10nId);
|
||||
|
||||
if (isTimeFormatChanged && !formatL10nId.hour) {
|
||||
return;
|
||||
}
|
||||
formatL10nId.hour12 = navigator.mozHour12;
|
||||
|
||||
var formatter = new Intl.DateTimeFormat(
|
||||
navigator.languages, formatL10nId
|
||||
);
|
||||
var localeData = formatter.format(new Date(+element.dataset.l10nDate));
|
||||
|
||||
if (element.hasAttribute('data-l10n-id') &&
|
||||
element.hasAttribute('data-l10n-args')) {
|
||||
var l10nAttrs = document.l10n.getAttributes(element);
|
||||
l10nAttrs.args.date = localeData;
|
||||
document.l10n.setAttributes(element, l10nAttrs.id, l10nAttrs.args);
|
||||
} else {
|
||||
element.textContent = localeData;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.LocalizationHelper = {
|
||||
init: function () {
|
||||
function localize() {
|
||||
localizeDateTime();
|
||||
return localizeAttachmentIFrames();
|
||||
}
|
||||
|
||||
// After startup, call localize every time the language changes
|
||||
document.addEventListener('DOMRetranslated', localize);
|
||||
|
||||
// This event is fired when time format (12h/24h) is changed
|
||||
window.addEventListener(
|
||||
'timeformatchange',
|
||||
localizeDateTime.bind(null, /* isTimeFormatChanged */ true)
|
||||
);
|
||||
|
||||
// Call localize during startup
|
||||
return document.l10n.ready.then(localize);
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
;;;(function(window){'use strict';var NavigationFactory=window.NavigationFactory=function(window){const debug=[()=>{},(arg1,...args)=>console.log('[Navigation] '+arg1,...args),(arg1,...args)=>console.log('[Navigation] (%s) '+arg1,Date.now(),...args),][0];const NGA_VIEWS=Object.freeze([Object.freeze({name:'thread',url:'views/conversation/index.html',behavior:'ConversationView',previous:'thread-list'}),Object.freeze({name:'thread-list',url:'views/inbox/index.html',behavior:'InboxView'}),Object.freeze({name:'report-view',behavior:'ReportView',previous:'thread',partOf:'thread'}),Object.freeze({name:'composer',behavior:'ConversationView',previous:'thread-list',partOf:'thread'}),Object.freeze({name:'group-view',behavior:'GroupView',previous:'thread',partOf:'thread'})]);const OA_VIEWS=Object.freeze([Object.freeze({name:'thread-list',url:'index.html',behavior:'InboxView'}),Object.freeze({name:'thread',behavior:'ConversationView',previous:'thread-list',partOf:'thread-list'}),Object.freeze({name:'composer',behavior:'ConversationView',previous:'thread-list',partOf:'thread-list'}),Object.freeze({name:'group-view',behavior:'GroupView',previous:'thread',partOf:'thread-list'}),Object.freeze({name:'report-view',behavior:'ReportView',previous:'thread',partOf:'thread-list'})]);const VIEWS=isUsingOldArchitecture()?OA_VIEWS:NGA_VIEWS;const priv={pendingInit:Symbol('pendingInit')};var currentState;var navigationTransition;function setLocation(url){debug('Setting location',url);window.location.assign(url);}
|
||||
function isUsingOldArchitecture(){debug('isUsingOldArchitecture()');var pathname=window.location.pathname;var result=(pathname==='/'||pathname==='/index.html');debug('isUsingOldArchitecture() =>',result);return result;}
|
||||
function findViewNameFromHash(hash){debug('findViewNameFromHash(%s)',hash);if(!hash||!hash.startsWith('#/')){return null;}
|
||||
var endOfViewName=hash.indexOf('?');if(endOfViewName<0){endOfViewName=undefined;}
|
||||
return hash.slice(2,endOfViewName);}
|
||||
function findViewFromLocation(location){debug('findViewFromLocation(%s)',location.href);var pathName=location.pathname;if(pathName.endsWith('/')){pathName+='index.html';}
|
||||
var hash=location.hash;var viewNameFromHash=findViewNameFromHash(hash);var viewFromHash=findViewFromName(viewNameFromHash);if(viewNameFromHash&&!viewFromHash){console.error('We found no view from the hash %s',hash);return null;}
|
||||
var viewFromPath=VIEWS.find((view)=>pathName.endsWith('/'+view.url))||null;if(!viewFromPath){console.error('We found no view from the path %s',pathName);return null;}
|
||||
if(viewFromHash&&viewFromHash.partOf&&viewFromHash.partOf!==viewFromPath.name){console.error('The view %s found from the hash %s is not part of view %s',viewFromHash.name,hash,viewFromPath.name);viewFromHash=null;}
|
||||
return viewFromHash||viewFromPath;}
|
||||
function findViewFromName(name){debug('findViewFromName(%s)',name);var view=VIEWS.find((view)=>view.name===name)||null;debug('findViewFromName() => %s',view&&view.name);return view;}
|
||||
function findContainerView(view){debug('findContainerView(%s)',view.name);if(view.partOf){debug('findContainerView: %s is part of view %s',view.name,view.partOf);view=findViewFromName(view.partOf);}
|
||||
debug('findContainerView() => %s',view.name);return view;}
|
||||
var readyDefer=Utils.Promise.defer();function waitForReady(){if(!readyDefer){return Promise.reject(new Error('waitForReady was called but status is ready already.'));}
|
||||
return readyDefer.promise;}
|
||||
function startNavigationFromCurrentLocation(){var location=window.location;debug('startNavigationFromCurrentLocation(), location=%s',location);var newPanel=findViewFromLocation(location);if(!newPanel){return Promise.reject(new Error('Couldn\'t find a view from location '+location.href));}
|
||||
var args=Utils.params(decodeURIComponent(location.hash));debug('startNavigationFromCurrentLocation:','found panel',newPanel.name,'with args',args);return startNavigation({newPanel,args});}
|
||||
function startNavigation(opts){var{newPanel,args,partial}=opts;args=args||{};debug('startNavigation(newPanel=%s, partial=%s)',newPanel&&newPanel.name,partial);if(navigationTransition&&(!navigationTransition.partial||partial)){debug('startNavigation: previous navigation exists, deferring.');return navigationTransition.defer.promise.then(()=>startNavigation(opts));}
|
||||
var newContainerView=newPanel&&findContainerView(newPanel);var containerView=currentState&&findContainerView(currentState.panel);var leaveDocument=currentState&&containerView!==newContainerView;if(newPanel&&!leaveDocument){var behaviorObject=window[newPanel.behavior];var viewReady=behaviorObject&&(typeof behaviorObject.isReady!=='function'||behaviorObject.isReady());if(!viewReady){return waitForReady().then(()=>startNavigation(opts));}}
|
||||
document.activeElement.blur();if(!navigationTransition){navigationTransition={stepsRun:[],defer:Utils.Promise.defer()};}
|
||||
navigationTransition.partial=partial;navigationTransition.leaveDocument=leaveDocument;if('meta'in args){throw new Error('Arguments cannot have a `meta` property.');}
|
||||
navigationTransition.transitionArgs={meta:{next:newPanel?{panel:newPanel.name,args}:null,prev:currentState?{panel:currentState.panel.name,args:currentState.args}:null}};Object.assign(navigationTransition.transitionArgs,args);navigationTransition.states={from:currentState,to:{panel:newPanel,args}};return Promise.resolve();}
|
||||
function setFutureState(){debug('setFutureState()');currentState=navigationTransition.states.to;}
|
||||
function endNavigation(){debug('endNavigation()');if(navigationTransition){navigationTransition.defer.resolve();navigationTransition=null;}}
|
||||
function onNavigationError(e=new Error('RejectedPromise')){console.error('Navigation error',e);endNavigation();return Promise.reject(e);}
|
||||
function catchStepError(e){if(e){console.error('[Navigation] got an exception: (%s) %s',e,e.stack&&e.stack.replace(/\n/g,'|'));}else{debug('[Navigation] got a rejected promise');}}
|
||||
function executeNavigationStep(stepName){debug('executeNavigationStep(%s)',stepName);if(!navigationTransition){return Promise.reject(new Error('Trying to execute the navigation step '+stepName+' but no transition is started yet.'));}
|
||||
if(navigationTransition.stepsRun.indexOf(stepName)>=0){debug('executeNavigationStep: step %s has already run.',stepName);return Promise.resolve();}
|
||||
var stateKey=stepName.endsWith('Leave')?'from':'to';var state=navigationTransition.states[stateKey];if(!state){debug('Nothing to execute for step',stepName);return Promise.resolve();}
|
||||
var panel=state.panel;var resultPromise=Promise.resolve();var viewObject=window[panel.behavior];if(viewObject[stepName]){resultPromise=resultPromise.then(()=>viewObject[stepName](navigationTransition.transitionArgs));}
|
||||
return resultPromise.then(()=>{navigationTransition.stepsRun.push(stepName);debug(`Finished executeNavigationStep(${stepName})`);});}
|
||||
var previousAnimationDefer;function waitForPanelAnimation(panelElement){if(previousAnimationDefer){previousAnimationDefer.reject(new Error('A new animation started'));}
|
||||
var defer=Utils.Promise.defer();previousAnimationDefer=defer;var timeout;function onAnimationEnd(e){panelElement.removeEventListener('animationend',onAnimationEnd);clearTimeout(timeout);debug('animationend',e&&e.type);defer.resolve();}
|
||||
panelElement.addEventListener('animationend',onAnimationEnd);timeout=setTimeout(onAnimationEnd,500);return defer.promise.then(()=>previousAnimationDefer=null);}
|
||||
function switchPanel(){debug('switchPanel()');if(!navigationTransition){throw new Error('Trying to switch panel but no transition is started yet.');}
|
||||
var from=navigationTransition.states.from;var to=navigationTransition.states.to;var oldView;var oldPanelElement;if(from){oldView=from.panel;oldPanelElement=document.querySelector(`.panel-${oldView.behavior}`);if(!oldPanelElement){throw new Error('Couldn\'t find the container element for view '+oldView.name);}}
|
||||
var newView=to.panel;if(!newView){throw new Error('Trying to switch panel, but no new view is defined!');}
|
||||
var newPanelElement=document.querySelector(`.panel-${newView.behavior}`);if(!newPanelElement){return Promise.reject(new Error('Couldn\'t find the container element for view '+newView.name));}
|
||||
if(newPanelElement===oldPanelElement){return Promise.resolve();}
|
||||
var isGoingBack=oldView&&oldView.previous===newView.name;newPanelElement.style='';newPanelElement.classList.remove('panel-hidden');newPanelElement.classList.add('panel-active');var doSlideAnimation=from&&from.panel&&from.panel.behavior!==newView.behavior;var doFadeInAnimation=!from||!from.panel;var animationPromise;if(doSlideAnimation){var shouldGoRight=isGoingBack^(document.dir==='rtl');newPanelElement.style.animationName=shouldGoRight?'left-to-current':'right-to-current';oldPanelElement.style.animationName=shouldGoRight?'current-to-right':'current-to-left';}else if(doFadeInAnimation){newPanelElement.style.animationName='fade-in';}
|
||||
if(doFadeInAnimation||doSlideAnimation){animationPromise=waitForPanelAnimation(newPanelElement).catch(()=>{}).then(()=>{if(oldPanelElement){oldPanelElement.style='';}
|
||||
newPanelElement.style='';});}
|
||||
return(animationPromise||Promise.resolve()).then(()=>{if(oldPanelElement){oldPanelElement.classList.remove('panel-active');oldPanelElement.setAttribute('aria-hidden','true');}
|
||||
newPanelElement.setAttribute('aria-hidden','false');});}
|
||||
function onHashChange(){debug('onHashChange, location=',window.location);startNavigationFromCurrentLocation().then(()=>executeNavigationStep('beforeLeave')).then(()=>executeNavigationStep('beforeEnter').catch(catchStepError)).then(setFutureState).then(switchPanel).then(()=>executeNavigationStep('afterEnter').catch(catchStepError)).then(()=>executeNavigationStep('afterLeave').catch(catchStepError)).then(endNavigation).then(()=>Navigation.emit('navigated')).catch(onNavigationError);}
|
||||
function attachHistoryListener(){window.addEventListener('hashchange',onHashChange);}
|
||||
function detachHistoryListener(){window.removeEventListener('hashchange',onHashChange);}
|
||||
var Navigation={back(){debug('back()');return startNavigation({partial:true}).then(()=>executeNavigationStep('beforeLeave')).then(()=>window.history.back()).then(()=>navigationTransition.defer.promise,onNavigationError);},toPanel(viewName,args){debug('toPanel(%s)',viewName);var view=findViewFromName(viewName);if(!view){return Promise.reject(new Error(`Couldn't find view ${viewName}`));}
|
||||
if(this.isCurrentPanel(viewName,args)){debug('View %s is the current view.',viewName);return Promise.resolve();}
|
||||
if(navigationTransition){debug('toPanel: previous navigation exists, deferring.');return navigationTransition.defer.promise.then(()=>this.toPanel(viewName,args));}
|
||||
this[priv.pendingInit]=false;var hash='#';if(view.partOf){hash+='/'+viewName;}
|
||||
hash=Utils.url(hash,args);return startNavigation({newPanel:view,args,partial:true}).then(()=>executeNavigationStep('beforeLeave')).then(()=>{var nextLocation;if(navigationTransition.leaveDocument){var containerView=findContainerView(view);nextLocation='/'+containerView.url+hash;}else{nextLocation=hash;}
|
||||
setLocation(nextLocation);}).then(()=>navigationTransition.defer.promise,onNavigationError);},init(){debug('init()');attachHistoryListener();if(navigator.mozHasPendingMessage('notification')){this[priv.pendingInit]=true;return Promise.reject();}
|
||||
this[priv.pendingInit]=false;return startNavigationFromCurrentLocation().then(setFutureState).then(()=>executeNavigationStep('beforeEnter').catch(catchStepError)).then(switchPanel).then(()=>executeNavigationStep('afterEnter').catch(catchStepError)).then(endNavigation).then(()=>Navigation.emit('navigated')).catch(onNavigationError);},[priv.pendingInit]:false,hasPendingInit(){return this[priv.pendingInit];},cleanup(){detachHistoryListener();},isDefaultPanel(){return currentState?!currentState.panel.partOf:!findViewNameFromHash(window.location.hash);},isCurrentPanel(panelName,args){debug('isCurrentPanel(%s)',panelName);if(!currentState||!panelName){return false;}
|
||||
if(panelName!==currentState.panel.name){return false;}
|
||||
if(!args){return true;}
|
||||
var currentArgs=currentState.args||{};for(var arg in args){if(args[arg]!=currentArgs[arg]){return false;}}
|
||||
return true;},setReady(){debug('setReady()');Array.from(document.querySelectorAll('.panel-hidden')).forEach((panel)=>{panel.classList.remove('panel-hidden');});readyDefer.resolve();readyDefer=null;}};return EventDispatcher.mixin(Navigation,['navigated']);};window.Navigation=NavigationFactory(window);})(window);
|
||||
101
assets/webapps/sms.gaiamobile.org/views/shared/js/notify.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*global SettingsURL */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var SETTINGS = {
|
||||
notificationVolume: 'audio.volume.notification',
|
||||
vibration: 'vibration.enabled',
|
||||
ringtone: 'notification.ringtone'
|
||||
};
|
||||
|
||||
function entry(key, value) {
|
||||
if (urls[key]) {
|
||||
return new SettingsURL().set(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// one day we'll be able to use ES6 initializers instead
|
||||
var urls = {};
|
||||
urls[SETTINGS.ringtone] = 1;
|
||||
|
||||
function getSetting(key) {
|
||||
if (!navigator.mozSettings) {
|
||||
return Promise.reject(new Error('The mozSettings API is not available.'));
|
||||
}
|
||||
|
||||
var request = navigator.mozSettings.createLock().get(key);
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = function() {
|
||||
resolve(entry(key, request.result[key]));
|
||||
};
|
||||
|
||||
request.onerror = function() {
|
||||
reject(new Error('Error while retrieving ' + key));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getSettings(settings) {
|
||||
return Promise.all(
|
||||
settings.map(getSetting)
|
||||
).catch((e) => {
|
||||
// catch and log errors
|
||||
console.error('Error while retrieving settings', e.message, e);
|
||||
return settings.map(() => null);
|
||||
}).then((results) => {
|
||||
return settings.reduce((result, setting, i) => {
|
||||
result[setting] = results[i];
|
||||
return result;
|
||||
}, {});
|
||||
});
|
||||
}
|
||||
|
||||
function ringtone(ringtoneFile) {
|
||||
var ringtonePlayer = new Audio();
|
||||
ringtonePlayer.src = ringtoneFile;
|
||||
ringtonePlayer.mozAudioChannelType = 'notification';
|
||||
ringtonePlayer.play();
|
||||
window.setTimeout(function smsRingtoneEnder() {
|
||||
ringtonePlayer.pause();
|
||||
ringtonePlayer.removeAttribute('src');
|
||||
ringtonePlayer.load();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function vibrate() {
|
||||
// vibration only works when App is in the foreground
|
||||
if (document.hidden) {
|
||||
window.addEventListener('visibilitychange', function waitOn() {
|
||||
window.removeEventListener('visibilitychange', waitOn);
|
||||
navigator.vibrate([200, 200, 200, 200]);
|
||||
});
|
||||
} else {
|
||||
navigator.vibrate([200, 200, 200, 200]);
|
||||
}
|
||||
}
|
||||
|
||||
var Notify = {
|
||||
ringtone: function notification_ringtone() {
|
||||
return getSettings(
|
||||
[SETTINGS.notificationVolume, SETTINGS.ringtone]
|
||||
).then((settings) => {
|
||||
if (settings[SETTINGS.notificationVolume] &&
|
||||
settings[SETTINGS.ringtone]) {
|
||||
ringtone(settings[SETTINGS.ringtone]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
vibrate: function notification_vibrate() {
|
||||
return getSettings([SETTINGS.vibration]).then((settings) => {
|
||||
if (settings[SETTINGS.vibration]) {
|
||||
vibrate();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.Notify = Notify;
|
||||
}(this));
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*exported SelectionHandler */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const REQUIRED_KEYS = [
|
||||
// elements
|
||||
'container',
|
||||
'checkUncheckAllButton',
|
||||
// methods
|
||||
'updateSelectionStatus',
|
||||
'getIdIterator',
|
||||
'isInEditMode'
|
||||
];
|
||||
|
||||
var SelectionHandler = function constructor(options) {
|
||||
// Set the necessary properties and method for selection module and throw
|
||||
// error if not existed in options.
|
||||
if (typeof options !== 'object') {
|
||||
throw new Error('options should be a valid object');
|
||||
}
|
||||
|
||||
REQUIRED_KEYS.forEach((key) => {
|
||||
if (options[key] === undefined) {
|
||||
throw new Error('Selection options does not provide required key ' + key);
|
||||
}
|
||||
|
||||
this[key] = options[key];
|
||||
});
|
||||
|
||||
this.selected = new Set();
|
||||
|
||||
this.container.addEventListener(
|
||||
'click', this.onSelected.bind(this)
|
||||
);
|
||||
this.checkUncheckAllButton.addEventListener(
|
||||
'click', this.toggleCheckedAll.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
SelectionHandler.prototype = {
|
||||
get selectedCount() {
|
||||
return this.selected.size;
|
||||
},
|
||||
|
||||
get selectedList() {
|
||||
return Array.from(this.selected);
|
||||
},
|
||||
|
||||
get inputIdSet() {
|
||||
var itemIdSet = new Set();
|
||||
for(var id of this.getIdIterator()) {
|
||||
itemIdSet.add(id.toString());
|
||||
}
|
||||
return itemIdSet;
|
||||
},
|
||||
|
||||
allSelected: function sel_allSelected() {
|
||||
/*jshint unused: false */
|
||||
var allItemCount = 0;
|
||||
var selectedCount = this.selectedCount;
|
||||
for (var id of this.getIdIterator()) {
|
||||
if (++allItemCount > selectedCount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
onSelected: function sel_onSelected(event) {
|
||||
if (!this.isInEditMode()) {
|
||||
return;
|
||||
}
|
||||
var target = event.target;
|
||||
var value = target.value;
|
||||
var existed = this.selected.has(value);
|
||||
|
||||
if (target.checked && !existed) {
|
||||
this.selected.add(value);
|
||||
} else if (!target.checked && existed) {
|
||||
this.selected.delete(value);
|
||||
} else {
|
||||
// Don't emit event if no selection change
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateSelectionStatus();
|
||||
},
|
||||
|
||||
select: function sel_select(id) {
|
||||
this.selected.add('' + id);
|
||||
},
|
||||
|
||||
unselect: function sel_unselect(id) {
|
||||
this.selected.delete('' + id);
|
||||
},
|
||||
|
||||
// Update check status of input elements in the container
|
||||
updateCheckboxes: function sel_updateCheckboxes() {
|
||||
var inputs = this.container.querySelectorAll('input[type=checkbox]');
|
||||
var length = inputs.length;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
inputs[i].checked = this.selected.has(inputs[i].value);
|
||||
}
|
||||
},
|
||||
|
||||
// if no message or few are checked : select all the messages
|
||||
// and if all messages are checked : deselect them all.
|
||||
toggleCheckedAll: function sel_toggleCheckedAll() {
|
||||
var allInputs = this.inputIdSet;
|
||||
var allSelected = (this.selected.size === allInputs.size);
|
||||
|
||||
if (allSelected) {
|
||||
this.selected.clear();
|
||||
} else {
|
||||
this.selected = allInputs;
|
||||
}
|
||||
|
||||
this.updateCheckboxes();
|
||||
|
||||
this.updateSelectionStatus();
|
||||
},
|
||||
|
||||
cleanForm: function sel_cleanForm() {
|
||||
// Reset all inputs
|
||||
this.selected.clear();
|
||||
|
||||
this.updateCheckboxes();
|
||||
|
||||
// Reset vars for deleting methods
|
||||
this.updateSelectionStatus();
|
||||
}
|
||||
};
|
||||
|
||||
exports.SelectionHandler = SelectionHandler;
|
||||
|
||||
}(this));
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
;;;'use strict';var Settings={SERVICE_ID_KEYS:{mmsServiceId:'ril.mms.defaultServiceId',smsServiceId:'ril.sms.defaultServiceId'},READ_AHEAD_THREADS_KEY:'ril.sms.maxReadAheadEntries',MMS_SIZE_OVERHEAD:5*1024,_serviceIds:null,supportEmailRecipient:true,maxConcatenatedMessages:10,mmsSizeLimitation:295*1024,mmsServiceId:null,smsServiceId:null,init:function settings_init(){var keyHandlerSet={'dom.mms.operatorSizeLimitation':this.initMmsSizeLimitation.bind(this),'operatorResource.sms.maxConcat':this.initSmsMaxConcatenatedMsg.bind(this)};var settings=navigator.mozSettings;var conns=navigator.mozMobileConnections;function setHandlerMap(key){var req=settings.createLock().get(key);req.onsuccess=function settings_getSizeSuccess(){var handler=keyHandlerSet[key];handler(req.result[key]);};}
|
||||
this._serviceIds=[];if(!settings){return;}
|
||||
if(conns&&conns.length>1){for(var prop in this.SERVICE_ID_KEYS){var setting=this.SERVICE_ID_KEYS[prop];keyHandlerSet[setting]=this.initServiceId.bind(this,setting,prop);}
|
||||
for(var i=0,l=conns.length;i<l;i++){this._serviceIds.push(conns[i].iccId);}}
|
||||
for(var key in keyHandlerSet){setHandlerMap(key);}},initSmsMaxConcatenatedMsg:function initSmsMaxConcatenatedMsg(num){if(num&&!isNaN(num)){this.maxConcatenatedMessages=num;}},initMmsSizeLimitation:function initMmsSizeLimitation(size){if(size&&!isNaN(size)){this.mmsSizeLimitation=size-this.MMS_SIZE_OVERHEAD;}},initServiceId:function initMmsServiceId(settingName,propName,id){if(id!==undefined){Settings[propName]=id;}
|
||||
navigator.mozSettings.addObserver(settingName,function(e){Settings[propName]=e.settingValue;});},setMmsSimServiceId:function setSimServiceId(id){navigator.mozSettings.createLock().set({'ril.mms.defaultServiceId':id,'ril.data.defaultServiceId':id});},switchMmsSimHandler:function switchSimHandler(targetId){var conn=window.navigator.mozMobileConnections[targetId];return new Promise(function(resolve,reject){if(conn){if(conn.data.state==='registered'){resolve();}else{conn.addEventListener('datachange',function onDataChange(){if(conn.data.state==='registered'){conn.removeEventListener('datachange',onDataChange);resolve();}});this.setMmsSimServiceId(targetId);}}else{reject('Invalid connection');}}.bind(this));},isDualSimDevice:function isDualSimDevice(){return this._serviceIds&&this._serviceIds.length>1;},hasSeveralSim:function hasSeveralSim(){if(!this.isDualSimDevice()){return false;}
|
||||
var simCount=this._serviceIds.reduce(function(simCount,iccId){return iccId===null?simCount:simCount+1;},0);return simCount>1;},getServiceIdByIccId:function getServiceIdByIccId(iccId){if(!this._serviceIds){return null;}
|
||||
var index=this._serviceIds.indexOf(iccId);return index>-1?index:null;},setReadAheadThreadRetrieval:function(value){if(!navigator.mozSettings){return;}
|
||||
var setting={};setting[this.READ_AHEAD_THREADS_KEY]=value;navigator.mozSettings.createLock().set(setting);}};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
;'use strict';(function(exports){var cachedTemplates=new Map();var getTemplate=function(id){if(!cachedTemplates.has(id)){cachedTemplates.set(id,Template(id));}
|
||||
return cachedTemplates.get(id);};var SharedComponents={phoneDetails:function(details,interpolationOptions){if(!details){throw new Error('Phone details are required!');}
|
||||
return getTemplate('phone-details-tmpl').prepare({type:details.type||'',number:details.number||'',carrier:details.carrier||'',hasTypeClass:!!details.type?'has-phone-type':'',hasCarrierClass:!!details.carrier?'has-phone-carrier':''},interpolationOptions);}};exports.SharedComponents=SharedComponents;})(window);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/* global ActivityShim,
|
||||
MozMobileConnectionsShim,
|
||||
MozMobileMessageShim,
|
||||
MozSettingsShim
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
exports.bootstrap = function(appInstanceId) {
|
||||
// We can't handle system messages inside iframe right now, so we access
|
||||
// parent window here. This should be removed once bug 818000 is resolved.
|
||||
ActivityShim.init(appInstanceId, parent.mozSetMessageHandler);
|
||||
MozSettingsShim.init(navigator.mozSettings);
|
||||
MozMobileMessageShim.init(appInstanceId, navigator.mozMobileMessage);
|
||||
MozMobileConnectionsShim.init(appInstanceId, navigator.mozMobileConnections);
|
||||
};
|
||||
})(self);
|
||||
165
assets/webapps/sms.gaiamobile.org/views/shared/js/silent_sms.js
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/* global asyncStorage, Promise, TaskRunner */
|
||||
/* exported SilentSms */
|
||||
'use strict';
|
||||
|
||||
var SilentSms = (function () {
|
||||
var NO_ALARM_ID = -1;
|
||||
// The maximum amount of time that the SmsManager waits for a message before
|
||||
// disabling automatically the silent mode is five minutes.
|
||||
var SERVICE_TIMEOUT = 5 * 60 * 1000;
|
||||
var taskQueue;
|
||||
|
||||
function init() {
|
||||
taskQueue = new TaskRunner();
|
||||
if (!window.navigator.mozSetMessageHandler) {
|
||||
// bailout if we're in Firefox
|
||||
return;
|
||||
}
|
||||
|
||||
// Msg from Usage (activation / deactivation)
|
||||
window.navigator.mozSetMessageHandler('connection',
|
||||
function onConnected(request) {
|
||||
if (request.keyword !== 'costcontrolSmsQuery') {
|
||||
return;
|
||||
}
|
||||
var port = request.port;
|
||||
port.onmessage = function onReceivedMessage(evt) {
|
||||
var data = evt.data;
|
||||
switch (data.action) {
|
||||
case 'enable':
|
||||
taskQueue.push(function() {
|
||||
return SilentSms.enableSilentModeFor(data.smsNumbers, data.type)
|
||||
.then(updateUsageSilentSms)
|
||||
.then(port.postMessage.bind(port, 'silentSMSEnabled'));
|
||||
});
|
||||
break;
|
||||
case 'disable':
|
||||
taskQueue.push(function() {
|
||||
return SilentSms.disableSilentModeFor(data.type)
|
||||
.then(updateUsageSilentSms)
|
||||
.then(port.postMessage.bind(port, 'silentSMSDisabled'));
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Deactivated silentSms mode by alarm
|
||||
window.navigator.mozSetMessageHandler('alarm', function _onAlarm(alarm) {
|
||||
if (alarm.data.alarmType === 'silentSmsTimeout') {
|
||||
SilentSms.disableSilentModeFor(alarm.data.type)
|
||||
.then(updateUsageSilentSms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getUsageSilentSms() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
asyncStorage.getItem('usageSilentSms', function(usageSilentSms) {
|
||||
resolve(usageSilentSms || {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeAlarmTimeout(alarmId) {
|
||||
// There is already an alarm, remove it
|
||||
if (alarmId && alarmId !== NO_ALARM_ID) {
|
||||
navigator.mozAlarms.remove(alarmId);
|
||||
}
|
||||
}
|
||||
|
||||
function addAlarmTimeout(type, delay) {
|
||||
return new Promise(function doEnablingSilentMode(resolve, reject) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + delay);
|
||||
var request = navigator.mozAlarms.add(date, 'ignoreTimezone',
|
||||
{alarmType: 'silentSmsTimeout',
|
||||
type: type });
|
||||
request.onsuccess = function _alarmSet(evt) {
|
||||
var alarmId = evt.target.result;
|
||||
resolve(alarmId);
|
||||
};
|
||||
request.onerror = function _onError() {
|
||||
var errorMsg = 'Failed to set timeout for ' + type + 'request';
|
||||
reject(new Error(errorMsg));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// The enabling silentMode process consist on two steps:
|
||||
// 1. Enabling silentMode for the provided number
|
||||
// 2. Activating alarm for TimeOut
|
||||
function enableSilentModeFor(smsNumbers, type) {
|
||||
function _removeAlarmTimeout(usageSilentSms) {
|
||||
if (usageSilentSms && usageSilentSms[type] &&
|
||||
usageSilentSms[type].alarmId !== NO_ALARM_ID) {
|
||||
removeAlarmTimeout(usageSilentSms[type].alarmId);
|
||||
}
|
||||
}
|
||||
|
||||
function handleErrorAddingAlarm(error) {
|
||||
console.error(error.toString());
|
||||
return NO_ALARM_ID;
|
||||
}
|
||||
|
||||
return getUsageSilentSms().then(function(usageSilentSms) {
|
||||
function refreshUsageSilentSms(alarmId) {
|
||||
usageSilentSms[type] = {
|
||||
smsNumbers: smsNumbers,
|
||||
alarmId: alarmId
|
||||
};
|
||||
return usageSilentSms;
|
||||
}
|
||||
_removeAlarmTimeout(usageSilentSms);
|
||||
return addAlarmTimeout(type, SERVICE_TIMEOUT)
|
||||
.catch(handleErrorAddingAlarm)
|
||||
.then(refreshUsageSilentSms);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function disableSilentModeFor(type) {
|
||||
return getUsageSilentSms().then(function(usageSilentSms) {
|
||||
if (usageSilentSms && usageSilentSms[type] &&
|
||||
usageSilentSms[type].alarmId !== NO_ALARM_ID) {
|
||||
removeAlarmTimeout(usageSilentSms[type].alarmId);
|
||||
}
|
||||
usageSilentSms[type] = null;
|
||||
return usageSilentSms;
|
||||
});
|
||||
}
|
||||
|
||||
function updateUsageSilentSms(usageSilentSms) {
|
||||
usageSilentSms.smsIndex = getIndex(usageSilentSms);
|
||||
asyncStorage.setItem('usageSilentSms', usageSilentSms);
|
||||
}
|
||||
|
||||
function getIndex(usageSilentSms) {
|
||||
var newIndex = [];
|
||||
Object.keys(usageSilentSms).forEach(function(silentRequest) {
|
||||
var silentSmsRequest = usageSilentSms[silentRequest];
|
||||
if (silentSmsRequest && silentSmsRequest.hasOwnProperty('smsNumbers')) {
|
||||
newIndex = newIndex.concat(silentSmsRequest.smsNumbers);
|
||||
}
|
||||
});
|
||||
return newIndex;
|
||||
}
|
||||
|
||||
function checkSilentModeFor(smsNumber) {
|
||||
return taskQueue.push(function() {
|
||||
function checkSilentMode(usageSilentSms) {
|
||||
return usageSilentSms && usageSilentSms.smsIndex &&
|
||||
usageSilentSms.smsIndex.indexOf(smsNumber) !== -1;
|
||||
}
|
||||
return getUsageSilentSms().then(checkSilentMode);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
enableSilentModeFor: enableSilentModeFor,
|
||||
disableSilentModeFor: disableSilentModeFor,
|
||||
checkSilentModeFor: checkSilentModeFor
|
||||
};
|
||||
}());
|
||||
29
assets/webapps/sms.gaiamobile.org/views/shared/js/smil.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
;(function(){'use strict';var unsafeFilenamePattern=/[^a-zA-Z0-9_#.()?&%-]/g;var encoder=new TextEncoder('UTF-8');function SMIL_generateSlides(data,slide,slideIndex){const DURATION=5000;var id;var blobType;var media='';var text='';var name='';if(slide.blob){blobType=Utils.typeFromMimeType(slide.blob.type);if(blobType){var tagName=tagNameFromBlobType(blobType);var region='region="Image"';if(tagName==='ref'){region='';}
|
||||
name=slide.name.substr(slide.name.lastIndexOf('/')+1);name=name.replace(unsafeFilenamePattern,'#');name=SMIL_generateUniqueLocation(data,name);media='<'+tagName+' src="'+name+'" '+region+'/>';data.attachments.push({id:'<'+name+'>',location:name,content:slide.blob});}}
|
||||
if(slide.text){id='text_'+slideIndex+'.txt';text='<text src="'+id+'" region="Text"/>';data.attachments.push({id:'<'+id+'>',location:id,content:new Blob([encoder.encode(slide.text)],{type:'text/plain'})});}
|
||||
data.parts.push('<par dur="'+DURATION+'ms">'+media+text+'</par>');return data;}
|
||||
function tagNameFromBlobType(blobType){var out;switch(blobType){case'vcard':out='ref';break;default:out=blobType;}
|
||||
return out;}
|
||||
function SMIL_generateUniqueLocation(data,location){var extension,name,result;var FILENAME_LIMIT=40;function SMIL_uniqueLocationMatches(attachment){return attachment.location===result;}
|
||||
var index=location.lastIndexOf('.');if(index===-1){name=location;extension='';}else{extension=location.slice(index);name=location.slice(0,index);}
|
||||
if(name.length+extension.length>FILENAME_LIMIT){name=name.slice(0,FILENAME_LIMIT-extension.length);}
|
||||
result=name+extension;var duplicateIndex=2;while(data.attachments.some(SMIL_uniqueLocationMatches)){var truncIndex=0;result=name+'_'+duplicateIndex++ +extension;while(result.length>FILENAME_LIMIT){duplicateIndex=2;name=name.slice(0,--truncIndex);result=name+extension;}}
|
||||
return result;}
|
||||
window.SMIL={parse:function SMIL_parse(message){var smil=message.smil;var attachments=message.attachments;var slides=[];var attachmentsNotFound=false;var smilMismatched=false;var doc;var parTags;function readTextBlob(blob){if(!blob){return Promise.resolve('');}
|
||||
var defer=Utils.Promise.defer();var textReader=new FileReader();textReader.onload=function(event){defer.resolve(event.target.result);};textReader.onerror=function(event){console.error('Error reading text blob');defer.resolve('');};textReader.readAsText(blob,'UTF-8');return defer.promise;}
|
||||
function findAttachment(name){var index=0;var length=attachments.length;name=name.replace(/^cid:/,'');for(;index<length;index++){if(attachments[index].location===name||attachments[index].id==='<'+name+'>'){return attachments[index];}}
|
||||
return null;}
|
||||
function convertWbmpToPng(slide){var defer=Utils.Promise.defer();var reader;slide.name=slide.name.slice(0,-5)+'.png';reader=new FileReader();reader.onload=function(event){WBMP.decode(event.target.result,function callback(blob){slide.blob=blob;defer.resolve();});};reader.onerror=function(event){console.error('Error reading text blob');defer.resolve();};reader.readAsArrayBuffer(slide.blob);return defer.promise;}
|
||||
function SMIL_parseWithoutSMIL(attachment,idx){var blob=attachment.content;if(!blob){return Promise.resolve();}
|
||||
var result;var type=Utils.typeFromMimeType(blob.type);if(type==='text'&&blob.type==='text/plain'){result=readTextBlob(blob).then((text)=>{slides[idx]={text:text};});}else if(type){var slide={name:attachment.location,blob:attachment.content};if(slide.name&&slide.name.slice(-5)==='.wbmp'){result=convertWbmpToPng(slide);}
|
||||
slides[idx]=slide;}
|
||||
return result||Promise.resolve();}
|
||||
function SMIL_parseHandleParTag(par,index){if(attachmentsNotFound){return;}
|
||||
var mediaElements=par.querySelectorAll('img, video, audio, ref');var textElement=par.querySelector('text');var attachment,src;var promises=Array.from(mediaElements).map((element)=>{var result;var slide={};src=element.getAttribute('src');attachment=findAttachment(src);if(attachment){slide={name:attachment.location,blob:attachment.content};slides.push(slide);if(slide.name&&slide.name.slice(-5)==='.wbmp'){result=convertWbmpToPng(slide);}}else{attachmentsNotFound=true;}
|
||||
return result||Promise.resolve();});if(textElement){src=textElement.getAttribute('src');attachment=findAttachment(src);if(attachment){var slide=slides[slides.length-1];if(!slide||typeof slide.text!=='undefined'){slide={};slides.push(slide);}
|
||||
slide.text='';promises.push(readTextBlob(attachment.content).then((text)=>slide.text=text));}else{attachmentsNotFound=true;}}
|
||||
return Promise.all(promises);}
|
||||
var result;if(smil){doc=(new DOMParser()).parseFromString(smil,'application/xml');parTags=doc.documentElement.getElementsByTagName('par');var elements=Array.reduce(parTags,(count,par)=>count+par.childElementCount,0);if(elements!==attachments.length){smilMismatched=true;}else{result=Promise.all(Array.from(parTags).map(SMIL_parseHandleParTag));}}
|
||||
if(!smil||attachmentsNotFound||!slides.length||smilMismatched){slides=Array(attachments.length);result=Promise.all(attachments.map(SMIL_parseWithoutSMIL));}
|
||||
return(result||Promise.resolve()).then(()=>slides);},generate:function SMIL_generate(slides){const HEADER='<head><layout>'+'<root-layout width="320px" height="480px"/>'+'<region id="Image" left="0px" top="0px"'+' width="320px" height="320px" fit="meet"/>'+'<region id="Text" left="0px" top="320px"'+' width="320px" height="160px" fit="meet"/>'+'</layout></head>';var data=slides.reduce(SMIL_generateSlides,{attachments:[],parts:[]});data.smil='<smil>'+HEADER+'<body>'+
|
||||
data.parts.join('')+'</body></smil>';delete data.parts;return data;}};})();
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
;'use strict';(function(exports){var debug=0?(arg1,...args)=>console.log('[Startup] '+arg1,...args):()=>{};var Startup=exports.Startup={_lazyLoadScripts:['/shared/js/settings_listener.js','/shared/js/mime_mapper.js','/shared/js/notification_helper.js','/shared/js/option_menu.js','/shared/js/gesture_detector.js','/shared/js/settings_url.js','/shared/js/mobile_operator.js','/shared/js/multi_sim_action_button.js','/shared/js/image_utils.js','/shared/elements/gaia_sim_picker/script.js','/views/shared/js/waiting_screen.js','/views/shared/js/errors.js','/views/shared/js/dialog.js','/views/shared/js/error_dialog.js','/views/conversation/js/link_helper.js','/views/conversation/js/link_action_handler.js','/views/shared/js/contact_renderer.js','/views/shared/js/activity_picker.js','/views/conversation/js/information.js','/views/shared/js/shared_components.js','/views/shared/js/task_runner.js','/views/shared/js/silent_sms.js','/views/conversation/js/recipients.js','/views/conversation/js/attachment.js','/views/conversation/js/attachment_renderer.js','/views/conversation/js/conversation.js','/views/conversation/js/subject_composer.js','/views/conversation/js/compose.js','/views/shared/js/wbmp.js','/views/shared/js/smil.js','/views/shared/js/notify.js','/views/shared/js/activity_handler.js','/views/shared/js/system_message_handler.js','/views/shared/js/localization_helper.js','/lib/bridge/client.js','/services/js/activity/activity_client.js','/services/js/messaging/messaging_client.js','/services/js/moz_mobile_connections/moz_mobile_connections_client.js'],_lazyLoadStyles:['/shared/style/confirm.css','/shared/style/status.css','/shared/style/input_areas.css','/shared/style/progress_activity.css','/views/shared/style/composer.css','/views/conversation/style/message.css','/views/conversation/style/report_view.css','/views/conversation/style/attachment.css','/views/conversation/style/conversation.css','/views/new_message/style/new_message.css','/views/new_message/style/recipients.css'],_lazyLoadInit:function(){var lazyLoadPromise=LazyLoader.load([...this._lazyLoadScripts,...this._lazyLoadStyles]).then(()=>{LocalizationHelper.init();InterInstanceEventDispatcher.connect();SilentSms.init();if(navigator.mozHasPendingMessage('activity')){ActivityHandler.init();}else{SystemMessageHandler.init();}
|
||||
TimeHeaders.init();ConversationView.init();MessagingClient.init(App.instanceId);MozMobileConnectionsClient.init(App.instanceId);Information.initDefaultViews();Navigation.setReady();window.performance.mark('contentInteractive');Settings.init();window.performance.mark('objectsInitEnd');});return lazyLoadPromise;},init:function(){var loaded=function(){window.removeEventListener('DOMContentLoaded',loaded);window.performance.mark('navigationLoaded');Utils.initializeShimHost(App.instanceId);MessageManager.init();Drafts.init();InboxView.init();Navigation.init();InboxView.once('fully-loaded',()=>{window.performance.mark('fullyLoaded');App.setReady();});InboxView.once('visually-loaded',()=>{window.performance.mark('visuallyLoaded');});if(Navigation.isDefaultPanel()&&!navigator.mozHasPendingMessage('notification')&&!navigator.mozHasPendingMessage('activity')){debug('Rendering threads now.');InboxView.once('visually-loaded',()=>{this._lazyLoadInit();});InboxView.renderThreads();}else{debug('Not using default panel, waiting for navigated event');Navigation.once('navigated',()=>InboxView.renderThreads());this._lazyLoadInit();}
|
||||
window.performance.mark('navigationInteractive');}.bind(this);window.addEventListener('DOMContentLoaded',loaded);}};Startup.init();})(window);
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/*global Contacts,
|
||||
MessageManager,
|
||||
Navigation,
|
||||
NotificationHelper,
|
||||
Notify,
|
||||
Settings,
|
||||
SilentSms,
|
||||
SMIL,
|
||||
Utils
|
||||
*/
|
||||
/*exported SystemMessageHandler */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which currently acquired wake lock will be
|
||||
* automatically released.
|
||||
* @type {Number}
|
||||
* @const
|
||||
*/
|
||||
const AUTO_WAKE_LOCK_UNLOCK_TIMEOUT = 30 * 1000;
|
||||
|
||||
const priv = {
|
||||
wakeLock: Symbol('wakeLock'),
|
||||
autoWakeLockUnlockTimeout: Symbol('autoWakeLockUnlockTimeout')
|
||||
};
|
||||
|
||||
/**
|
||||
* Object that is responsible for handling "sms-received" and "notification"
|
||||
* system messages.
|
||||
*/
|
||||
var SystemMessageHandler = {
|
||||
/**
|
||||
* Wake lock instance.
|
||||
* @type {MozWakeLock}
|
||||
* @private
|
||||
*/
|
||||
[priv.wakeLock]: null,
|
||||
|
||||
/**
|
||||
* Timeout that will trigger to release of currently active wake lock.
|
||||
* @type {Number}
|
||||
* @private
|
||||
*/
|
||||
[priv.autoWakeLockUnlockTimeout]: null,
|
||||
|
||||
/**
|
||||
* Initializes system message handlers, if app is run as activity, no
|
||||
* system message handlers will be registered.
|
||||
*/
|
||||
init() {
|
||||
if (!navigator.mozSetMessageHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.mozSetMessageHandler(
|
||||
'sms-received', this.onSmsReceivedSystemMessage.bind(this)
|
||||
);
|
||||
|
||||
navigator.mozSetMessageHandler(
|
||||
'notification', this.onNotificationSystemMessage.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests application wake lock.
|
||||
* @private
|
||||
*/
|
||||
requestWakeLock() {
|
||||
this[priv.wakeLock] = navigator.requestWakeLock('cpu');
|
||||
|
||||
this[priv.autoWakeLockUnlockTimeout] = setTimeout(
|
||||
() => this.releaseWakeLock(), AUTO_WAKE_LOCK_UNLOCK_TIMEOUT
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Releases currently active application wake lock.
|
||||
* @private
|
||||
*/
|
||||
releaseWakeLock() {
|
||||
var autoWakeLockUnlockTimeout = this[priv.autoWakeLockUnlockTimeout];
|
||||
if (autoWakeLockUnlockTimeout) {
|
||||
clearTimeout(autoWakeLockUnlockTimeout);
|
||||
this[priv.autoWakeLockUnlockTimeout] = null;
|
||||
}
|
||||
|
||||
var wakeLock = this[priv.wakeLock];
|
||||
if (wakeLock) {
|
||||
wakeLock.unlock();
|
||||
this[priv.wakeLock] = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepares notification body for the message
|
||||
* @param {MozMmsMessage|MozSmsMessage} message Message to prepare
|
||||
* notification body for.
|
||||
* @returns {Promise.<{raw: string}|string>} L10n object that will serve as
|
||||
* notification body.
|
||||
* @private
|
||||
*/
|
||||
getNotificationBody(message) {
|
||||
// In case of sms, we want to display text content in notification.
|
||||
if (message.type === 'sms') {
|
||||
return Promise.resolve({ raw: message.body || '' });
|
||||
}
|
||||
|
||||
var mmsNotificationBodyL10n = null;
|
||||
|
||||
// Here we can only have one sender, so deliveryInfo[0].deliveryStatus
|
||||
// => message status from sender.
|
||||
if (message.deliveryInfo[0].deliveryStatus !== 'success') {
|
||||
// If the delivery status is manual/rejected/error, we need to apply
|
||||
// specific text to notify user that message is not downloaded.
|
||||
mmsNotificationBodyL10n = 'notDownloaded-title';
|
||||
} else if (message.subject) {
|
||||
// If subject exist, we display subject in notification.
|
||||
mmsNotificationBodyL10n = { raw: message.subject };
|
||||
}
|
||||
|
||||
return mmsNotificationBodyL10n ?
|
||||
Promise.resolve(mmsNotificationBodyL10n) :
|
||||
SMIL.parse(message).then((slideArray) => {
|
||||
// If MMS has at least one non-empty text slide, let's display that
|
||||
// text in notification otherwise use predefined 'mms message'.
|
||||
var textSlide = slideArray.find((slide) => !!slide.text);
|
||||
return textSlide ? { raw: textSlide.text } : 'mms-message';
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepares notification title for the message
|
||||
* @param {MozMmsMessage|MozSmsMessage} message Message to prepare
|
||||
* notification title for.
|
||||
* @returns {Promise.<{raw: string}|{id: string, args: Object}>} L10n object
|
||||
* that will serve as notification title.
|
||||
* @private
|
||||
*/
|
||||
getNotificationTitle(message) {
|
||||
return Contacts.findByAddress(message.sender).then((contacts) => {
|
||||
var sender = contacts.length && contacts[0].name &&
|
||||
contacts[0].name.length && contacts[0].name[0] ||
|
||||
message.sender;
|
||||
|
||||
if (Settings.hasSeveralSim() && message.iccId) {
|
||||
return Utils.getSimNameByIccId(message.iccId).then(
|
||||
(simName) => ({
|
||||
id: 'dsds-notification-title-with-sim',
|
||||
args: { sim: simName, sender: sender }
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return { raw: sender };
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves correct application icon to use within notifications.
|
||||
* @returns {string} Application icon URI.
|
||||
* @private
|
||||
*/
|
||||
getNotificationIcon() {
|
||||
return navigator.mozApps.getSelf().then(
|
||||
(app) => NotificationHelper.getIconURI(app)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Processes received class-0 message (flash message).
|
||||
* @param {MozMmsMessage|MozSmsMessage} message Received class-0 message.
|
||||
* @returns {Promise.<void>} Promise that is resolved once message is fully
|
||||
* handled.
|
||||
* @private
|
||||
*/
|
||||
onFlashMessageReceived(message) {
|
||||
// We have to remove message from DB as it should not be kept in Inbox.
|
||||
return MessageManager.deleteMessages(message.id).then(
|
||||
() => navigator.mozApps.getSelf()
|
||||
).then((app) =>{
|
||||
app.launch();
|
||||
|
||||
Notify.ringtone();
|
||||
Notify.vibrate();
|
||||
|
||||
Utils.alert({ raw: message.body || '' }, { raw: message.sender });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles received class-1, class-2 or class-3 message.
|
||||
* @param {MozMmsMessage|MozSmsMessage} message Received message.
|
||||
* @returns {Promise.<void>} Promise that is resolved once message is fully
|
||||
* handled.
|
||||
* @private
|
||||
*/
|
||||
onMessageReceived(message) {
|
||||
// If message type is mms and pending on server, ignore the notification
|
||||
// because it will be retrieved from server automatically. Handle other
|
||||
// manual/error status as manual download and dispatch notification.
|
||||
// Please ref mxr for all the possible delivery status:
|
||||
// http://mxr.mozilla.org/mozilla-central/source/dom/mms/src/ril/
|
||||
// MmsService.js#62
|
||||
// Here we can only have one sender, so deliveryInfo[0].deliveryStatus
|
||||
// => message status from sender.
|
||||
if (message.type === 'mms' &&
|
||||
message.deliveryInfo[0].deliveryStatus === 'pending') {
|
||||
return;
|
||||
}
|
||||
|
||||
var isInConversation = Navigation.isCurrentPanel(
|
||||
'thread', { id: message.threadId }
|
||||
);
|
||||
|
||||
// If app is already opened and we're in the right conversation, let's
|
||||
// just notify user about new message.
|
||||
if (!document.hidden && isInConversation) {
|
||||
Notify.ringtone();
|
||||
Notify.vibrate();
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
this.getNotificationTitle(message),
|
||||
this.getNotificationBody(message),
|
||||
this.getNotificationIcon()
|
||||
]).then(([titleL10n, bodyL10n, icon]) => {
|
||||
// Force the helper to leave close behavior as it is because it should
|
||||
// be handled in Utils.closeNotificationsForThread later.
|
||||
return NotificationHelper.send(titleL10n, {
|
||||
icon: icon,
|
||||
bodyL10n: bodyL10n,
|
||||
tag: 'threadId:' + message.threadId,
|
||||
data: { id: message.id, threadId: message.threadId },
|
||||
closeOnClick: false
|
||||
});
|
||||
}).then((notification) => {
|
||||
notification.addEventListener(
|
||||
'click', () => this.onNotificationClicked(message)
|
||||
);
|
||||
|
||||
// Close notification if we are already in conversation view and app
|
||||
// becomes visible.
|
||||
if (document.hidden && isInConversation) {
|
||||
Utils.onceDocumentIsVisible().then(() => notification.close());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fires once notification is clicked.
|
||||
* @param {{ id: string, threadId: string }} notification Notification
|
||||
* arbitrary data.
|
||||
* @returns {Promise.<void>} Promise that is resolved once notification
|
||||
* click is handled.
|
||||
* @private
|
||||
*/
|
||||
onNotificationClicked({ id, threadId }) {
|
||||
return navigator.mozApps.getSelf().then((app) => {
|
||||
app.launch();
|
||||
|
||||
// If we're currently in the target conversation, just do nothing.
|
||||
if (Navigation.isCurrentPanel('thread', { id: threadId })) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Validate if message still exists before opening message thread
|
||||
//See issue https://bugzilla.mozilla.org/show_bug.cgi?id=837029
|
||||
return MessageManager.getMessage(id).then(
|
||||
(message) => Navigation.toPanel('thread', { id: message.threadId }),
|
||||
() => {
|
||||
if (Navigation.hasPendingInit()) {
|
||||
Navigation.init();
|
||||
}
|
||||
Utils.alert('deleted-sms');
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles "sms-received" system message.
|
||||
* @param {MozMmsMessage|MozSmsMessage} message Received message.
|
||||
* @returns {Promise.<void>} Promise that is resolved once message is fully
|
||||
* handled.
|
||||
* @private
|
||||
*/
|
||||
onSmsReceivedSystemMessage(message) {
|
||||
// Acquire the cpu wake lock when we receive an SMS. This raises the
|
||||
// priority of this process above vanilla background apps, making it less
|
||||
// likely to be killed on OOM. It also prevents the device from going to
|
||||
// sleep before the user is notified of the new message.
|
||||
//
|
||||
// We'll release it once we display a notification to the user. We also
|
||||
// release the lock after 30s, in case we never run the notification code
|
||||
// for some reason.
|
||||
this.requestWakeLock();
|
||||
|
||||
var handleReceivedMessagePromise;
|
||||
if (message.messageClass === 'class-0') {
|
||||
handleReceivedMessagePromise = this.onFlashMessageReceived(message);
|
||||
} else {
|
||||
handleReceivedMessagePromise = SilentSms.checkSilentModeFor(
|
||||
message.sender
|
||||
).then((isSilent) => {
|
||||
if (!isSilent) {
|
||||
return this.onMessageReceived(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return handleReceivedMessagePromise.then(() => this.releaseWakeLock());
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles "notification" system message.
|
||||
* @param {Notification} notification Notification object.
|
||||
* @returns {Promise.<void>} Promise that is resolved once notification
|
||||
* system message is handled.
|
||||
* @private
|
||||
*/
|
||||
onNotificationSystemMessage(notification) {
|
||||
// When notification is removed from notification tray, notification
|
||||
// system message will still be fired, but "clicked" property will be
|
||||
// equal to false. This should change once bug 1139363 is landed. When
|
||||
// user clicks on notification we'll get two system messages, first to
|
||||
// notify app that notification is clicked and then, once we show
|
||||
// Conversation view to the user, we remove that notification from the
|
||||
// tray that causes the second system message with "clicked" set to false.
|
||||
if (!notification.clicked || !notification.data) {
|
||||
// Force closing the window for notification removal case
|
||||
// because navigation didn't init correctly if pending notification
|
||||
// message existed.
|
||||
if (Navigation.hasPendingInit()) {
|
||||
window.close();
|
||||
return Promise.reject(new Error('Notification has been dismissed.'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// At the moment notification.data is { id, threadId }.
|
||||
return this.onNotificationClicked(notification.data);
|
||||
}
|
||||
};
|
||||
|
||||
exports.SystemMessageHandler = SystemMessageHandler;
|
||||
})(self);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;'use strict';function TaskRunner(){this._currentTask=Promise.resolve();}
|
||||
TaskRunner.prototype.push=function(task){return(this._currentTask=this._currentTask.then(task,task));};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
;;;(function(exports){'use strict';var updateTimer=null;var TimeHeaders={init:function th_init(){onvisibilityChange();document.addEventListener('visibilitychange',onvisibilityChange);window.addEventListener('timeformatchange',ontimeFormatChange);window.addEventListener('languagechange',onlanguageChange);},startScheduler:function th_startScheduler(){var now=Date.now(),nextTimeout=new Date(now+60000);nextTimeout.setSeconds(0);nextTimeout.setMilliseconds(0);this.stopScheduler();updateTimer=setTimeout(function(){this.updateAll('header[data-time-update=repeat]');this.startScheduler();}.bind(this),nextTimeout.getTime()-now);},stopScheduler:function th_stopScheduler(){clearTimeout(updateTimer);},updateAll:function th_updateAll(selector){selector=selector||'[data-time-update]';var elements=document.querySelectorAll(selector),length=elements.length,i;for(i=0;i<length;i++){this.update(elements[i]);}},update:function th_update(element){var ts=element.dataset.time;if(!ts){return;}
|
||||
if(element.dataset.dateOnly==='true'){Utils.setHeaderDate({time:ts,element:element,withTime:false});}else if(element.dataset.timeOnly==='true'){element.removeAttribute('data-l10n-id');element.textContent=Utils.getFormattedHour(ts);}else{Utils.setHeaderDate({time:ts,element:element,withTime:true});}}};function onvisibilityChange(){if(document.hidden){TimeHeaders.stopScheduler();}
|
||||
else{TimeHeaders.updateAll();TimeHeaders.startScheduler();}}
|
||||
function ontimeFormatChange(){Utils.resetDateFormatters();TimeHeaders.updateAll();}
|
||||
function onlanguageChange(){Utils.resetDateFormatters();TimeHeaders.updateAll();}
|
||||
exports.TimeHeaders=TimeHeaders;}(this));
|
||||
55
assets/webapps/sms.gaiamobile.org/views/shared/js/utils.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
;;;(function(exports){'use strict';var rdashes=/-(.)/g;var rescape=/[.?*+^$[\]\\(){}|-]/g;var rparams=/([^?=&]+)(?:=([^&]*))?/g;var rnondialablechars=/[^,#+\*\d]/g;var rmail=/[\w-]+@[\w\-]/;var Utils={date:{shared:new Date(),formatters:{'time':{options:{hour:'numeric',minute:'numeric',},formatter:null},'full':{options:{month:'2-digit',day:'2-digit',year:'numeric',},formatter:null},'full-wt':{options:{month:'2-digit',day:'2-digit',year:'numeric',hour:'numeric',minute:'numeric',},formatter:null},'day':{options:{weekday:'long',},formatter:null},'day-wt':{options:{weekday:'long',hour:'numeric',minute:'numeric',},formatter:null},},},escapeRegex:function ut_escapeRegex(str){if(typeof str!=='string'){return'';}
|
||||
return str.replace(rescape,'\\$&');},getFormattedHour:function ut_getFormattedHour(time){this.date.shared.setTime(+time);return this._getFormatter('time',false).format(this.date.shared);},getDayDate:function re_getDayDate(time){this.date.shared.setTime(+time);this.date.shared.setHours(0,0,0,0);return this.date.shared.getTime();},resetDateFormatters:function ut_resetDateFormatters(){for(var type in this.date.formatters){this.date.formatters[type].formatter=null;}},_getFormatter:function ut_getFormatter(type,withTime){if(withTime){type+='-wt';}
|
||||
if(!this.date.formatters[type].formatter){var options=Object.assign({},this.date.formatters[type].options);if(options.hour){options.hour12=navigator.mozHour12;}
|
||||
this.date.formatters[type].formatter=Intl.DateTimeFormat(navigator.languages,options);}
|
||||
return this.date.formatters[type].formatter;},setHeaderDate:function ut_setHeaderDate({time,element,withTime}){var formatter;var today=Utils.getDayDate(Date.now());var otherDay=Utils.getDayDate(time);var dayDiff=(today-otherDay)/86400000;this.date.shared.setTime(+time);if(isNaN(dayDiff)){element.setAttribute('data-l10n-id','incorrectDate');return;}
|
||||
if(dayDiff<0){formatter=this._getFormatter('full',withTime);element.removeAttribute('data-l10n-id');element.textContent=formatter.format(this.date.shared);return;}
|
||||
if(dayDiff===0){if(withTime){document.l10n.setAttributes(element,'todayWithTime',{time:this.getFormattedHour(this.date.shared)});}else{element.setAttribute('data-l10n-id','today');}}else if(dayDiff===1){if(withTime){document.l10n.setAttributes(element,'yesterdayWithTime',{time:this.getFormattedHour(this.date.shared)});}else{element.setAttribute('data-l10n-id','yesterday');}}else if(dayDiff<6){formatter=this._getFormatter('day',withTime);element.removeAttribute('data-l10n-id');element.textContent=formatter.format(this.date.shared);}else{formatter=this._getFormatter('full',withTime);element.removeAttribute('data-l10n-id');element.textContent=formatter.format(this.date.shared);}},getContactDetails:function ut_getContactDetails(number,contacts,include){var details={};include=include||{};function updateDetails(contact){var name,phone,i,length,subscriber,org;name=contact.name[0];org=contact.org&&contact.org[0];length=contact.tel?contact.tel.length:0;subscriber=number.length>7?number.substr(-8):number;for(i=0;i<length;i++){if(contact.tel[i].value.indexOf(subscriber)!==-1){phone=contact.tel[i];break;}}
|
||||
details.isContact=true;if(include.photoURL){var photo=ContactPhotoHelper.getThumbnail(contact);if(photo){details.photoURL=window.URL.createObjectURL(photo);}}
|
||||
details.name=name;details.org=details.org||org;}
|
||||
if(!contacts||(Array.isArray(contacts)&&contacts.length===0)){details.title='';}else if(!Array.isArray(contacts)){updateDetails(contacts);details.title=details.name||details.org;}else{for(var i=0,l=contacts.length;i<l;i++){updateDetails(contacts[i]);if(details.name){break;}}
|
||||
details.title=details.name||details.org;}
|
||||
return details;},getPhoneDetails:function ut_getPhoneDetails(input,tels){var length=tels.length;var hasUniqueCarriers=true;var hasUniqueTypes=true;var found,tel,type,carrier;for(var i=0;i<length;i++){tel=tels[i];if(tel.value&&Utils.probablyMatches(tel.value,input)){found=tel;}
|
||||
if(carrier&&carrier===tel.carrier){hasUniqueCarriers=false;}
|
||||
if(type&&type===tel.type[0]){hasUniqueTypes=false;}
|
||||
carrier=tel.carrier;type=(tel.type&&tel.type[0])||'';}
|
||||
if(!found){return null;}
|
||||
return{type:(found.type&&found.type[0])||'',carrier:hasUniqueCarriers||hasUniqueTypes?found.carrier:'',number:found.value};},removeNonDialables:function ut_removeNonDialables(input){return input.replace(rnondialablechars,'');},probablyMatches:function ut_probablyMatches(a,b){var service=navigator.mozPhoneNumberService;if(typeof a!=='string'||typeof b!=='string'){return false;}
|
||||
if(Settings.supportEmailRecipient&&Utils.isEmailAddress(a)&&Utils.isEmailAddress(b)){return a===b;}
|
||||
if(service&&service.normalize){a=service.normalize(a);b=service.normalize(b);}else{a=Utils.removeNonDialables(a);b=Utils.removeNonDialables(b);}
|
||||
return a===b||a.slice(-7)===b.slice(-7);},multiRecipientMatch:function ut_multiRecipientMatch(a,b){a=[].concat(a);b=[].concat(b);var blen=b.length;if(a.length!==blen){return false;}
|
||||
return a.every(function(number){for(var i=0;i<blen;i++){if(Utils.probablyMatches(number,b[i])){return true;}}});},getResizedImgBlob:function ut_getResizedImgBlob(blob,limit,callback){var defaultLimit=295*1024;if(typeof limit==='function'){callback=limit;limit=defaultLimit;}
|
||||
limit=limit===0?defaultLimit:Math.min(limit,defaultLimit);if(blob.size<limit){setTimeout(function blobCb(){callback(blob);});return;}
|
||||
var ratio=Math.sqrt(blob.size/limit);Utils._resizeImageBlobWithRatio({blob:blob,limit:limit,ratio:ratio,callback:callback});},getSizeForL10n:function ut_getSizeForL10n(size){var sizeKB=size/1024;var sizeMB=sizeKB/1024;if(size<1000){return{l10nId:'attachmentSizeB',l10nArgs:{n:size}};}else if(sizeKB<1000){return{l10nId:'attachmentSizeKB',l10nArgs:{n:sizeKB.toFixed(1)}};}
|
||||
return{l10nId:'attachmentSizeMB',l10nArgs:{n:sizeMB.toFixed(1)}};},_resizeImageBlobWithRatio:function ut_resizeImageBlobWithRatio(obj){var blob=obj.blob;var callback=obj.callback;var limit=obj.limit;var ratio=Math.ceil(obj.ratio);var qualities=[0.65,0.5,0.25];var sampleSize=1;var sampleSizeHash='';if(blob.size<limit||ratio<=1){setTimeout(function blobCb(){callback(blob);});return;}
|
||||
if(blob.type==='image/jpeg'){if(ratio>=8){sampleSize=8;}else{sampleSize=ratio=Utils.getClosestSampleSize(ratio);}
|
||||
sampleSizeHash='#-moz-samplesize='+sampleSize;}
|
||||
var img=document.createElement('img');var url=window.URL.createObjectURL(blob);img.src=url+sampleSizeHash;img.onload=function onBlobLoaded(){window.URL.revokeObjectURL(url);var targetWidth=img.width*sampleSize/ratio;var targetHeight=img.height*sampleSize/ratio;var canvas=document.createElement('canvas');canvas.width=targetWidth;canvas.height=targetHeight;var context=canvas.getContext('2d',{willReadFrequently:true});context.drawImage(img,0,0,targetWidth,targetHeight);img.src='';var level=0;function cleanup(){canvas.width=canvas.height=0;canvas=null;}
|
||||
function ensureSizeLimit(resizedBlob){if(resizedBlob.size<limit){cleanup();setTimeout(callback.bind(null,resizedBlob));}else{resizedBlob=null;if(level<qualities.length){canvas.toBlob(ensureSizeLimit,'image/jpeg',qualities[level++]);}else{cleanup();setTimeout(Utils._resizeImageBlobWithRatio.bind(Utils,{blob:blob,limit:limit,ratio:ratio*2,callback:callback}));}}}
|
||||
canvas.toBlob(ensureSizeLimit,blob.type);};},getClosestSampleSize:function ut_getClosestSampleSize(ratio){if(ratio>=8){return 8;}
|
||||
if(ratio>=4){return 4;}
|
||||
if(ratio>=2){return 2;}
|
||||
return 1;},camelCase:function ut_camelCase(str){return str.replace(rdashes,function replacer(str,p1){return p1.toUpperCase();});},typeFromMimeType:function ut_typeFromMimeType(mime){var MAX_MIME_TYPE_LENGTH=256;if(typeof mime!=='string'||mime.length>MAX_MIME_TYPE_LENGTH){return null;}
|
||||
var index=mime.indexOf('/');if(index===-1){return null;}
|
||||
var mainPart=mime.slice(0,index);var secondPart=mime.substr(index+1).toLowerCase();switch(mainPart){case'image':return'img';case'text':if(secondPart.indexOf('vcard')!==-1){return'vcard';}
|
||||
if(secondPart!=='plain'){return'ref';}
|
||||
return mainPart;case'video':case'audio':case'application':return mainPart;default:return null;}},params:function(input){var parsed={};input.replace(rparams,function($0,$1,$2){if($2==='true'){$2=true;}else if($2==='false'){$2=false;}
|
||||
parsed[$1]=$2;});return parsed;},url(base,params){if(base.indexOf('?')===-1){base+='?';}else{base+='&';}
|
||||
for(var key in params){if(params[key]==null){continue;}
|
||||
base+=encodeURIComponent(key)+'='+
|
||||
encodeURIComponent(params[key])+'&';}
|
||||
return base.slice(0,-1);},basicContact:function(number,records){var record;if(Array.isArray(records)){if(records.length>0){record=records[0];}}else if(records!==null){record=records;}
|
||||
if(!record&&!number){return;}
|
||||
var telLength=(record&&record.tel)?record.tel.length:0;var tel;for(var i=0;i<telLength;i++){if(record.tel[i].value===number){tel=record.tel[i];break;}}
|
||||
if(!tel){tel={type:[''],value:number,carrier:''};}
|
||||
var details=Utils.getContactDetails(tel,record);var info=Utils.getDisplayObject(details.title||null,tel);return info;},getDisplayObject:function(theTitle,tel){var number=tel.value;var type=tel.type&&tel.type.length?tel.type[0]:'';var data={name:theTitle||number,number:number,type:type,carrier:tel.carrier||''};if(Settings.supportEmailRecipient){data.email=number;data.emailHTML='';}
|
||||
return data;},asyncLoadRevokeURL:function(url){setTimeout(function(){var image=new Image();image.src=url;image.onload=image.onerror=function revokePhotoURL(){window.URL.revokeObjectURL(this.src);};});},isEmailAddress:function(email){return rmail.test(email);},closeNotificationsForThread:function(threadId){var targetTag='threadId:'+threadId;return Notification.get({tag:targetTag}).then(function onSuccess(notifications){for(var i=0;i<notifications.length;i++){notifications[i].close();}}).catch(function onError(reason){console.error('Notification.get(tag: '+targetTag+'): ',reason);});},debounce:function(func,waitTime){var timeout,args,context;var executeLater=function(){func.apply(context,args);timeout=context=args=null;};return function(){context=this;args=arguments;if(timeout){clearTimeout(timeout);}
|
||||
timeout=setTimeout(executeLater,waitTime);};},throttle:function(func,delay=300,options={}){var timeout=null;var previous=0;var preventFirstCall=!!options.preventFirstCall;var preventLastCall=!!options.preventLastCall;if(typeof func!=='function'){throw new Error('func must be a Function');}
|
||||
if(typeof delay!=='number'||delay<0){throw new Error('delay must be a positive number');}
|
||||
return function(...args){var now=Date.now();if(!previous&&preventFirstCall){previous=now;}
|
||||
var remaining=delay-(now-previous);if(remaining<=0){if(timeout){clearTimeout(timeout);timeout=null;}
|
||||
previous=now;func.apply(this,args);}else if(!timeout&&!preventLastCall){timeout=setTimeout(()=>{previous=preventFirstCall?0:Date.now();timeout=null;func.apply(this,args);},remaining);}};},alert:function(message,title){var deferred=this.Promise.defer();var dialog=new Dialog({title:title||'modal-dialog-default-title',body:message,options:{cancel:{text:'modal-dialog-ok-button',method:deferred.resolve}}});dialog.show();return deferred.promise;},confirm:function(message,title,confirmOptions){var confirmButtonText=confirmOptions&&confirmOptions.text||'modal-dialog-ok-button';var confirmButtonClassName=confirmOptions&&confirmOptions.className||'recommend';var deferred=this.Promise.defer();var dialog=new Dialog({title:title||'modal-dialog-default-title',body:message,options:{cancel:{text:'modal-dialog-cancel-button',method:deferred.reject},confirm:{text:confirmButtonText,className:confirmButtonClassName,method:deferred.resolve}}});dialog.show();return deferred.promise;},Promise:{defer:function(){var deferred={};deferred.promise=new Promise(function(resolve,reject){deferred.resolve=resolve;deferred.reject=reject;});return deferred;},async:function(generatorFunction){return function asyncGenerator(){var generator=generatorFunction.apply(this,arguments);function handle(result){if(result.done){return Promise.resolve(result.value);}
|
||||
return Promise.resolve(result.value).then((result)=>handle(generator.next(result)),(error)=>handle(generator.throw(error)));}
|
||||
try{return handle(generator.next());}catch(error){return Promise.reject(error);}};}},getSimNameByIccId(iccId){var index=Settings.getServiceIdByIccId(iccId);if(index===null){return Promise.resolve('');}
|
||||
return document.l10n.formatValue('sim-id-label',{id:index+1});},initializeShimHost(appInstanceId){var shimHostIframe=document.querySelector('.shim-host');var promise=typeof shimHostIframe.contentWindow.bootstrap==='function'?Promise.resolve():new Promise((resolve)=>{shimHostIframe.addEventListener('load',function onLoad(){shimHostIframe.removeEventListener('load',onLoad);resolve();});});return promise.then(()=>{window.mozSetMessageHandler=function(type,handler){navigator.mozSetMessageHandler(type,(request)=>handler(request));};shimHostIframe.contentWindow.bootstrap(appInstanceId);});},onceDocumentIsVisible(){if(!document.hidden){return Promise.resolve();}
|
||||
var defer=this.Promise.defer();document.addEventListener('visibilitychange',function waitVisibility(){document.removeEventListener('visibilitychange',waitVisibility);defer.resolve();});return defer.promise;}};exports.Utils=Utils;}(this));
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/*exported WaitingScreen */
|
||||
|
||||
'use strict';
|
||||
|
||||
var WaitingScreen = {
|
||||
get loading() {
|
||||
delete this.loading;
|
||||
return (this.loading = document.getElementById('loading'));
|
||||
},
|
||||
get loadingHeader() {
|
||||
delete this.loadingHeader;
|
||||
return (this.loadingHeader = document.getElementById('loading-header'));
|
||||
},
|
||||
show: function ws_show() {
|
||||
this.loading.classList.add('show-loading');
|
||||
},
|
||||
hide: function ws_hide() {
|
||||
this.loading.classList.remove('show-loading');
|
||||
}
|
||||
};
|
||||
11
assets/webapps/sms.gaiamobile.org/views/shared/js/wbmp.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
;'use strict';var WBMP=(function(document){function decode(arrayBuffer,callback){var bytes=new Uint8Array(arrayBuffer);var ptr=0;function readOctet(){return bytes[ptr++]&0xff;}
|
||||
function readMultiByteInteger(){var result=0;while(true){if(result&0xfe000000){throw'error parsing integer';}
|
||||
var b=bytes[ptr++];result=(result<<7)|(b&0x7f);if(!(b&0x80)){return result;}}}
|
||||
function write(data,w,bit){var color=bit?255:0;data[w]=color;data[w+1]=color;data[w+2]=color;data[w+3]=255;}
|
||||
try{if(readMultiByteInteger()!==0){return false;}
|
||||
if(readOctet()!==0){return false;}
|
||||
var width=readMultiByteInteger();var height=readMultiByteInteger();if(width===0||width>65535||height===0||height>65535){return false;}
|
||||
var canvas=document.createElement('canvas');canvas.setAttribute('width',width);canvas.setAttribute('height',height);var ctx=canvas.getContext('2d',{willReadFrequently:true});var imageData=ctx.createImageData(width,height);var data=imageData.data;for(var y=0;y<height;++y){for(var x=0;x<width;x+=8){var bits=bytes[ptr++];var w=(y*width+x)*4;write(data,w,bits&0x80);write(data,w+4,bits&0x40);write(data,w+8,bits&0x20);write(data,w+12,bits&0x10);write(data,w+16,bits&0x08);write(data,w+20,bits&0x04);write(data,w+24,bits&0x02);write(data,w+28,bits&0x01);}}
|
||||
if(ptr>bytes.length){return null;}
|
||||
ctx.putImageData(imageData,0,0);canvas.toBlob(callback);}catch(e){return null;}}
|
||||
return{decode:decode};})(document);
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Messages Shim Host</title>
|
||||
<!-- Firefox Desktop shims -->
|
||||
|
||||
|
||||
|
||||
<!-- Shim scripts -->
|
||||
<script src="/lib/bridge/service.js"></script>
|
||||
<script src="/lib/bridge/plugins/stream/service.js"></script>
|
||||
<script src="/services/js/bridge_service_mixin.js"></script>
|
||||
<script src="/services/js/activity/activity_shim.js"></script>
|
||||
<script src="/services/js/moz_settings/moz_settings_shim.js"></script>
|
||||
<script src="/services/js/moz_mobile_message/moz_mobile_message_shim.js"></script>
|
||||
<script src="/services/js/moz_mobile_connections/moz_mobile_connections_shim.js"></script>
|
||||
<script src="/views/shared/js/shim_host_bootstrap.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Structure of app
|
||||
*/
|
||||
|
||||
.view-body {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: -moz-calc(100% - 5rem);
|
||||
width: 100%;
|
||||
/* This box-sizing is necessary to prevent vertical scrolling in views that
|
||||
* specify vertical paddings and/or borders. */
|
||||
-moz-box-sizing: border-box;
|
||||
will-change: scroll-position;
|
||||
}
|
||||
|
||||
/* Style of highlighted text. */
|
||||
.highlight {
|
||||
background-color: #b2f2ff;
|
||||
}
|
||||
|
||||
/* Hidden elements */
|
||||
.hide, [data-template] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
#messages-compose-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: auto; /* overrides building blocks */
|
||||
min-height: 4rem;
|
||||
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* Overwrite. No more fixed height */
|
||||
form[role="search"] p input,
|
||||
form[role="search"] p textarea,
|
||||
form[role="search"] button[type="submit"] + p > textarea {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
* Input now are contentEditable containers. We define
|
||||
* placeholder management here as well.
|
||||
*/
|
||||
|
||||
#messages-input {
|
||||
position: relative; /* to position its generated content */
|
||||
|
||||
overflow: auto;
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-inline-end: 0.5rem;
|
||||
|
||||
color: #474747;
|
||||
border-inline-end: 0.1rem solid #ccc;
|
||||
|
||||
font-size: 1.7rem;
|
||||
line-height: 2.1rem;
|
||||
|
||||
unicode-bidi: -moz-plaintext;
|
||||
}
|
||||
|
||||
#messages-input.placeholder {
|
||||
unicode-bidi: unset;
|
||||
}
|
||||
|
||||
#messages-input:after {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
color: #9b9b9b;
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#messages-input.placeholder:after {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Overwrite BB because of the previous rule */
|
||||
form[role="search"] button[type="submit"]:after {
|
||||
content: "";
|
||||
background: none;
|
||||
}
|
||||
|
||||
.mms-label-line {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
background-color: #e1f0ec;
|
||||
}
|
||||
|
||||
.subject-composer {
|
||||
position: relative;
|
||||
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
padding: 1rem 0 0.3rem;
|
||||
}
|
||||
|
||||
.mms-label {
|
||||
padding: 1rem 0 0.3rem;
|
||||
}
|
||||
|
||||
.mms-label,
|
||||
.message-counter {
|
||||
display: none;
|
||||
flex: none;
|
||||
text-align: center;
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 6rem;
|
||||
|
||||
color: #575757;
|
||||
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.message-counter {
|
||||
padding-top: .7rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. "MMS" label is displayed near the subject field when subject input is
|
||||
* visible and message type is MMS;
|
||||
* 2. Remaining characters counter is displayed in bottom section if character
|
||||
* count exceeds predefined threshold and message type is SMS.
|
||||
*/
|
||||
[data-message-type='mms'] .mms-label,
|
||||
[data-message-type='sms'] .message-counter:not(:empty) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content-composer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.subject-composer-input {
|
||||
position: relative;
|
||||
|
||||
box-sizing: border-box;
|
||||
min-height: 2.2rem;
|
||||
padding: 0;
|
||||
padding-bottom: 0.1rem;
|
||||
padding-inline-start: 1.5rem;
|
||||
|
||||
color: #008787;
|
||||
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.subject-composer-placeholder {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
/* adding both left/right so that it behaves correctly in RTL */
|
||||
left: 1.5rem;
|
||||
right: 1.5rem;
|
||||
|
||||
color: #00adad;
|
||||
font-size: 1.7rem;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.composer-message-input-container {
|
||||
position: relative;
|
||||
|
||||
flex: auto;
|
||||
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
min-height: 4rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.composer-button-container {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex: none;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
width: 6rem;
|
||||
|
||||
-moz-user-select: none; /* without this, tapping on the element selects its content */
|
||||
}
|
||||
|
||||
.messages-attach-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#messages-send-button {
|
||||
height: 4rem;
|
||||
overflow: visible;
|
||||
background: #fff no-repeat center center / 3rem;
|
||||
|
||||
align-self: flex-end;
|
||||
}
|
||||
html[dir="ltr"] #messages-send-button {
|
||||
background-image: url(images/icons/actionicon_sms_send_30x30.png);
|
||||
/* When we put the icon at the center, an optical illusion makes it look to be
|
||||
* slightly to the left, so we offset the button by 2px to the right to
|
||||
* counteract this illusion */
|
||||
transform: translate(.2rem);
|
||||
}
|
||||
html[dir="rtl"] #messages-send-button {
|
||||
background-image: url(images/icons/actionicon_sms_send_30x30_rtl.png);
|
||||
transform: translate(-.2rem);
|
||||
}
|
||||
|
||||
#messages-send-button:after {
|
||||
display: none
|
||||
}
|
||||
|
||||
#messages-send-button[disabled] {
|
||||
background-blend-mode: luminosity;
|
||||
opacity: 0.625;
|
||||
}
|
||||
|
||||
#messages-attach-button {
|
||||
align-self: flex-end;
|
||||
flex: none;
|
||||
|
||||
box-sizing: content-box;
|
||||
width: 3rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-inline-start: 0.5rem;
|
||||
padding-inline-end: 1rem;
|
||||
|
||||
background: transparent
|
||||
url('images/icons/actionicon_sms_add_attachment_30x30.png')
|
||||
no-repeat center / 3rem;
|
||||
}
|
||||
|
||||
#messages-attach-button[disabled] {
|
||||
background-blend-mode: luminosity;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/*
|
||||
DSDS
|
||||
*/
|
||||
|
||||
.sim-indication {
|
||||
position: absolute;
|
||||
offset-inline-end: 1.8rem;
|
||||
bottom: 0.4rem;
|
||||
|
||||
display: none;
|
||||
line-height: 1;
|
||||
|
||||
color: #5f5f5f;
|
||||
font-size: 1.4rem;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.sim-indication:before {
|
||||
position: absolute;
|
||||
/* 2.6rem from the end; means .8rem from .sim-indication */
|
||||
offset-inline-end: .8rem;
|
||||
bottom: -0.1rem;
|
||||
|
||||
content: '...';
|
||||
|
||||
color: #b2b2b2;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
button[disabled] .sim-indication {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.has-preferred-sim .sim-indication {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override Building Block [Input areas] styles
|
||||
* We should get rid of all [role="search"] Messages rules in bug 1055357.
|
||||
*/
|
||||
form.bottom[role="search"] {
|
||||
z-index: 3;
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* This file contains styles shared between views when they're in edit mode.
|
||||
* View edit mode is activated via Options -> Select threads/messages.
|
||||
*/
|
||||
|
||||
|
||||
/* Override Building Block [Edit mode] styles */
|
||||
form[role="dialog"][data-type="edit"] gaia-header {
|
||||
transform: translateY(-110%);
|
||||
}
|
||||
|
||||
form[role="dialog"][data-type="edit"] > menu {
|
||||
transform: translateY(110%);
|
||||
}
|
||||
|
||||
/* Add a margin at the end of the last thread or message node to avoid overlay
|
||||
* with the edit mode menu */
|
||||
.edit .edit-container {
|
||||
padding-bottom: 7rem;
|
||||
}
|
||||
|
||||
#main-wrapper.edit [data-type="list"] li a {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.edit .bottom-tab[role="region"] {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Override Building Block Tabs styles */
|
||||
form[role="dialog"][data-type="edit"] .bb-tablist:before {
|
||||
bottom: 5.5rem ;
|
||||
}
|
||||
|
||||
form[role="dialog"][data-type="edit"] .bb-tablist > li {
|
||||
height: 5.5rem ;
|
||||
}
|
||||
|
||||
.bb-tablist > li button {
|
||||
background-position: 50% 50% ;
|
||||
}
|
||||
|
||||
.bb-tablist > li button[data-action="delete-button"] {
|
||||
background-image: url("images/icons/delete.png");
|
||||
}
|
||||
|
||||
/* Lists: edit mode */
|
||||
html[dir="ltr"] .edit [data-type="list"] li > a {
|
||||
transform: translateX(-1.5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit [data-type="list"] li > a {
|
||||
transform: translateX(1.5rem);
|
||||
}
|
||||
|
||||
.edit [data-type="list"] li > label {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
html[dir="ltr"] .edit [data-type="list"] li > label {
|
||||
transform: translateX(2rem);
|
||||
}
|
||||
html[dir="rtl"] .edit [data-type="list"] li > label {
|
||||
transform: translateX(-2rem);
|
||||
}
|
||||
|
||||
.edit [data-type="list"] li > .pack-checkbox input ~ span:after {
|
||||
opacity: 1;
|
||||
}
|
||||
html[dir="ltr"] .edit [data-type="list"] li > .pack-checkbox input ~ span:after {
|
||||
transform: translateX(2.5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit [data-type="list"] li > .pack-checkbox input ~ span:after {
|
||||
transform: translateX(-2.5rem);
|
||||
}
|
||||
|
||||
.edit [data-type="list"] li > a > p {
|
||||
margin-inline-end: 3.4rem;
|
||||
}
|
||||
html[dir="ltr"] .edit [data-type="list"] li > a > p {
|
||||
transform: translateX(5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit [data-type="list"] li > a > p {
|
||||
transform: translateX(-5rem);
|
||||
}
|
||||
|
||||
/* Currently is used in ConversationView only */
|
||||
html[dir="ltr"] .edit [data-type="list"] .danger {
|
||||
transform: translateX(2.5rem);
|
||||
}
|
||||
html[dir="rtl"] .edit [data-type="list"] .danger {
|
||||
transform: translateX(-2.5rem);
|
||||
}
|
||||
|
||||
form[role="dialog"][data-type="edit"] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.edit form[role="dialog"][data-type="edit"] {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.edit form[role="dialog"][data-type="edit"] > menu {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.edit form[role="dialog"][data-type="edit"] gaia-header {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
After Width: | Height: | Size: 513 B |
|
After Width: | Height: | Size: 292 B |
|
After Width: | Height: | Size: 483 B |
|
After Width: | Height: | Size: 289 B |
|
|
@ -0,0 +1,34 @@
|
|||
/* Most of this should probably move to the Building Blocks: Bug 1011418 */
|
||||
|
||||
section[role="notification"] {
|
||||
box-sizing: border-box;
|
||||
z-index: 1;
|
||||
position: relative; /* so that we can compete
|
||||
with absolutely positioned elements */
|
||||
|
||||
overflow: hidden;
|
||||
min-height: 3.5rem;
|
||||
|
||||
background-color: rgba(77,77,77,0.95);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
section[role="notification"] a {
|
||||
color: #9ef2e7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
section[role="notification"] p {
|
||||
margin: .9rem 1.5rem;
|
||||
line-height: 1.7rem;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section[role="notification"] a {
|
||||
color: #9ef2e7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
.panel {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
background: white;
|
||||
|
||||
animation: var(--transition-duration) both ease;
|
||||
}
|
||||
|
||||
.panel {
|
||||
transform: translate(100%); /* BiDi proof */
|
||||
}
|
||||
|
||||
.panel-active {
|
||||
transform: translate(0);
|
||||
}
|
||||
|
||||
.panel-hidden {
|
||||
display: none;
|
||||
}
|
||||
172
assets/webapps/sms.gaiamobile.org/views/shared/style/root.css
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/* Global */
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
color: #000;
|
||||
font-size: 1.6rem;
|
||||
line-height: 2.2rem;
|
||||
vertical-align: middle;
|
||||
background: var(--header-background);
|
||||
}
|
||||
|
||||
/* Text conventions */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul, ol, dl, dt, dd {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
p, li {
|
||||
word-wrap: break-word;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
q {
|
||||
quotes: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
pre, blockquote {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
address {
|
||||
font-style: normal;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
cursor: default;
|
||||
border: none;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1rem;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
color: #ff4e00;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
a abbr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 2rem 0 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
fieldset.first {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
font: 0/0 a;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input, textarea, select, button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none;
|
||||
overflow: auto;
|
||||
height: 4.6rem;
|
||||
}
|
||||
|
||||
input[type="text"], textarea {
|
||||
font-size: 1.3rem;
|
||||
line-height: 4rem;
|
||||
}
|
||||
|
||||
input[type="text"]:focus, textarea:focus {
|
||||
border-color: #7a7a7a;
|
||||
}
|
||||
|
||||
[contenteditable] {
|
||||
outline: none;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Media */
|
||||
img {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
img:-moz-broken {
|
||||
line-height: 1.1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* HTML 5 */
|
||||
article, aside, details, figcaption, figure, footer, header, hgroup,
|
||||
menu, nav, section, video, audio, canvas, progress, meter, time {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
261
assets/webapps/sms.gaiamobile.org/views/shared/style/sms.css
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
overflow-x: hidden !important;
|
||||
overflow-y: hidden !important;
|
||||
}
|
||||
|
||||
.shim-host {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Default alignment in case a dir attribute is specified. */
|
||||
html[dir="ltr"] [dir] { text-align: left; }
|
||||
html[dir="rtl"] [dir] { text-align: right; }
|
||||
|
||||
/*
|
||||
Override BB. Some CSS tweaks for having a better confirm readability
|
||||
*/
|
||||
form[role="dialog"][data-type="confirm"] p {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
form[role="dialog"].error-dialog-show-recipient ul {
|
||||
border: none;
|
||||
}
|
||||
|
||||
form[role="dialog"].error-dialog-show-recipient ul li {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* This does not override the building block, that's why we use a shorter
|
||||
* selector */
|
||||
.error-dialog-show-recipient li {
|
||||
/* future-proof in case BB moves to "text-align: start" */
|
||||
width: -moz-fit-content;
|
||||
|
||||
/* List items are phone numbers */
|
||||
direction: ltr;
|
||||
unicode-bidi: -moz-isolate;
|
||||
}
|
||||
|
||||
gaia-header {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.subheader {
|
||||
position: absolute;
|
||||
z-index: 6; /* should be between 4 and 9 (inclusive). 3 is the z-index for
|
||||
the input form, 10 is the z-index for the header */
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 5rem;
|
||||
}
|
||||
|
||||
/* Container for conversation/new message view */
|
||||
#composer-container {
|
||||
position: relative;
|
||||
|
||||
background: #e1f0ec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flex layout is wrapped by 'flex-composer'. Our flex layout has
|
||||
* 2 well differentiated:
|
||||
* - 'messages-container'
|
||||
* - 'messages-compose-form'
|
||||
*/
|
||||
|
||||
.flex-composer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: 100%;
|
||||
top: 0;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Override Building Block [Lists] styles */
|
||||
[data-type="list"] {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bottom-tab[role="region"] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
[data-type="list"] header {
|
||||
margin: 0 1.5rem;
|
||||
}
|
||||
|
||||
[data-type="list"] li > label {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
[data-type="list"] aside.pack-end {
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
|
||||
/* Panel handling */
|
||||
#main-wrapper {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Loading screen while deleting
|
||||
*/
|
||||
|
||||
#loading.show-loading {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.loading-header {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
#loading [role="status"] {
|
||||
text-align: center;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
#loading p {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.article-list[data-type="list"] ul.contact-list {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
width: calc(100% - 3.1rem);
|
||||
padding: 0 1.5rem;
|
||||
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: It's an adjustment for line-height/padding to prevent bottom of chars
|
||||
* been cut. This fix will be moved to shared list.css once Bug 1144588 landed
|
||||
*/
|
||||
.contact-list .suggestion > .name {
|
||||
line-height: 2.4rem;
|
||||
padding-top: 0.8rem;
|
||||
}
|
||||
|
||||
form[role="dialog"][data-type="action"].contact-prompt > header {
|
||||
height: auto;
|
||||
min-height: 5rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.unknown-contact-header {
|
||||
line-height: 2.5rem;
|
||||
padding: 1.3rem 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
font-size: 2.3rem;
|
||||
}
|
||||
|
||||
.contact-header {
|
||||
/* bottom padding is .9rem because we need .1rem more for the
|
||||
* nice shadow line at the header bottom, defined as a :after element */
|
||||
padding: 0.8rem 0 0.9rem;
|
||||
}
|
||||
|
||||
.contact-header .name {
|
||||
line-height: 3.3rem;
|
||||
padding: 0.1rem 0 0.4rem;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
font-size: 2.3rem;
|
||||
}
|
||||
|
||||
.contact-header .details {
|
||||
line-height: 1.5rem;
|
||||
padding: 0 0 0.7rem;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.contact-header span[data-type=img] {
|
||||
display: block;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.contact-header aside.pack-end {
|
||||
margin-inline-end: -1.5rem; /* compensate the header's 3rem padding */
|
||||
}
|
||||
html[dir="ltr"] .contact-header aside.pack-end { float: right; }
|
||||
html[dir="rtl"] .contact-header aside.pack-end { float: left; }
|
||||
|
||||
.suggestion * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
form[role="dialog"][data-type="action"] > header {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/* Phone details label styles */
|
||||
.phone-type,
|
||||
.phone-type-separator,
|
||||
.phone-carrier,
|
||||
.phone-carrier-separator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-phone-type > .phone-type,
|
||||
.has-phone-type > .phone-type-separator,
|
||||
.has-phone-carrier > .phone-carrier,
|
||||
.has-phone-carrier > .phone-carrier-separator {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
/* Text selection control */
|
||||
html {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.editable-select-mode [contenteditable="true"] {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
/* ellipsis direction hack before bug 883884 fixed */
|
||||
.ellipsis-dir-fix {
|
||||
display: block;
|
||||
width: -moz-fit-content;
|
||||
max-width: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||