Publish Vulpes Preview 15 sources for desktop, Android and Tundra

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

File diff suppressed because one or more lines are too long

View 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>&nbsp;<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>

View file

@ -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));

View file

@ -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));

View file

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

File diff suppressed because one or more lines are too long

View file

@ -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));

View file

@ -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));

View file

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

View file

@ -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));

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -0,0 +1,6 @@
html, body {
height: 100%;
width: 100%;
font-size: 10px;
}

View file

@ -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;
}

View file

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

View file

@ -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;
}

View file

@ -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;
}