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.
50
gaia/apps/ftu/build/build.js
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true */
|
||||
|
||||
var utils = require('utils');
|
||||
var importBuild = require('import-config.js');
|
||||
var DEBUG = false;
|
||||
|
||||
var FtuAppBuilder = function() {
|
||||
};
|
||||
|
||||
// Set destination directory and application directory
|
||||
FtuAppBuilder.prototype.setOptions = function(options) {
|
||||
this.stageDir = utils.getFile(options.STAGE_APP_DIR);
|
||||
this.appDir = utils.getFile(options.APP_DIR);
|
||||
this.gaia = utils.gaia.getInstance(options);
|
||||
this.gaia.stageDir = this.stageDir;
|
||||
this.gaia.gaiaDir = options.GAIA_DIR;
|
||||
this.raptorMode = options.RAPTOR === '1';
|
||||
this.webapp = options.webapp;
|
||||
};
|
||||
|
||||
FtuAppBuilder.prototype.generateManifest = function() {
|
||||
var manifestObject = importBuild.generateManifest(this.webapp, this.gaia);
|
||||
var file = utils.getFile(this.stageDir.path, 'manifest.webapp');
|
||||
|
||||
// Remove role:system from FTU so it gets an icon on the homescreen.
|
||||
// Used to launch the FTU from the homescreen for Raptor tests.
|
||||
if (this.raptorMode) {
|
||||
delete manifestObject.role;
|
||||
}
|
||||
|
||||
var args = DEBUG ? [manifestObject, undefined, 2] : [manifestObject];
|
||||
utils.writeContent(file, JSON.stringify.apply(JSON, args));
|
||||
};
|
||||
|
||||
FtuAppBuilder.prototype.generateAll = function() {
|
||||
this.generateManifest();
|
||||
importBuild.generateConfig('ftu', '/', this.gaia);
|
||||
};
|
||||
|
||||
FtuAppBuilder.prototype.execute = function(options) {
|
||||
this.setOptions(options);
|
||||
this.generateAll();
|
||||
};
|
||||
|
||||
exports.execute = function(options) {
|
||||
utils.copyToStage(options);
|
||||
(new FtuAppBuilder()).execute(options);
|
||||
};
|
||||
22
gaia/apps/ftu/config/large.json
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"default": {
|
||||
"steps": [
|
||||
{
|
||||
"image": "/style/images/tutorial/1_large.png",
|
||||
"l10nKey": "tutorial-step1-large-3"
|
||||
},
|
||||
{
|
||||
"image": "/style/images/tutorial/2_large.png",
|
||||
"l10nKey": "tutorial-step2-large-2"
|
||||
},
|
||||
{
|
||||
"image": "/style/images/tutorial/3_large.png",
|
||||
"l10nKey": "tutorial-step3-large-2"
|
||||
},
|
||||
{
|
||||
"image": "/style/images/tutorial/4_large.png",
|
||||
"l10nKey": "tutorial-step4-large-2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
38
gaia/apps/ftu/config/tiny.json
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"default": {
|
||||
"steps": [
|
||||
{
|
||||
"video": "/style/images/tutorial/Homescreen.mp4",
|
||||
"l10nKey": "tutorial-homescreen-tiny"
|
||||
},
|
||||
{
|
||||
"video": "/style/images/tutorial/Notifications.mp4",
|
||||
"l10nKey": "tutorial-notifications-v2-tiny"
|
||||
},
|
||||
{
|
||||
"video": "/style/images/tutorial/Sheets.mp4",
|
||||
"l10nKey": "tutorial-sheets-tiny"
|
||||
},
|
||||
{
|
||||
"video": "/style/images/tutorial/Universal_Search.mp4",
|
||||
"l10nKey": "tutorial-rocketbar-tiny"
|
||||
}
|
||||
]
|
||||
},
|
||||
"2.0..2.6": {
|
||||
"steps": [
|
||||
]
|
||||
},
|
||||
"2.1..2.6": {
|
||||
"steps": [
|
||||
]
|
||||
},
|
||||
"2.2..2.6": {
|
||||
"steps": [
|
||||
]
|
||||
},
|
||||
"2.5..2.6": {
|
||||
"steps": [
|
||||
]
|
||||
}
|
||||
}
|
||||
754
gaia/apps/ftu/index.html
Executable file
|
|
@ -0,0 +1,754 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<meta name="theme-color" content="#eeeeee">
|
||||
|
||||
<!-- Common style -->
|
||||
<link rel="stylesheet" href="/shared/style/input_areas.css">
|
||||
<link rel="stylesheet" href="/shared/style/buttons.css">
|
||||
<link rel="stylesheet" href="/shared/style/confirm.css">
|
||||
<link rel="stylesheet" href="/shared/style/lists.css">
|
||||
<link rel="stylesheet" href="/shared/style/progress_activity.css">
|
||||
<link rel="stylesheet" href="/shared/style/status.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/shared/style/animations.css">
|
||||
|
||||
<!-- Specific style -->
|
||||
<link rel="stylesheet" href="style/style.css">
|
||||
<link rel="stylesheet" href="style/status.css">
|
||||
<link rel="stylesheet" href="style/overlay.css">
|
||||
<link rel="stylesheet" href="style/extensions.css">
|
||||
<link rel="stylesheet" href="style/tutorial.css">
|
||||
<link rel="stylesheet" href="style/responsive.css">
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/curtain_frame.css">
|
||||
|
||||
<title>Welcome to Browser OS</title>
|
||||
|
||||
<!-- Localization -->
|
||||
<link rel="resource" type="image/png" href="/shared/resources/branding/logosmall.png"/>
|
||||
<link rel="resource" type="image/png" href="/shared/resources/branding/powered.png"/>
|
||||
<link rel="resource" type="image/png" href="/shared/resources/branding/privacy_sprite.png"/>
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="/shared/locales/branding/branding.{locale}.properties">
|
||||
<!-- This is needed for not duplicating SIM Import locales -->
|
||||
<link rel="localization" href="/shared/locales/import_contacts/import_contacts.{locale}.properties">
|
||||
<link rel="localization" href="locales/ftu.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/date/date.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/tz/tz.{locale}.properties">
|
||||
|
||||
<link rel="resource" type="application/json" href="/shared/resources/tz.json"/>
|
||||
<link rel="resource" type="application/json" href="/shared/resources/languages.json"/>
|
||||
<link rel="resource" type="application/json" href="/shared/resources/keyboard_layouts.json"/>
|
||||
<link rel="resource" type="application/json" href="/shared/resources/apn.json"/>
|
||||
<link rel="resource" type="application/json" href="/shared/resources/apn_tz.json"/>
|
||||
<script defer src="/shared/js/intl/l20n.js"></script>
|
||||
<script defer src="/shared/js/tz_select.js"></script>
|
||||
<script defer src="/shared/js/sanitizer.js"></script>
|
||||
<script defer src="/shared/js/input_mgmt/input_app_list.js"></script>
|
||||
<script defer src="/shared/js/keyboard_helper.js"></script>
|
||||
<script defer src="/shared/js/language_list.js"></script>
|
||||
<script defer src="/shared/js/settings_listener.js"></script>
|
||||
<script defer src="/shared/js/version_helper.js"></script>
|
||||
<script defer src="/shared/js/event_safety.js"></script>
|
||||
<script defer src="/shared/js/date_time_helper.js"></script>
|
||||
|
||||
<!-- Import contacts help files -->
|
||||
<script defer src="/shared/js/contacts/import/utilities/status.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/overlay.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/config.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/misc.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/sdcard.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/vcard_parser.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/import_from_vcard.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/utilities/import_sim_contacts.js"></script>
|
||||
|
||||
<!-- Code for the FTU App -->
|
||||
<script defer src="/shared/js/screen_layout.js"></script>
|
||||
<script defer src="/shared/js/text_normalizer.js"></script>
|
||||
<script defer src="/shared/js/lazy_loader.js"></script>
|
||||
<script defer src="/shared/js/basket_client.js"></script>
|
||||
<script defer src="js/services_launcher.js"></script>
|
||||
<script defer src="/shared/js/contacts/import/import_status_data.js"></script>
|
||||
<script defer src="/shared/js/async_storage.js"></script>
|
||||
<script defer src="/shared/js/mobile_operator.js"></script>
|
||||
<script defer src="/shared/js/icc_helper.js"></script>
|
||||
<script defer src="/shared/js/wifi_helper.js"></script>
|
||||
<script defer src="/shared/js/fxa_iac_client.js"></script>
|
||||
<script defer src="js/sim_manager.js"></script>
|
||||
<script defer src="js/memcard_manager.js"></script>
|
||||
<script defer src="js/wifi.js"></script>
|
||||
<script defer src="js/import_services.js"></script>
|
||||
<script defer src="js/utils.js"></script>
|
||||
<script defer src="js/finish_screen.js"></script>
|
||||
<script defer src="js/tutorial_utils.js"></script>
|
||||
<script defer src="js/tutorial.js"></script>
|
||||
<script defer src="js/navigation.js"></script>
|
||||
<script defer src="js/data_mobile.js"></script>
|
||||
<script defer src="js/time.js"></script>
|
||||
<script defer src="js/ui.js"></script>
|
||||
<script defer src="js/language.js"></script>
|
||||
<script defer src="js/app.js"></script>
|
||||
<script defer src="js/operatorVariant.js"></script>
|
||||
|
||||
<!-- Confirm dialogs BB library -->
|
||||
<script defer src="/shared/js/confirm.js"></script>
|
||||
|
||||
<!-- Components -->
|
||||
<script defer src="/shared/js/component_utils.js"></script>
|
||||
<script defer src="/shared/elements/gaia_switch/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia_checkbox/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia_radio/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia_buttons/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia_subheader/script.js"></script>
|
||||
<script defer src="/shared/elements/config.js"></script>
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css" />
|
||||
<script defer src="/shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
<!-- Late Customization -->
|
||||
<script defer src="/shared/js/icons_helper.js"></script>
|
||||
<script defer src="js/late_customization.js"></script>
|
||||
<link rel="stylesheet" href="style/late_customization.css">
|
||||
|
||||
<!--
|
||||
<link href="/shared/pages/import/import.html">
|
||||
<link href="/shared/pages/import/curtain.html">
|
||||
<link href="/shared/pages/import/oauth.html">
|
||||
-->
|
||||
|
||||
<!--
|
||||
<link href="/shared/js/simple_phone_matcher.js">
|
||||
<link href="/shared/js/mime_mapper.js">
|
||||
<link href="/shared/js/contact_photo_helper.js">
|
||||
<link href="/shared/js/contacts/contacts_matcher.js">
|
||||
<link href="/shared/js/contacts/contacts_merger.js">
|
||||
<link href="/shared/js/contacts/utilities/image_thumbnail.js">
|
||||
<link href="/shared/js/contacts/merger_adapter.js">
|
||||
<link href="/shared/js/contacts/utilities/http_rest.js">
|
||||
-->
|
||||
</head>
|
||||
<body id="container" class="theme-settings" aria-hidden="true">
|
||||
<section id="splash-screen" role="region" class="show">
|
||||
</section>
|
||||
|
||||
<section id="unlock-sim-screen" role="region" class="skin-organic">
|
||||
<gaia-header id="unlock-sim-action" action="back">
|
||||
<h1 id="unlock-sim-header"></h1>
|
||||
</gaia-header>
|
||||
<article role="main">
|
||||
<section id="pincode-screen" role="region">
|
||||
<label id="pin-label"></label>
|
||||
<label id="pin-retries-left" class="retries hidden"></label>
|
||||
<section class="input-wrapper">
|
||||
<input id="pin-input" name="simpin" type="password" x-inputmode="digit" size="8" maxlength="8" />
|
||||
<label id="pin-error" class="hidden error">
|
||||
<span data-l10n-id="pinError" class="main">The PIN was incorrect.</span>
|
||||
<span data-l10n-id="pinLastChanceMsg" class="lastchance hidden"></span>
|
||||
</label>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="pukcode-screen" role="region">
|
||||
<label id="puk-label"></label>
|
||||
<label id="puk-retries-left" class="retries hidden"></label>
|
||||
<section class="input-wrapper">
|
||||
<input id="puk-input" name="simpuk" type="password" x-inputmode="digit" size="8" maxlength="8" />
|
||||
<div id="puk-info" data-l10n-id="pukInfo" class="info">You must enter the Personal Unlocking Key (PUK) Code for the SIM card. Refer to your SIM card documentation or contact your carrier for more information.</div>
|
||||
<div id="puk-error" class="hidden error">
|
||||
<span data-l10n-id="pukError" class="main">The PUK was incorrect.</span>
|
||||
<span data-l10n-id="pukLastChanceMsg" class="lastchance hidden"></span>
|
||||
</div>
|
||||
</section>
|
||||
<label id="newpin" data-l10n-id="create_newpin">Create new PIN (4 to 8 digits)</label>
|
||||
<section class="input-wrapper">
|
||||
<input id="newpin-input" name="newpin" type="password" x-inputmode="digit" size="8" maxlength="8" />
|
||||
<label id="newpin-error" data-l10n-id="pinValidation" class="hidden error">PIN must have between 4 and 8 characters.</label>
|
||||
</section>
|
||||
<label id="confirm-newpin" data-l10n-id="confirm_newpin">Confirm new PIN</label>
|
||||
<section class="input-wrapper">
|
||||
<input id="confirm-newpin-input" name="confirm-newpin" type="password" x-inputmode="digit" size="8" maxlength="8" />
|
||||
<label id="confirm-newpin-error" data-l10n-id="confirm_newpin" class="hidden error">Confirm new PIN</label>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="xckcode-screen" role="region">
|
||||
<label id="xck-label" data-l10n-id="type_nck">Type your NCK code</label>
|
||||
<label id="xck-retries-left" class="hidden retries"></label>
|
||||
<section class="input-wrapper">
|
||||
<input id="xck-input" name="simxck" type="password" x-inputmode="digit" size="16" maxlength="16" />
|
||||
<label id="xck-error" class="hidden error">
|
||||
<span data-l10n-id="confirm_newpin" class="main"></span>
|
||||
<span data-l10n-id="nckLastChanceMsg" class="lastchance hidden"></span>
|
||||
</label>
|
||||
</section>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<gaia-buttons class="nav">
|
||||
<button id="skip-pin-button" class="button-left" data-l10n-id="skip">
|
||||
Skip
|
||||
</button>
|
||||
<button id="back-sim-button" class="button-left back hidden" data-l10n-id="back">
|
||||
Back
|
||||
</button>
|
||||
<button id="unlock-sim-button" class="recommend" data-l10n-id="ok">
|
||||
OK
|
||||
</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
|
||||
<section id="sim-info-screen" role="region" class="skin-organic">
|
||||
<gaia-header>
|
||||
<h1 id="sim-info-header" data-l10n-id="simManager"></h1>
|
||||
</gaia-header>
|
||||
<article role="main">
|
||||
<section>
|
||||
<section id="sim-info-1" class="sim-info">
|
||||
<div class="sim-image"></div>
|
||||
<div class="sim-details">
|
||||
<p class="sim-title">
|
||||
<span data-l10n-id="simDetail" data-l10n-args='{"n": "1"}'>SIM 1</span>
|
||||
<span data-l10n-id="simDefault" class="default">(default)</span>
|
||||
</p>
|
||||
<p id="sim-carrier-1" class="sim-carrier"></p>
|
||||
<p id="sim-number-1" class="sim-number"></p>
|
||||
</div>
|
||||
</section>
|
||||
<section id="sim-info-2" class="sim-info">
|
||||
<div class="sim-image"></div>
|
||||
<div class="sim-details">
|
||||
<p class="sim-title">
|
||||
<span data-l10n-id="simDetail" data-l10n-args='{"n": "2"}'>SIM 2</span>
|
||||
</p>
|
||||
<p id="sim-carrier-2" class="sim-carrier"></p>
|
||||
<p id="sim-number-2" class="sim-number"></p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="sim-info-messages">
|
||||
<p id="sim-default-message" data-l10n-id="simDefaultMessage">
|
||||
Outgoing calls, outgoing messages and data connection are set to SIM 1 as default
|
||||
</p>
|
||||
<p id="sim-settings-message" data-l10n-id="simSettingsMessage">
|
||||
You can change default settings at Settings > SIM manager
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<gaia-buttons class="nav">
|
||||
<button id="sim-info-back" class="button-left icon icon-previous" data-l10n-id="navbar-back">Back</button>
|
||||
<button id="sim-info-forward" class="recommend icon icon-next" data-l10n-id="navbar-next">Next</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
|
||||
<section id="activation-screen" role="region" class="skin-organic no-options">
|
||||
<gaia-header>
|
||||
<h1 id="main-title"></h1>
|
||||
<button id="wifi-refresh-button" data-l10n-id="refresh">Refresh</button>
|
||||
</gaia-header>
|
||||
<article role="main">
|
||||
<section role="region" id="languages">
|
||||
<article data-type="list">
|
||||
<ul>
|
||||
<!-- filled by js/language.js -->
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
<section role="region" id="data_3g">
|
||||
<article data-type="list">
|
||||
<ul>
|
||||
<li>
|
||||
<gaia-switch id="data-connection-switch">
|
||||
<label data-l10n-id="dataConnection"></label>
|
||||
</gaia-switch>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<gaia-subheader id="dataCaution" skin="organic">
|
||||
<span data-l10n-id="caution" />
|
||||
</gaia-subheader>
|
||||
<section class="content">
|
||||
<p id="dataExplanation" data-l10n-id="dataConnectionInfo">Some text here</p>
|
||||
</section>
|
||||
</section>
|
||||
<section role="region" id="wifi">
|
||||
<div id="wifi-wrapper" role="main">
|
||||
<article id="networks">
|
||||
</article>
|
||||
<button id="join-hidden-button" data-l10n-id="join-hidden-button">
|
||||
Join hidden network
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section role="region" id="configure_network">
|
||||
<section id="configure_network_params">
|
||||
<form data-type="list">
|
||||
<ul>
|
||||
<li class="hidden">
|
||||
<input type="text" id="wifi_ssid" value="" dir="auto"></input>
|
||||
</li>
|
||||
<li>
|
||||
<label id="label_wifi_user" for="wifi_user" data-l10n-id="identity">User</label>
|
||||
<input type="text" id="wifi_user" data-l10n-id="identityInput" placeholder="User" x-inputmode="verbatim" dir="auto"></input>
|
||||
</li>
|
||||
<li>
|
||||
<label for="wifi_password" data-l10n-id="password">Password</label>
|
||||
<input type="password" id="wifi_password" data-l10n-id="passwordInput" placeholder="Password" value="" maxlength="63" x-inputmode="verbatim"></input>
|
||||
<gaia-checkbox class="inline" id="label_show_password" name="show_password">
|
||||
<label id="wifi_show_password" data-l10n-id="showPassword"></label>
|
||||
</gaia-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
<section role="region" id="hidden-wifi-authentication">
|
||||
<div>
|
||||
<form data-type="list">
|
||||
<ul>
|
||||
<li>
|
||||
<label id="label_wifi_ssid" for="hidden-wifi-ssid" data-l10n-id="ssid-name">
|
||||
SSID Network Name
|
||||
</label>
|
||||
<input type="text" name="wifi_ssid" id="hidden-wifi-ssid" dir="auto"/>
|
||||
</li>
|
||||
<li>
|
||||
<label id="label_wifi_security" for="hidden-wifi-security" data-l10n-id="security">
|
||||
Security
|
||||
</label>
|
||||
<span class="button icon icon-dialog">
|
||||
<select id="hidden-wifi-security">
|
||||
<option data-l10n-id="securityNone">none</option>
|
||||
<option data-l10n-id="securityWEP">WEP</option>
|
||||
<option data-l10n-id="securityWPA-PSK" selected>WPA-PSK</option>
|
||||
<option data-l10n-id="securityWPA-EAP">WPA-EAP</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<li class="hidden" id="hidden-wifi-identity-box">
|
||||
<label id="label_wifi_identity" for="hidden-wifi-identity" data-l10n-id="identity">
|
||||
Identity
|
||||
</label>
|
||||
<input type="text" name="wifi_identity" id="hidden-wifi-identity" dir="auto"/>
|
||||
</li>
|
||||
<li id="hidden-wifi-password-box">
|
||||
<label id="label_hidden_wifi_password" for="hidden-wifi-password" data-l10n-id="password">
|
||||
Password
|
||||
</label>
|
||||
<input type="password" name="wifi_hidden_password" id="hidden-wifi-password" value="" x-inputmode="verbatim" maxlength="63" />
|
||||
<gaia-checkbox class="inline" id="hidden-wifi-show-password">
|
||||
<label data-l10n-id="showPassword"></label>
|
||||
</gaia-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section role="region" id="date_and_time">
|
||||
<section id="time_zone_overlay" class="gmt0">
|
||||
</section>
|
||||
<section id="time_zone_map">
|
||||
<h1 data-l10n-id="timeZone">
|
||||
Time zone
|
||||
</h1>
|
||||
<h2 id="time-zone-title">
|
||||
UTC +00:00 Greenwich Mean Time
|
||||
</h2>
|
||||
</section>
|
||||
<form id="time-form" data-type="list">
|
||||
<ul>
|
||||
<!-- timezones -->
|
||||
<li>
|
||||
<p id="tz-region-label" aria-label="region" data-l10n-id="region"></p>
|
||||
<select id="tz-region" class="invisible"></select>
|
||||
<button id="tz-region-button" class="change icon icon-dialog" data-l10n-id="change" type="button" aria-describedby="tz-region-label">
|
||||
Change
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<p id="tz-city-label" aria-label="city" data-l10n-id="city">GMT</p>
|
||||
<select id="tz-city" class="invisible"></select>
|
||||
<button id="tz-city-button" class="change icon icon-dialog" data-l10n-id="change" type="button" aria-describedby="tz-city-label">
|
||||
Change
|
||||
</button>
|
||||
</li>
|
||||
<!-- date/time -->
|
||||
<li>
|
||||
<p id="date-configuration-label" aria-label="date" data-l10n-id="date"></p>
|
||||
<input id="date-configuration" class="invisible" type="date" min="1970-1-1" max="2037-12-31"></input>
|
||||
<button id="date-configuration-button" class="change icon icon-dialog" data-l10n-id="change" aria-describedby="date-configuration-label">
|
||||
Change
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<p id="time-configuration-label" aria-label="time" data-l10n-id="time">00:00</p>
|
||||
<input id="time-configuration" class="invisible" type="time"></input>
|
||||
<button id="time-configuration-button" class="change icon icon-dialog" data-l10n-id="change" type="button" aria-describedby="time-configuration-label">
|
||||
Change
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</section>
|
||||
<section role="region" id="geolocation">
|
||||
<article data-type="list">
|
||||
<ul>
|
||||
<li>
|
||||
<gaia-switch id="geolocation-switch" name="geolocation.enabled" checked>
|
||||
<label data-l10n-id="geolocationCheckbox"></label>
|
||||
</gaia-switch>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<gaia-subheader id="geolocationNotice" skin="organic">
|
||||
<span data-l10n-id="notice" />
|
||||
</gaia-subheader>
|
||||
<section class="content">
|
||||
<p id="geolocationInfo" data-l10n-id="geolocationInfo2">Some text here</p>
|
||||
<p id="geolocationInfoMore" data-l10n-id="geolocationInfoMore">Some text here</p>
|
||||
<p><a href="#about-your-privacy" data-l10n-id="aboutYourPrivacy">Your Privacy</a></p>
|
||||
</section>
|
||||
</section>
|
||||
<section role="region" id="import_contacts">
|
||||
<article data-type="list">
|
||||
<ul>
|
||||
<li id="sim-import" class="importOption">
|
||||
<button id="sim-import-button" class="icon icon-sim" data-l10n-id="importSim2">
|
||||
SIM card
|
||||
</button>
|
||||
<p id="no-sim" data-l10n-id="noSimMsg">To import insert a SIM card</p>
|
||||
</li>
|
||||
<li id="sd_import" class="importOption">
|
||||
<button id="sd-import-button" class="icon icon-sd" data-l10n-id="importMemoryCard">
|
||||
SD card
|
||||
</button>
|
||||
<p id="no-memorycard" data-l10n-id="noMemoryCardMsg">To import insert a SD card</p>
|
||||
</li>
|
||||
<li id="gmail_import" class="importOption">
|
||||
<button id="gmail-import-button" class="icon icon-gmail" data-l10n-id="importGmail">
|
||||
GMail
|
||||
</button>
|
||||
</li>
|
||||
<li id="live_import" class="importOption">
|
||||
<button id="live-import-button" class="icon icon-wlive" data-l10n-id="importOutlook">
|
||||
Outlook
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section role="region" id="firefox_accounts">
|
||||
<div class="fxa-logo"></div>
|
||||
|
||||
<p data-l10n-id="fxa-upsell" id="fxa-intro"></p>
|
||||
|
||||
<p>
|
||||
<button id="fxa-create-account" data-l10n-id="fxa-create-account-or-sign-in">
|
||||
Create Account or Sign In
|
||||
</button>
|
||||
</p>
|
||||
|
||||
</section>
|
||||
|
||||
<section role="region" id="about-your-rights">
|
||||
<p data-l10n-id="about-your-rights-0">
|
||||
Browser OS is built on free and open source software
|
||||
by a community of thousands from all over the world.
|
||||
There are a few things you should know:
|
||||
</p>
|
||||
<p data-l10n-id="about-your-rights-1">
|
||||
Browser OS is made available to you under the terms of several
|
||||
open source licenses including the Mozilla Public License.
|
||||
A device running Browser OS may also contain proprietary
|
||||
software from third parties. Any code provided under open licenses
|
||||
give you the right to modify the source code and distribute your
|
||||
modified versions as long as you comply with their terms.
|
||||
</p>
|
||||
<p data-l10n-id="about-your-rights-2">
|
||||
You are not granted any trademark rights or licenses to the trademarks
|
||||
of the Mozilla Foundation or any party, including without limitation
|
||||
the Browser OS name or logo.
|
||||
</p>
|
||||
<p data-l10n-id="about-your-rights-3">
|
||||
Some features in Browser OS, such as the Crash Reporter,
|
||||
give you the option to provide feedback to Mozilla.
|
||||
By choosing to submit feedback, you give Mozilla permission to use
|
||||
the feedback to improve its products, to publish the feedback
|
||||
on its websites, and to distribute the feedback.
|
||||
</p>
|
||||
</section>
|
||||
<section role="region" id="about-your-privacy">
|
||||
<i class='hack-first-child'></i> <!-- see Bug 819886 -->
|
||||
<article data-type="list">
|
||||
<ul>
|
||||
<li>
|
||||
<a class="privacy-menuitem privacy-browserOS external"
|
||||
href="https://www.mozilla.org/privacy/firefox-os"
|
||||
data-l10n-id="privacy-b2g"
|
||||
title="B2G OS">Browser OS</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="privacy-menuitem privacy-marketPlace external"
|
||||
href="https://marketplace.firefox.com/privacy-policy"
|
||||
data-l10n-id="privacy-marketplace"
|
||||
title="Marketplace">
|
||||
Marketplace
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
<section role="region" id="sharing-performance-data">
|
||||
<p data-l10n-id='telemetry-sends2'>
|
||||
Telemetry sends Mozilla data about performance, hardware,
|
||||
configurations, customizations and app usage in your
|
||||
Browser OS device. We use this to make our products better,
|
||||
faster and more responsive.
|
||||
</p>
|
||||
<!-- Links destinations are discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=812956 -->
|
||||
<p data-l10n-id='learn-more-telemetry2'
|
||||
data-l10n-args='{"url": "www.mozilla.org/telemetry"}'>
|
||||
<a href="http://www.mozilla.org/telemetry" class="external"></a>
|
||||
</p>
|
||||
<p data-l10n-id='learn-more-information2'
|
||||
data-l10n-args='{"url": "www.mozilla.org/privacy"}'>
|
||||
<a href="http://www.mozilla.org/privacy" class="external"></a>
|
||||
</p>
|
||||
</section>
|
||||
<section role="region" id="SIM_mandatory">
|
||||
<p data-l10n-id="SIM_mandatory_warm">
|
||||
Please, insert a SIM card to continue.
|
||||
</p>
|
||||
</section>
|
||||
<section role="region" id="browser_privacy">
|
||||
<p data-l10n-id="mozillaCommunity2">
|
||||
When you use Browser OS, you become
|
||||
part of a global community helping to
|
||||
build a brighter future for the Web.
|
||||
If you’d like to know more about the
|
||||
Mozilla community, our other products
|
||||
and events near you, please enter your
|
||||
email address below.
|
||||
</p>
|
||||
<section id="newsletter-form">
|
||||
<input type="email" data-l10n-id="emailAddress" placeholder="Email address" id="newsletter-input"></input>
|
||||
</section>
|
||||
<section id="newsletter-success-screen">
|
||||
<p data-l10n-id="email-successfully-added">
|
||||
Email successfully added!
|
||||
</p>
|
||||
</section>
|
||||
<p data-l10n-id="learn-more-privacy2">
|
||||
<a href="http://www.mozilla.org/privacy/firefox-os" class="external"></a>
|
||||
</p>
|
||||
</section>
|
||||
<section role="region" id="welcome_browser">
|
||||
<section id="browser_logo_horizontal">
|
||||
<img src="/shared/resources/branding/logosmall.png" role="presentation">
|
||||
</section>
|
||||
<p data-l10n-id="helpImprove3">
|
||||
<a href="https://www.mozilla.org/privacy/firefox-os" class="external"></a>
|
||||
</p>
|
||||
<p data-l10n-id="learn-more-privacy3">
|
||||
<a href="http://www.mozilla.org/privacy/firefox-os" class="external"></a>
|
||||
</p>
|
||||
<section id="form_share_statistics">
|
||||
<ul>
|
||||
<li id="menuItem-basicmetrics">
|
||||
<gaia-radio name="metricsGroup" id="metrics-basic" value="Basic">
|
||||
<label data-l10n-id="basicMetricsRadio"></label>
|
||||
<details data-l10n-id="basicMetricsExplain"></details>
|
||||
</gaia-radio>
|
||||
</li>
|
||||
<li id="menuItem-enhancedMetrics">
|
||||
<gaia-radio name="metricsGroup" id="metrics-enhanced" value="Enhanced">
|
||||
<label data-l10n-id="enhancedMetricsRadio"></label>
|
||||
<details data-l10n-id="enhancedMetricsExplain"></details>
|
||||
</gaia-radio>
|
||||
</li>
|
||||
<li id="menuItem-noneMetrics">
|
||||
<gaia-radio name="metricsGroup" id="metrics-none" value="None">
|
||||
<label data-l10n-id="noneMetricsRadio"></label>
|
||||
<details data-l10n-id="noneMetricsExplain"></details>
|
||||
</gaia-radio>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
</article>
|
||||
<gaia-buttons id="nav-bar" class="nav forward-only">
|
||||
<button id="back" class="button-left icon icon-previous" data-l10n-id="navbar-back">Back</button>
|
||||
<button id="forward" class="recommend icon icon-next" data-l10n-id="navbar-next">Next</button>
|
||||
<button id="wifi-join-button" class="recommend" data-l10n-id="navbar-join">Join</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
|
||||
<section id="finish-screen" class="finish-screen-base" role="region">
|
||||
<section class="content">
|
||||
|
||||
<!-- for tiny devices -->
|
||||
<div class="for-tiny">
|
||||
<h1 data-l10n-id="startYourPhone">
|
||||
Start your Phone Tour!
|
||||
</h1>
|
||||
<h2 data-l10n-id="joinTutorial">
|
||||
Join us for a quick tutorial about your new phone.
|
||||
</h2>
|
||||
<p data-l10n-id="optionsAvailable">
|
||||
You can alter the language, Date / Time, Wi-Fi connection in the settings area.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- for large devices -->
|
||||
<div class="for-large">
|
||||
<p data-l10n-id="startYourTablet">
|
||||
Start your tablet tour!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<gaia-buttons skin="dark" class="nav">
|
||||
<button id="skip-tutorial-button" class="button-left" data-l10n-id="skip">
|
||||
Skip
|
||||
</button>
|
||||
<button id="lets-go-button" class="recommend" data-l10n-id="startTour">
|
||||
Start Tour!
|
||||
</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="update-screen" class="finish-screen-base" role="region">
|
||||
<section class="content">
|
||||
<h1 data-l10n-id="startYourPhone">
|
||||
Start your phone tour!
|
||||
</h1>
|
||||
<p data-l10n-id="joinTutorialUpdated">
|
||||
Join us for a quick tutorial about the updates to your phone!
|
||||
</p>
|
||||
</section>
|
||||
<gaia-buttons skin="dark" class="nav">
|
||||
<button id="update-skip-tutorial-button" class="button-left" data-l10n-id="skip">
|
||||
Skip
|
||||
</button>
|
||||
<button id="update-lets-go-button" class="recommend" data-l10n-id="startTour">
|
||||
Start Tour!
|
||||
</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
|
||||
<section id="tutorial" role="region" data-step="1">
|
||||
<article id="tutorial-steps-container" role="main">
|
||||
<section id="tutorial-step-header">
|
||||
<p id="tutorial-step-title">
|
||||
</p>
|
||||
</section>
|
||||
<section id="tutorial-step-media">
|
||||
<img id="tutorial-step-image" class="step-img-base" role="presentation">
|
||||
<video id="tutorial-step-video" class="step-video-base" role="presentation" autoplay hidden loop></video>
|
||||
</section>
|
||||
</article>
|
||||
<gaia-buttons id="tutorial-nav-bar" class="nav" skin="dark">
|
||||
<button id="back-tutorial" class="button-left icon icon-previous" data-l10n-id="navbar-back">Back</button>
|
||||
<button id="forward-tutorial" class="recommend icon icon-next" data-l10n-id="navbar-next">Next</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
<section id="tutorial-finish-tiny" class="tutorial-finish-base" role="region">
|
||||
<section class="content">
|
||||
<h1 data-l10n-id="thatsIt">
|
||||
That's it!
|
||||
</h1>
|
||||
<h2 id="enjoyYourPhone" data-l10n-id="enjoyYourPhone">
|
||||
You're ready to enjoy your new phone ;)
|
||||
</h2>
|
||||
<h2 id="enjoyYourPhoneUpdated" data-l10n-id="enjoyYourPhoneUpdated" hidden>
|
||||
You're ready to enjoy your updated phone.
|
||||
</h2>
|
||||
</section>
|
||||
<gaia-buttons skin="dark" class="forward-only">
|
||||
<button class="recommend" id="tutorialFinished" data-l10n-id="letsGo">
|
||||
</button>
|
||||
</menu>
|
||||
</section>
|
||||
</section>
|
||||
<section id="tutorial-finish-large" class="tutorial-finish-base" role="region">
|
||||
<section class="content">
|
||||
<p data-l10n-id="thatsIt1Tablet-2">
|
||||
You can always swipe from the bottom edge to enter home screen.
|
||||
</p>
|
||||
<p data-l10n-id="thatsIt2Tablet-2">
|
||||
Let's do it !
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<form role='dialog' data-type='confirm' id='offline-error-dialog'>
|
||||
<section>
|
||||
<h1 data-l10n-id='offline-dialog-title'>You must be connected to the Internet to view the page.</h1>
|
||||
<p><small>Please go back and connect to
|
||||
the Internet. You can also view the page later from Settings or by
|
||||
entering {{url}} in any browser.</small></p>
|
||||
<gaia-buttons skin="dark">
|
||||
<button data-l10n-id='OK'>OK</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<form role='dialog' data-type='confirm' id='offline-newsletter-error-dialog'>
|
||||
<section>
|
||||
<h1 data-l10n-id='offline-newsletter-dialog-title'>You must be connected to the Internet to subscribe to the newsletter.</h1>
|
||||
<p><small data-l10n-id='offline-newsletter-dialog-text'>Please go back and connect to
|
||||
the Internet.</small></p>
|
||||
<gaia-buttons skin="dark">
|
||||
<button data-l10n-id='OK'>OK</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<form role='dialog' data-type='confirm' id='invalid-email-error-dialog'>
|
||||
<section>
|
||||
<h1 data-l10n-id='invalid-email-dialog-title'>Your email is not valid</h1>
|
||||
<p><small data-l10n-id='invalid-email-dialog-text'>Please go back and correct your entry.</small></p>
|
||||
<gaia-buttons skin="dark">
|
||||
<button data-l10n-id='OK'>OK</button>
|
||||
</gaia-buttons>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<form id="confirmation-message" class="hide" role="dialog" data-type="confirm">
|
||||
<section>
|
||||
<h1>Confirmation title</h1>
|
||||
<p>Confirmation message</p>
|
||||
</section>
|
||||
<menu>
|
||||
<button>Action 1</button>
|
||||
<button>Action 2</button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<form data-type="confirm" id="loading-overlay" role="dialog" class="hide no-opacity">
|
||||
<section id="progress-activity" class="hide">
|
||||
<h1 id="progress-title"></h1>
|
||||
<p role="status">
|
||||
<span id="progress-msg"></span>
|
||||
<progress id="progress-element" class="pack-activity" value="0" max="100"></progress>
|
||||
</p>
|
||||
<menu>
|
||||
<button data-l10n-id="cancel" id="cancel-overlay">Cancel</button>
|
||||
</menu>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<section id="statusMsg" role="status" class="ftu">
|
||||
<p></p>
|
||||
</section>
|
||||
|
||||
<iframe id="iframe_curtain" src="/shared/pages/import/curtain.html"></iframe>
|
||||
<iframe id="iframe_oauth" hidden src="/shared/pages/import/oauth.html"></iframe>
|
||||
<iframe id="iframe_extensions"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
125
gaia/apps/ftu/js/app.js
Executable file
|
|
@ -0,0 +1,125 @@
|
|||
/* global DataMobile, Navigation, SimManager, TimeManager,
|
||||
UIManager, WifiManager, ImportIntegration, Tutorial,
|
||||
VersionHelper, LanguageManager, eventSafety */
|
||||
/* exported AppManager */
|
||||
'use strict';
|
||||
|
||||
var AppManager = {
|
||||
EVENT_PREFIX: 'ftu-',
|
||||
|
||||
init: function init(versionInfo) {
|
||||
window.performance.mark('initialize');
|
||||
this.isInitialized = true;
|
||||
|
||||
LanguageManager.init();
|
||||
UIManager.init();
|
||||
Navigation.init();
|
||||
|
||||
window.performance.mark('navigationLoaded');
|
||||
this.publish('setup');
|
||||
|
||||
var splashTimeout = 1025;
|
||||
var splashScreenHidden = this.whenEvent(UIManager.splashScreen,
|
||||
'transitionend',
|
||||
splashTimeout).then(() => {
|
||||
UIManager.container.removeAttribute('aria-hidden');
|
||||
window.performance.mark('visuallyLoaded');
|
||||
});
|
||||
|
||||
var languageListReady = this.whenEvent(window, 'languagelistready');
|
||||
|
||||
Promise.all([splashScreenHidden, languageListReady]).then(() => {
|
||||
window.performance.mark('fullyLoaded');
|
||||
});
|
||||
|
||||
// if it's an upgrade we can jump to tutorial directly
|
||||
if (versionInfo && versionInfo.isUpgrade()) {
|
||||
var stepsKey = versionInfo.delta();
|
||||
// Play the FTU Tuto steps directly on update
|
||||
UIManager.changeStatusBarColor(UIManager.DARK_THEME);
|
||||
UIManager.splashScreen.classList.remove('show');
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.updateScreen.classList.add('show');
|
||||
|
||||
// Load and play the what's new tutorial
|
||||
Tutorial.init(stepsKey, function() {
|
||||
Tutorial.start();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
SimManager.init();
|
||||
WifiManager.init();
|
||||
ImportIntegration.init();
|
||||
TimeManager.init();
|
||||
DataMobile.init();
|
||||
Tutorial.init();
|
||||
|
||||
var kSplashTimeout = 700;
|
||||
// Retrieve mobile connection if available
|
||||
// this is used to keep all tests passing while introducing multi-sim APIs
|
||||
var conn = window.navigator.mozMobileConnections &&
|
||||
window.navigator.mozMobileConnections[0];
|
||||
|
||||
if (!conn) {
|
||||
setTimeout(function() {
|
||||
// For desktop
|
||||
window.location.hash = '#';
|
||||
UIManager.activationScreen.classList.add('show');
|
||||
window.location.hash = '#languages';
|
||||
|
||||
UIManager.splashScreen.classList.remove('show');
|
||||
window.performance.mark('navigationInteractive');
|
||||
window.performance.mark('contentInteractive');
|
||||
}, kSplashTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do we need pin code after splash screen?
|
||||
setTimeout(function() {
|
||||
// TODO Include VIVO SIM Card management
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=801269#c6
|
||||
Navigation.manageStep();
|
||||
UIManager.activationScreen.classList.add('show');
|
||||
// Remove the splash
|
||||
UIManager.splashScreen.classList.remove('show');
|
||||
window.performance.mark('navigationInteractive');
|
||||
window.performance.mark('contentInteractive');
|
||||
}, kSplashTimeout);
|
||||
},
|
||||
whenEvent: function (target, name, timeoutMs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
eventSafety(target, name, resolve, timeoutMs || 1000);
|
||||
});
|
||||
},
|
||||
publish: function(name, detail) {
|
||||
var evt = new CustomEvent(this.EVENT_PREFIX + name,
|
||||
{
|
||||
bubbles: true,
|
||||
detail: detail || this
|
||||
});
|
||||
window.dispatchEvent(evt);
|
||||
}
|
||||
};
|
||||
|
||||
function init() {
|
||||
VersionHelper.getVersionInfo().then(function(versionInfo) {
|
||||
if (!AppManager.isInitialized) {
|
||||
AppManager.init(versionInfo);
|
||||
} else {
|
||||
UIManager.initTZ();
|
||||
if (!UIManager.mainTitle.hasAttribute('data-l10n-id')) {
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'language');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
init();
|
||||
document.addEventListener('DOMRetranslated', init);
|
||||
|
||||
document.l10n.ready.then(() => {
|
||||
window.performance.mark('l10nready');
|
||||
});
|
||||
|
||||
115
gaia/apps/ftu/js/data_mobile.js
Executable file
|
|
@ -0,0 +1,115 @@
|
|||
/* exported DataMobile */
|
||||
/* global Navigation */
|
||||
/* global UIManager */
|
||||
|
||||
'use strict';
|
||||
|
||||
var DataMobile = {
|
||||
key: 'ril.data.enabled',
|
||||
keySV: 'ftu.ril.data.enabled',
|
||||
STEP_DATA_3G: 2,
|
||||
apnRetrieved: false,
|
||||
|
||||
init: function dm_init() {
|
||||
var settings = navigator.mozSettings;
|
||||
if (!settings) {
|
||||
console.log('Settings is not available');
|
||||
return;
|
||||
}
|
||||
this.settings = settings;
|
||||
},
|
||||
|
||||
removeSVStatusObserver: function dm_removeSVStatusObserver() {
|
||||
this.settings.removeObserver(this.keySV, this.getStatus);
|
||||
},
|
||||
|
||||
getStatus: function dm_getStatus(aCallback) {
|
||||
var self = this;
|
||||
var reqSV = this.settings.createLock().get(this.keySV);
|
||||
reqSV.onsuccess = function gst_svsuccess() {
|
||||
// We need to be sure that we are on Cellular Data screen
|
||||
// if the user has passed the screen very fast this configuration will
|
||||
// have no effect
|
||||
var svStatus = reqSV.result[self.keySV];
|
||||
if (svStatus !== undefined && !UIManager.dataConnectionChangedByUsr) {
|
||||
self.settings.removeObserver(self.keySV, self.getStatus);
|
||||
if (Navigation.currentStep === self.STEP_DATA_3G) {
|
||||
self.toggle(svStatus);
|
||||
if (typeof aCallback === 'function') {
|
||||
aCallback(svStatus);
|
||||
}
|
||||
} else {
|
||||
self.getRealStatus(aCallback);
|
||||
}
|
||||
} else {
|
||||
self.getRealStatus(aCallback);
|
||||
}
|
||||
};
|
||||
|
||||
reqSV.onerror = function gst_error() {
|
||||
console.log('Error retrieving ' + self.keySV);
|
||||
};
|
||||
|
||||
this.settings.addObserver(this.keySV, this.getStatus.bind(this, aCallback));
|
||||
},
|
||||
|
||||
getRealStatus: function dm_getRealStatus(callback) {
|
||||
var request = this.settings.createLock().get(this.key);
|
||||
var self = this;
|
||||
request.onsuccess = function gst_success() {
|
||||
var currentStatus = request.result[self.key];
|
||||
self.isDataAvailable = currentStatus;
|
||||
callback(currentStatus);
|
||||
};
|
||||
request.onerror = function gst_error() {
|
||||
console.log('Error retrieving ' + self.key);
|
||||
};
|
||||
},
|
||||
|
||||
toggle: function dm_toggle(status, callback) {
|
||||
var options = {};
|
||||
options[this.key] = status;
|
||||
if (!this.apnRetrieved) {
|
||||
// I need to retrieve APN
|
||||
var self = this;
|
||||
this.getAPN(function apn_recovered() {
|
||||
self.settings.createLock().set(options);
|
||||
self.apnRetrieved = true;
|
||||
self.isDataAvailable = status;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.settings.createLock().set(options);
|
||||
this.apnRetrieved = true;
|
||||
this.isDataAvailable = status;
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
getAPN: function dm_getapn(callback) {
|
||||
// By the time the APN settings are needed in the FTU before enabling data
|
||||
// calls the system app (through the operator variant logic) might store the
|
||||
// APN settings into the settings database. If not wait for that before
|
||||
// enabling data calls.
|
||||
var _self = this;
|
||||
function ensureApnSettings() {
|
||||
var req = _self.settings.createLock().get('ril.data.apnSettings');
|
||||
req.onsuccess = function loadApn() {
|
||||
var apnSettings = req.result['ril.data.apnSettings'];
|
||||
if (apnSettings) {
|
||||
if (callback) {
|
||||
callback(req.result);
|
||||
}
|
||||
_self.settings.removeObserver('ril.data.apnSettings',
|
||||
ensureApnSettings);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ensureApnSettings();
|
||||
this.settings.addObserver('ril.data.apnSettings', ensureApnSettings);
|
||||
}
|
||||
};
|
||||
70
gaia/apps/ftu/js/finish_screen.js
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
/* exported FinishScreen */
|
||||
/* global ScreenLayout, Utils */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
// Defaut layout is 'tiny', the one for mobile device
|
||||
var currentLayout = 'tiny';
|
||||
|
||||
var elementIDs = [
|
||||
'tutorialFinished',
|
||||
'enjoyYourPhone',
|
||||
'enjoyYourPhoneUpdated'
|
||||
];
|
||||
|
||||
var dom = {};
|
||||
|
||||
var initialized = false;
|
||||
|
||||
var FinishScreen = {
|
||||
init: function (isUpgrade) {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We consider only 'tablet' & 'mobile' (tiny)
|
||||
if (ScreenLayout.getCurrentLayout() !== 'tiny') {
|
||||
currentLayout = 'large';
|
||||
}
|
||||
// Get panel selector based on layout
|
||||
var panelSelector = 'tutorial-finish-' + currentLayout;
|
||||
|
||||
// Show finish panel
|
||||
var finishPanel = document.getElementById(panelSelector);
|
||||
finishPanel.classList.add('show');
|
||||
|
||||
// Cache non-layout-related DOM elements
|
||||
elementIDs.forEach(function (name) {
|
||||
dom[Utils.camelCase(name)] = document.getElementById(name);
|
||||
}, this);
|
||||
|
||||
if (isUpgrade) {
|
||||
dom.enjoyYourPhone.hidden = true;
|
||||
dom.enjoyYourPhoneUpdated.hidden = false;
|
||||
}
|
||||
|
||||
// Add regular listener if needed
|
||||
if (currentLayout === 'tiny') {
|
||||
dom.tutorialFinished.addEventListener('click', function ftuEnd() {
|
||||
window.close();
|
||||
});
|
||||
} else {
|
||||
// In 'tablet', we have to use IAC to tell system ftu is done
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
app.connect('ftucomms').then(function onConnAccepted(ports) {
|
||||
ports.forEach(function(port) {
|
||||
port.postMessage('done');
|
||||
});
|
||||
}, function onConnRejected(reason) {
|
||||
console.warn('FTU is rejected due to ' + reason);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
exports.FinishScreen = FinishScreen;
|
||||
|
||||
}(this));
|
||||
48
gaia/apps/ftu/js/import_services.js
Executable file
|
|
@ -0,0 +1,48 @@
|
|||
/* global ServicesLauncher */
|
||||
/* exported ImportIntegration */
|
||||
'use strict';
|
||||
|
||||
var ImportIntegration = {
|
||||
|
||||
get liveImportButton() {
|
||||
delete this.liveImportButton;
|
||||
return (this.liveImportButton =
|
||||
document.getElementById('live-import-button'));
|
||||
},
|
||||
|
||||
get gmailImportButton() {
|
||||
delete this.gmailImportButton;
|
||||
return (this.gmailImportButton =
|
||||
document.getElementById('gmail-import-button'));
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this.liveImportButton.addEventListener('click', this);
|
||||
this.gmailImportButton.addEventListener('click', this);
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
switch (event.type) {
|
||||
case 'click':
|
||||
if (event.target === this.liveImportButton) {
|
||||
ServicesLauncher.start('live');
|
||||
}
|
||||
else if (event.target === this.gmailImportButton) {
|
||||
ServicesLauncher.start('gmail');
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
checkImport: function (nextState) {
|
||||
|
||||
if (nextState === 'disabled') {
|
||||
this.gmailImportButton.setAttribute('disabled', 'disabled');
|
||||
this.liveImportButton.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
else if (nextState === 'enabled') {
|
||||
this.gmailImportButton.removeAttribute('disabled');
|
||||
this.liveImportButton.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
};
|
||||
85
gaia/apps/ftu/js/language.js
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
/* global KeyboardHelper, LanguageList */
|
||||
/* exported LanguageManager */
|
||||
'use strict';
|
||||
|
||||
var LanguageManager = {
|
||||
settings: window.navigator.mozSettings,
|
||||
|
||||
init: function init() {
|
||||
this.buildLanguageList();
|
||||
document.getElementById('languages').addEventListener('change', this);
|
||||
|
||||
var onSettingChange = this._boundHandleSettingChange =
|
||||
this.handleSettingChange.bind(this);
|
||||
this.settings.addObserver('language.current', onSettingChange);
|
||||
this.settings.addObserver('accessibility.screenreader', onSettingChange);
|
||||
},
|
||||
|
||||
uninit: function uninit() {
|
||||
if (this._boundHandleSettingChange) {
|
||||
this.settings.removeObserver('language.current',
|
||||
this._boundHandleSettingChange);
|
||||
this.settings.removeObserver('accessibility.screenreader',
|
||||
this._boundHandleSettingChange);
|
||||
}
|
||||
},
|
||||
|
||||
handleSettingChange: function(evt) {
|
||||
switch (evt.settingName) {
|
||||
case 'accessibility.screenreader':
|
||||
this.buildLanguageList();
|
||||
break;
|
||||
case 'language.current':
|
||||
// the 2nd parameter is to reset the current enabled layouts
|
||||
KeyboardHelper.changeDefaultLayouts(evt.settingValue, true);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(evt) {
|
||||
if (!this.settings || evt.target.name != 'language.current') {
|
||||
return true;
|
||||
}
|
||||
this.setLanguage(evt.target.value);
|
||||
return false;
|
||||
},
|
||||
|
||||
// update current launguage settings
|
||||
setLanguage: function settings_setLanguage(language) {
|
||||
this.settings.createLock().set({
|
||||
'language.current': language
|
||||
});
|
||||
},
|
||||
|
||||
buildLanguageList: function settings_buildLanguageList() {
|
||||
var panel = document.querySelector('#languages');
|
||||
var container = panel.querySelector('ul');
|
||||
container.innerHTML = '';
|
||||
LanguageList.get(function fillLanguageList(allLanguages, currentLanguage) {
|
||||
for (var lang in allLanguages) {
|
||||
var label = document.createElement('label');
|
||||
// wrap the name of the language in Unicode control codes which force
|
||||
// the proper direction of the text regardless of the direction of
|
||||
// the whole app
|
||||
label.textContent = LanguageList.wrapBidi(lang, allLanguages[lang]);
|
||||
|
||||
var radio = document.createElement('gaia-radio');
|
||||
radio.className = 'checkone';
|
||||
radio.setAttribute('name', 'language.current');
|
||||
radio.setAttribute('value', lang);
|
||||
radio.checked = (lang === currentLanguage);
|
||||
radio.appendChild(label);
|
||||
|
||||
// Needed to use a custom class with dynamic insertion.
|
||||
radio.configureClass();
|
||||
|
||||
var li = document.createElement('li');
|
||||
li.dataset.value = lang;
|
||||
li.appendChild(radio);
|
||||
container.appendChild(li);
|
||||
}
|
||||
panel.dataset.languagesReady = true;
|
||||
window.dispatchEvent(new CustomEvent('languagelistready'));
|
||||
});
|
||||
}
|
||||
};
|
||||
644
gaia/apps/ftu/js/late_customization.js
Executable file
|
|
@ -0,0 +1,644 @@
|
|||
/* global URLSearchParams, Navigation, UIManager,
|
||||
IconsHelper, MobileOperator, Sanitizer, LazyLoader */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const MANIFEST_SETTING_NAME = 'latecustomization.url';
|
||||
const OPERATOR_INFO_SETTING_NAME = 'latecustomization.operatorInfo';
|
||||
const COMMS_CHANNEL = 'ftucomms';
|
||||
const ICON_SIZE = 32;
|
||||
const IMG_PATH = 'style/images/late_customization';
|
||||
const ICON_INSTALLING_URL = `${IMG_PATH}/app_installing.svg`;
|
||||
const ICON_FAILED_URL = `${IMG_PATH}/app_install_unrecoverable.svg`;
|
||||
const ICON_DEFAULT_URL = `${IMG_PATH}/default_icon.svg`;
|
||||
|
||||
function LateCustomizationPanel() {
|
||||
this.debug('LateCustomizationPanel ctor');
|
||||
}
|
||||
|
||||
LateCustomizationPanel.prototype = {
|
||||
DEBUG: false,
|
||||
name: 'LateCustomizationPanel',
|
||||
constructor: LateCustomizationPanel,
|
||||
_initialized: false,
|
||||
_downloadPossible: false,
|
||||
|
||||
/* The manifest URL for the customization (comes from mozSettings) */
|
||||
// e.g. https://marketplace-dev.allizom.org/\
|
||||
// api/v2/late-customization/?carrier=telenor®ion=ca
|
||||
_customizationManifestUrl: '',
|
||||
|
||||
get enabled() {
|
||||
return Boolean(this._customizationManifestUrl &&
|
||||
(this._operatorInfo || this._predefinedOperatorInfo));
|
||||
},
|
||||
|
||||
get operatorInfo() {
|
||||
var info = {};
|
||||
Object.assign(info,
|
||||
this._operatorInfo || this.getInfoFromMobileConnection(),
|
||||
this._predefinedOperatorInfo);
|
||||
return info;
|
||||
},
|
||||
|
||||
get carrierName() {
|
||||
var info = this.operatorInfo;
|
||||
return info.carrier || info.operator;
|
||||
},
|
||||
|
||||
get region() {
|
||||
return this.operatorInfo.region || '';
|
||||
},
|
||||
|
||||
start: function() {
|
||||
window.addEventListener('hashchange', this);
|
||||
window.addEventListener('ftu-done', this);
|
||||
window.addEventListener('online', this);
|
||||
window.addEventListener('offline', this);
|
||||
|
||||
this._operatorInfo = this.getInfoFromMobileConnection();
|
||||
this.waitForSettingValues().then((results) => {
|
||||
var [url, predefinedInfo] = results;
|
||||
if (url) {
|
||||
this._customizationManifestUrl = url;
|
||||
}
|
||||
if (predefinedInfo) {
|
||||
// store operator/params separate from
|
||||
// bona-fide mobileconnection details
|
||||
this._predefinedOperatorInfo = predefinedInfo;
|
||||
}
|
||||
this.debug(
|
||||
'start, resolved manifest url: ' + this._customizationManifestUrl,
|
||||
' is enabled? ' + this.enabled,
|
||||
' operatorInfo: ', this._operatorInfo);
|
||||
|
||||
if (this.enabled) {
|
||||
// bail completely if not enabled: don't register step, don't render
|
||||
this.init();
|
||||
} else {
|
||||
this.debug('Not enabled; not initializing');
|
||||
if (!this._operatorInfo) {
|
||||
// keep watching in case we can init later
|
||||
this.waitForOperatorInfo().then((info) => {
|
||||
if (!this._initialized && this.enabled) {
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).catch(e => {
|
||||
this.debug('start: didnt init, error from waitForSettingValues: ', e);
|
||||
});
|
||||
},
|
||||
|
||||
init: function() {
|
||||
// conditions for displaying Late Customization pane
|
||||
// are embodied in this.enabled. If not enabled, init is skipped
|
||||
// and the step is not registered at all.
|
||||
if (this._initialized || !this.enabled) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
this.appManifestUrls = [];
|
||||
this.render();
|
||||
|
||||
Navigation.registerStep({
|
||||
hash: '#late_customization',
|
||||
afterStep: 'firefox_accounts'
|
||||
});
|
||||
|
||||
this.attemptToPopulateAppList();
|
||||
},
|
||||
|
||||
postConnectMessage: function(message) {
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
if (app.connect) {
|
||||
app.connect(COMMS_CHANNEL).then(function onConnAccepted(ports) {
|
||||
ports.forEach(function(port) {
|
||||
port.postMessage(message);
|
||||
});
|
||||
}, function onConnRejected(reason) {
|
||||
console.error('Cannot connect to comms channel: ', reason);
|
||||
});
|
||||
} else {
|
||||
console.error ('mozApps does not have a connect method. ' +
|
||||
'Cannot connect to the comms channel.');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
attemptToPopulateAppList: function() {
|
||||
this.debug('attemptToPopulateAppList, waiting to go online');
|
||||
// start waiting to go online.
|
||||
// We bail if ftu completes first
|
||||
var connected = this.whenUsableConnection();
|
||||
|
||||
connected.then(() => {
|
||||
this.debug('attemptToPopulateAppList, came online');
|
||||
return this.fetchAppsToInstall().then((appsMap) => {
|
||||
if (!(this._appsToInstall && this._appsToInstall.size)) {
|
||||
// no apps, bail early
|
||||
this.debug('No apps in response from ' +
|
||||
this._customizationManifestUrl + ', bailing');
|
||||
return;
|
||||
}
|
||||
var urls = Array.from(this._appsToInstall.keys());
|
||||
var postMessageDetail = {
|
||||
type: 'late-customization-apps',
|
||||
urls: urls
|
||||
};
|
||||
this.postConnectMessage(postMessageDetail);
|
||||
this.updateAppList(this._appsToInstall);
|
||||
}).catch(err => {
|
||||
console.warn('LateCustomizationPanel, error fetching customization ' +
|
||||
'manifest from: ' + this._customizationManifestUrl,
|
||||
err.message);
|
||||
});
|
||||
}).catch((e) => {
|
||||
this.debug('No connection available in time to ' +
|
||||
'fetch late customization manifest:', e);
|
||||
});
|
||||
return connected;
|
||||
},
|
||||
fetchAppsToInstall: function() {
|
||||
if (this._appsToInstall) {
|
||||
return Promise.resolve(this._appsToInstall);
|
||||
}
|
||||
return this.fetchManifest().then((manifestResp) => {
|
||||
var appsMap = this.getAppsFromManifest(manifestResp);
|
||||
if (!appsMap) {
|
||||
return Promise.reject('getAppsFromManifest didnt return appsMap');
|
||||
}
|
||||
return (this._appsToInstall = appsMap);
|
||||
}, (err) => {
|
||||
console.warn('LateCustomization: ', err.message || err);
|
||||
});
|
||||
},
|
||||
|
||||
waitForSettingValues: function() {
|
||||
var haveUrl, haveInfo;
|
||||
var lock = navigator.mozSettings.createLock();
|
||||
|
||||
function getSetting(name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
lock = (lock && !lock.closed) ?
|
||||
lock : navigator.mozSettings.createLock();
|
||||
var get = lock.get(name);
|
||||
get.addEventListener('success', (function() {
|
||||
resolve(get.result[name]);
|
||||
}));
|
||||
get.addEventListener('error', (function() {
|
||||
reject();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (this._customizationManifestUrl) {
|
||||
haveUrl = Promise.resolve(this._customizationManifestUrl);
|
||||
} else {
|
||||
haveUrl = getSetting(MANIFEST_SETTING_NAME);
|
||||
}
|
||||
haveInfo = getSetting(OPERATOR_INFO_SETTING_NAME);
|
||||
|
||||
return Promise.all([haveUrl, haveInfo]);
|
||||
},
|
||||
|
||||
waitForOperatorInfo: function() {
|
||||
var info = this.getInfoFromMobileConnection();
|
||||
if (info) {
|
||||
this._operatorInfo = info;
|
||||
return Promise.resolve(info);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._operatorInfoRequest = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
getInfoFromMobileConnection: function() {
|
||||
var connections = navigator.mozMobileConnections;
|
||||
if (connections) {
|
||||
for (var i=0, conn; i<connections.length; i++) {
|
||||
conn = connections[i];
|
||||
if(conn && conn.voice && conn.voice.connected) {
|
||||
var operatorInfos = MobileOperator.userFacingInfo(conn);
|
||||
if (operatorInfos.operator) {
|
||||
operatorInfos.mcc = conn.voice.network.mcc;
|
||||
operatorInfos.mnc = conn.voice.network.mnc;
|
||||
return operatorInfos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getAppsFromManifest: function(data) {
|
||||
var apps = new Map();
|
||||
var langs = navigator.languages;
|
||||
var list = data && (data.apps || data.objects);
|
||||
if (!(list && list.length)) {
|
||||
return apps;
|
||||
}
|
||||
list.filter((app) => {
|
||||
return app.is_disabled !== true &&
|
||||
(new Set(app.device_types)).has('firefoxos');
|
||||
}).forEach((appData) => {
|
||||
appData.manifestURL = appData.manifest_url;
|
||||
// get locale-appropriate name for user display
|
||||
var appName = '';
|
||||
langs.some(function tryLanguage(lang) {
|
||||
if (this[lang]) {
|
||||
appName = this[lang];
|
||||
// aborts [].some
|
||||
return true;
|
||||
}
|
||||
}, appData.name);
|
||||
|
||||
appData.displayName = appName;
|
||||
this.debug('app data: ', appData.displayName, appData);
|
||||
apps.set(appData.manifestURL, appData);
|
||||
});
|
||||
return apps;
|
||||
},
|
||||
|
||||
attemptInstalls: function() {
|
||||
this._installQueue = [];
|
||||
if (this._appsToInstall && this._appsToInstall.size) {
|
||||
var attempts = [];
|
||||
this._appsToInstall.forEach((appData, manifestURL) => {
|
||||
if (appData.installState == 'installed' ||
|
||||
appData.installState == 'pending') {
|
||||
this.debug('attemptInstalls: app install already underway: ',
|
||||
manifestURL, appData.installState);
|
||||
} else {
|
||||
attempts.push(this.enqueAppInstall(appData));
|
||||
}
|
||||
});
|
||||
return Promise.all(attempts);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
installApp: function(appData) {
|
||||
this.debug('installApp: ', appData.manifestURL);
|
||||
return this.whenUsableConnection().then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
var req = window.navigator.mozApps.install(appData.manifestURL);
|
||||
var onOutcome = (arg) => {
|
||||
var selector = `li[data-manifest-url="${appData.manifestURL}"]`;
|
||||
var itemNode = this.appListNode.querySelector(selector);
|
||||
var manifestURL = req.result && req.result.manifestURL;
|
||||
this.debug('appinstall DOMRequest outcome:', arg, req.result);
|
||||
|
||||
if (req.error) {
|
||||
delete appData.installState;
|
||||
itemNode && itemNode.classList.add('failed');
|
||||
console.warn('Install error: ', req.error);
|
||||
} else if (req.result) {
|
||||
appData.installState = req.result.installState;
|
||||
this.debug('Install success, app: ',
|
||||
manifestURL, appData.installState);
|
||||
}
|
||||
itemNode && itemNode.classList.remove('pending');
|
||||
resolve(req);
|
||||
};
|
||||
req.onsuccess = req.onerror = onOutcome;
|
||||
});
|
||||
}).catch(() => {
|
||||
this.debug('Lost connection while attempting install of ' +
|
||||
appData.manifestURL);
|
||||
});
|
||||
},
|
||||
|
||||
enqueAppInstall: function(app) {
|
||||
var installQueue = this._installQueue;
|
||||
installQueue.push(app);
|
||||
this.debug('enqueAppInstall, queue: ', app.manifestURL);
|
||||
|
||||
var currentInstallAttempt = this._installAttempt || Promise.resolve();
|
||||
return currentInstallAttempt.then(req => {
|
||||
if (req && req.error) {
|
||||
console.warn('install error:', req.error);
|
||||
}
|
||||
if (this._installQueue.length) {
|
||||
this._installAttempt =
|
||||
this.installApp(this._installQueue.shift());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
debug: function() {
|
||||
if (!this.DEBUG) {
|
||||
return;
|
||||
}
|
||||
var args = Array.from(arguments);
|
||||
args.unshift('LateCustomizationPanel');
|
||||
console.log.apply(console, args);
|
||||
},
|
||||
|
||||
buildManifestUrl: function(url, infos={}) {
|
||||
var apiURL = new URL(url);
|
||||
var params = new URLSearchParams(apiURL.search.substr(1));
|
||||
for(var key in infos) {
|
||||
if (typeof infos[key] !== 'undefined') {
|
||||
params.set(key, infos[key]);
|
||||
}
|
||||
}
|
||||
apiURL.search = params.toString();
|
||||
return apiURL.href;
|
||||
},
|
||||
|
||||
fetchManifest: function() {
|
||||
var url = this.buildManifestUrl(this._customizationManifestUrl,
|
||||
this.operatorInfo);
|
||||
if (!url) {
|
||||
return Promise.reject(new Error('No manifest URL available'));
|
||||
}
|
||||
this.debug('fetchManifest at: ' + url);
|
||||
return new Promise((resolve, reject) => {
|
||||
LazyLoader.getJSON(url, true).then((resp) => {
|
||||
if (resp.objects) {
|
||||
resolve(resp);
|
||||
} else {
|
||||
reject('No apps in reponse to: ' + url);
|
||||
}
|
||||
}).catch(ex => {
|
||||
reject(ex);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
whenUsableConnection: function() {
|
||||
if (this._pendingNextOnline) {
|
||||
return this._pendingNextOnline;
|
||||
}
|
||||
var canDownload = navigator.onLine;
|
||||
if (canDownload) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._nextOnlineRequest = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
switch (evt.type) {
|
||||
case 'hashchange':
|
||||
if (this.enabled &&
|
||||
evt.target.location.hash === '#late_customization') {
|
||||
if (this._appsToInstall && this._appsToInstall.size) {
|
||||
this.onPanelShown();
|
||||
} else {
|
||||
Navigation.skipStep();
|
||||
}
|
||||
}
|
||||
// TODO: is some event I can listen to to know the sim is ready
|
||||
// to be asked these questions?
|
||||
if (this._operatorInfoRequest) {
|
||||
var info = this.getInfoFromMobileConnection();
|
||||
if (info) {
|
||||
this._operatorInfo = info;
|
||||
this._operatorInfoRequest.resolve(info);
|
||||
this._operatorInfoRequest = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'online':
|
||||
case 'offline':
|
||||
// install is underway, try to resume
|
||||
if (navigator.onLine &&
|
||||
this._installQueue && this._installQueue.length) {
|
||||
return this.attemptInstalls();
|
||||
}
|
||||
if (navigator.onLine && this._nextOnlineRequest) {
|
||||
this._nextOnlineRequest.resolve(true);
|
||||
} else if (this._nextOnlineRequest) {
|
||||
this._nextOnlineRequest.reject(new Error('offline'));
|
||||
// re-start the wait/fetch/install process
|
||||
if (!this._appsToInstall) {
|
||||
this.attemptToPopulateAppList();
|
||||
}
|
||||
}
|
||||
this._nextOnlineRequest = null;
|
||||
break;
|
||||
case 'ftu-done':
|
||||
// abort anything pending
|
||||
if (this._operatorInfoRequest) {
|
||||
this._operatorInfoRequest.reject(
|
||||
new Error('FTU complete with no viable operator'));
|
||||
this._operatorInfoRequest = null;
|
||||
}
|
||||
if (this._nextOnlineRequest) {
|
||||
this._nextOnlineRequest.reject(
|
||||
new Error('done before connection became available'));
|
||||
this._nextOnlineRequest = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onPanelShown: function() {
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id',
|
||||
'late-customization');
|
||||
this.finalizeRender();
|
||||
this.attemptInstalls();
|
||||
},
|
||||
|
||||
_createElement: function(name, options) {
|
||||
var elm = document.createElement(name);
|
||||
if (options.id) {
|
||||
elm.id = options.id;
|
||||
}
|
||||
if (options.className) {
|
||||
elm.className = options.className;
|
||||
}
|
||||
if (options.dataset) {
|
||||
for(var p in options.dataset) {
|
||||
elm.dataset[p] = options.dataset[p];
|
||||
}
|
||||
}
|
||||
if (options.attributes) {
|
||||
for(var a in options.attributes) {
|
||||
elm.setAttribute(a, options.attributes[a]);
|
||||
}
|
||||
}
|
||||
if ('textContent' in options) {
|
||||
elm.textContent = options.textContent;
|
||||
}
|
||||
return elm;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var section = this.element =
|
||||
document.getElementById('late_customization');
|
||||
if (!section) {
|
||||
section = this._createElement('section', {
|
||||
attributes: { role: 'region' },
|
||||
id: 'late_customization'
|
||||
});
|
||||
this.element = section;
|
||||
}
|
||||
|
||||
section.innerHTML = Sanitizer.escapeHTML `
|
||||
<section id="late-customization-cando" class="content hidden">
|
||||
<p id="late_customization-downloading-message"></p>
|
||||
</section>
|
||||
<section id="late-customization-nocando" class="content">
|
||||
<p id="late_customization-download-later-message"></p>
|
||||
<p id="late_customization-download-later-marketplace"></p>
|
||||
</section>
|
||||
<article data-type="list">
|
||||
<ul id="late_customization-applist" class="hidden"></ul>
|
||||
</article>`;
|
||||
|
||||
function setTranslationById(domId, l10Id, attrs) {
|
||||
var elem = section.querySelector('#' + domId);
|
||||
document.l10n.setAttributes(elem, l10Id, attrs);
|
||||
}
|
||||
|
||||
var carrierName = this.carrierName;
|
||||
if (carrierName) {
|
||||
setTranslationById(
|
||||
'late_customization-downloading-message',
|
||||
'late-customization-downloading-message',
|
||||
{ carrierName: this.carrierName });
|
||||
setTranslationById(
|
||||
'late_customization-download-later-message',
|
||||
'late-customization-download-later',
|
||||
{ carrierName: this.carrierName });
|
||||
} else {
|
||||
// Use an alternate string if we have no carrier name to display
|
||||
setTranslationById(
|
||||
'late_customization-downloading-message',
|
||||
'late-customization-downloading-message-default');
|
||||
setTranslationById(
|
||||
'late_customization-download-later-message',
|
||||
'late-customization-download-later-default');
|
||||
}
|
||||
setTranslationById(
|
||||
'late_customization-download-later-marketplace',
|
||||
'late-customization-download-later-marketplace',
|
||||
{ carrierName: this.carrierName });
|
||||
|
||||
if (!section.parentNode) {
|
||||
document.querySelector('#activation-screen > article')
|
||||
.appendChild(section);
|
||||
}
|
||||
this.appListNode = document.querySelector('#late_customization-applist');
|
||||
},
|
||||
|
||||
renderAppItem: function(appData) {
|
||||
var itemNode = this._createElement('li', {
|
||||
dataset: { manifestUrl: appData.manifestURL },
|
||||
className: 'pending'
|
||||
});
|
||||
var listNode = this.element.querySelector('#late_customization-applist');
|
||||
if (!listNode) {
|
||||
this.debug('No element found to render app details into');
|
||||
return;
|
||||
}
|
||||
|
||||
itemNode.innerHTML = Sanitizer.escapeHTML `
|
||||
<span class="icon"
|
||||
style="background-image: url(${ICON_INSTALLING_URL})"></span>
|
||||
<p dir="auto">${appData.displayName}</p>
|
||||
`;
|
||||
listNode.appendChild(itemNode);
|
||||
var iconElem = itemNode.querySelector('.icon');
|
||||
|
||||
this.getAppIconUrl(appData).then((iconUrl) => {
|
||||
this.debug('getAppIconUrl returned:', iconUrl);
|
||||
var img = new Image();
|
||||
img.onload = () => {
|
||||
iconElem.style.backgroundImage = 'url('+img.src+')';
|
||||
};
|
||||
img.onerror = () => {
|
||||
iconElem.style.backgroundImage = 'url('+ICON_DEFAULT_URL+')';
|
||||
};
|
||||
img.src = iconUrl.href;
|
||||
});
|
||||
return itemNode;
|
||||
},
|
||||
|
||||
getAppIconUrl: function(appData, iconSize = ICON_SIZE) {
|
||||
var siteObj = {};
|
||||
var manifest = appData;
|
||||
var manifestURL = appData.manifest_url;
|
||||
var origin = manifest.origin = new URL(manifestURL).origin;
|
||||
var hasIcons = (manifest.icons && Object.keys(manifest.icons).length);
|
||||
if (!hasIcons) {
|
||||
return Promise.resolve(new URL(ICON_DEFAULT_URL));
|
||||
}
|
||||
//Getting the icons from the FirefoxOS-style manifest
|
||||
siteObj.manifestURL = manifestURL;
|
||||
siteObj.manifest = manifest;
|
||||
var gotIcon = new Promise((resolve, reject) => {
|
||||
IconsHelper.getIcon(origin, iconSize, null, siteObj)
|
||||
.then((iconUrl) => {
|
||||
this.debug('getIcon returned: ', iconUrl);
|
||||
resolve(iconUrl);
|
||||
}).catch(err => {
|
||||
this.debug('Failed to load icon from: ' +
|
||||
JSON.stringify(appData.icons));
|
||||
console.warn('getIcon errback, err: ',err);
|
||||
resolve(new URL(ICON_FAILED_URL));
|
||||
});
|
||||
});
|
||||
return gotIcon;
|
||||
},
|
||||
|
||||
finalizeRender: function() {
|
||||
var container = this.element;
|
||||
var appsMap = this._appsToInstall;
|
||||
var hasApps = (appsMap && appsMap.size);
|
||||
|
||||
var canDoElm = container.querySelector('#late-customization-cando');
|
||||
var noCanDoElm = container.querySelector('#late-customization-nocando');
|
||||
var appListElm = container.querySelector('#late_customization-applist');
|
||||
|
||||
canDoElm.classList.toggle('hidden', !hasApps);
|
||||
appListElm.classList.toggle('hidden', !hasApps);
|
||||
noCanDoElm.classList.toggle('hidden', !!hasApps);
|
||||
},
|
||||
|
||||
updateAppList: function(appsMap) {
|
||||
var list = this.element.querySelector('#late_customization-applist');
|
||||
|
||||
this.debug('updateAppList: ', appsMap);
|
||||
|
||||
while (list.firstChild) {
|
||||
list.firstChild.remove();
|
||||
}
|
||||
|
||||
if (appsMap && appsMap.size) {
|
||||
for (var appData of appsMap.values()) {
|
||||
this.debug('calling renderAppItem with app: ', appData.manifestURL);
|
||||
this.renderAppItem(appData);
|
||||
}
|
||||
}
|
||||
this.finalizeRender();
|
||||
}
|
||||
};
|
||||
|
||||
exports.LateCustomizationPanel = LateCustomizationPanel;
|
||||
var panel;
|
||||
|
||||
document.l10n.ready.then(() => {
|
||||
panel = exports.lateCustomizationPanel = new LateCustomizationPanel();
|
||||
});
|
||||
|
||||
window.addEventListener('ftu-setup', function onSetup(evt) {
|
||||
window.removeEventListener('ftu-setup', onSetup);
|
||||
panel && (panel.manager = evt.detail);
|
||||
panel && panel.start();
|
||||
});
|
||||
|
||||
})(window);
|
||||
164
gaia/apps/ftu/js/memcard_manager.js
Executable file
|
|
@ -0,0 +1,164 @@
|
|||
/* global ConfirmDialog,
|
||||
UIManager,
|
||||
utils,
|
||||
VCFReader */
|
||||
|
||||
/* exported SdManager */
|
||||
|
||||
'use strict';
|
||||
|
||||
var SdManager = {
|
||||
available: function() {
|
||||
return utils.sdcard.checkStorageCard();
|
||||
},
|
||||
|
||||
checkSDButton: function sm_checkSDButton() {
|
||||
var sdOption = UIManager.sdImportButton;
|
||||
// If there is an unlocked SD we activate import from SD
|
||||
if (!SdManager.alreadyImported && SdManager.available()) {
|
||||
sdOption.removeAttribute('disabled');
|
||||
UIManager.noMemorycard.classList.add('hidden');
|
||||
} else {
|
||||
sdOption.setAttribute('disabled', 'disabled');
|
||||
if (!SdManager.alreadyImported) {
|
||||
UIManager.noMemorycard.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
importContacts: function sm_importContacts() {
|
||||
var importedContacts = 0;
|
||||
|
||||
UIManager.navBar.setAttribute('aria-disabled', 'true');
|
||||
|
||||
var cancelled = false;
|
||||
var importer = null;
|
||||
var importButton;
|
||||
|
||||
var progress = utils.overlay.show(
|
||||
'memoryCardContacts-importing',
|
||||
'activityBar',
|
||||
'infiniteProgress'
|
||||
);
|
||||
utils.overlay.showMenu();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
utils.overlay.oncancel = () => {
|
||||
cancelled = true;
|
||||
if (importer) {
|
||||
importer.finish();
|
||||
} else {
|
||||
UIManager.navBar.removeAttribute('aria-disabled');
|
||||
utils.overlay.hide();
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
importButton = UIManager.sdImportButton;
|
||||
|
||||
utils.sdcard.retrieveFiles([
|
||||
'text/vcard',
|
||||
'text/directory;profile=vCard',
|
||||
'text/directory'
|
||||
], ['vcf', 'vcard'], (error, fileArray) => {
|
||||
if (!error && !fileArray.length) {
|
||||
error = 'No contacts were found';
|
||||
}
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
return import_error(error);
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var promises = [];
|
||||
fileArray.forEach(file => {
|
||||
promises.push(utils.sdcard.getTextFromFile(file, onContacts));
|
||||
});
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
utils.misc.setTimestamp('sd');
|
||||
UIManager.navBar.removeAttribute('aria-disabled');
|
||||
utils.overlay.hide();
|
||||
if (cancelled) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
SdManager.alreadyImported = true;
|
||||
importButton.setAttribute('disabled', 'disabled');
|
||||
utils.status.show({
|
||||
id: 'memoryCardContacts-imported3',
|
||||
args: { n: importedContacts }
|
||||
});
|
||||
resolve();
|
||||
}).catch(reject);
|
||||
});
|
||||
});
|
||||
|
||||
function onContacts(text) {
|
||||
if (cancelled) {
|
||||
return Promise.reject();
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
importer = new VCFReader(text);
|
||||
|
||||
if (!text || !importer) {
|
||||
var error = 'No contacts were found';
|
||||
import_error(error);
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
importer.onimported = imported_contact;
|
||||
importer.onerror = error => {
|
||||
import_error(error);
|
||||
reject(error);
|
||||
};
|
||||
|
||||
importer.process(() => {
|
||||
if (cancelled) {
|
||||
reject('Cancelled');
|
||||
utils.overlay.hide();
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function imported_contact() {
|
||||
importedContacts++;
|
||||
progress.update();
|
||||
}
|
||||
|
||||
function import_error(e) {
|
||||
UIManager.navBar.removeAttribute('aria-disabled');
|
||||
utils.overlay.hide();
|
||||
// Just in case the user decides to do so later
|
||||
importButton.removeAttribute('disabled');
|
||||
|
||||
// Showing error message allowing user to retry
|
||||
var cancel = {
|
||||
title: 'cancel',
|
||||
callback: function() {
|
||||
ConfirmDialog.hide();
|
||||
}
|
||||
};
|
||||
|
||||
var retry = {
|
||||
title: 'retry',
|
||||
isRecommend: true,
|
||||
callback: function() {
|
||||
ConfirmDialog.hide();
|
||||
// And now the action is reproduced one more time
|
||||
importButton.click();
|
||||
}
|
||||
};
|
||||
ConfirmDialog.show(null, 'memoryCardContacts-error', cancel, retry);
|
||||
}
|
||||
}
|
||||
};
|
||||
532
gaia/apps/ftu/js/navigation.js
Executable file
|
|
@ -0,0 +1,532 @@
|
|||
/* global DataMobile, SimManager, IccHelper,
|
||||
SdManager, UIManager, WifiManager, WifiUI,
|
||||
ImportIntegration,
|
||||
OperatorVariant */
|
||||
/* exported Navigation */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const DOGFOODSETTING = 'debug.performance_data.dogfooding';
|
||||
const METRICSSETTING = 'metrics.selectedMetrics.level';
|
||||
|
||||
/*
|
||||
Steps of the First Time Usage App
|
||||
*/
|
||||
var steps = [
|
||||
{
|
||||
onlyForward: true,
|
||||
hash: '#languages',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#data_3g',
|
||||
requireSIM: true
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#wifi',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#date_and_time',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#geolocation',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#import_contacts',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#firefox_accounts',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#welcome_browser',
|
||||
requireSIM: false
|
||||
},
|
||||
{
|
||||
onlyForward: false,
|
||||
hash: '#browser_privacy',
|
||||
requireSIM: false
|
||||
}
|
||||
];
|
||||
|
||||
exports.Navigation = {
|
||||
get stepCount() {
|
||||
return this._stepOrder ? this._stepOrder.length : 0;
|
||||
},
|
||||
currentStepIndex: 0,
|
||||
previousStepIndex: 0,
|
||||
simMandatory: false,
|
||||
skipMobileDataScreen: false,
|
||||
skipDateTimeScreen: false,
|
||||
tzInitialized: false,
|
||||
|
||||
_stepsById: null,
|
||||
_stepOrder: null,
|
||||
|
||||
init: function n_init() {
|
||||
this._stepOrder = [];
|
||||
this._stepsById = {};
|
||||
|
||||
var settings = navigator.mozSettings;
|
||||
var forward = document.getElementById('forward');
|
||||
var back = document.getElementById('back');
|
||||
this._onForwardClick = this.forward.bind(this);
|
||||
this._onBackClick = this.back.bind(this);
|
||||
forward.addEventListener('click', this._onForwardClick);
|
||||
back.addEventListener('click', this._onBackClick);
|
||||
window.addEventListener('hashchange', this);
|
||||
this._onActivationScreenClick = this.handleExternalLinksClick.bind(this);
|
||||
UIManager.activationScreen.addEventListener('click',
|
||||
this._onActivationScreenClick);
|
||||
|
||||
// register pre-defined steps in the oder they appear
|
||||
steps.forEach(step => {
|
||||
this.registerStep(step);
|
||||
});
|
||||
|
||||
var self = this;
|
||||
|
||||
var reqSIM =
|
||||
settings && settings.createLock().get('ftu.sim.mandatory') || {};
|
||||
reqSIM.onsuccess = function onSuccess() {
|
||||
self.simMandatory = reqSIM.result['ftu.sim.mandatory'] || false;
|
||||
};
|
||||
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
app.connect('ftucomms').then(function onConnAccepted(ports) {
|
||||
self.ports = ports;
|
||||
ports.forEach(port => port.postMessage('started'));
|
||||
}, function onConnRejected(reason) {
|
||||
console.warn('FTU navigation cannot use IAC: ' + reason);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
this._stepOrder = null;
|
||||
this._stepsById = null;
|
||||
this.currentStepIndex = 0;
|
||||
this.previousStepIndex = 0;
|
||||
this.ports = null;
|
||||
|
||||
var forward = document.getElementById('forward');
|
||||
var back = document.getElementById('back');
|
||||
var activationScreen = UIManager && UIManager.activationScreen;
|
||||
|
||||
if (forward && this._onForwardClick) {
|
||||
forward.removeEventListener('click', this._onForwardClick);
|
||||
}
|
||||
if (back && this._onBackClick) {
|
||||
back.removeEventListener('click', this._onBackClick);
|
||||
}
|
||||
if (activationScreen && this._onActivationScreenClick) {
|
||||
activationScreen.removeEventListener('click',
|
||||
this._onActivationScreenClick);
|
||||
}
|
||||
window.removeEventListener('hashchange', this);
|
||||
},
|
||||
|
||||
registerStep: function(step) {
|
||||
if (!step.id) {
|
||||
step.id = step.hash.substring(1); // strip off '#' to make an id;
|
||||
}
|
||||
var id = step.id;
|
||||
this._stepsById[id] = step;
|
||||
var beforeStepIdx = step.hasOwnProperty('beforeStep') ?
|
||||
this._stepOrder.indexOf(step.beforeStep) : -1;
|
||||
var afterStepIdx = step.hasOwnProperty('afterStep') ?
|
||||
this._stepOrder.indexOf(step.afterStep) : -1;
|
||||
|
||||
if (beforeStepIdx > -1) {
|
||||
this._stepOrder.splice(beforeStepIdx, 0, id);
|
||||
} else if (afterStepIdx > -1) {
|
||||
this._stepOrder.splice(afterStepIdx+1, 0, id);
|
||||
} else {
|
||||
this._stepOrder.push(id);
|
||||
}
|
||||
},
|
||||
|
||||
stepAt: function(idx) {
|
||||
var stepId = this._stepOrder[idx];
|
||||
if (stepId) {
|
||||
return this._stepsById[stepId];
|
||||
}
|
||||
},
|
||||
indexOfStep: function(step) {
|
||||
var id = (typeof id === 'string') ? step.id : step;
|
||||
return this._stepOrder.indexOf(id);
|
||||
},
|
||||
|
||||
back: function n_back(event) {
|
||||
var currentStep = this.stepAt(this.currentStepIndex);
|
||||
var actualHash = window.location.hash;
|
||||
if (actualHash != currentStep.hash) {
|
||||
window.history.back();
|
||||
} else {
|
||||
var self = this;
|
||||
var goToStep = function() {
|
||||
self.previousStepIndex = self.currentStepIndex;
|
||||
self.currentStepIndex--;
|
||||
if (self.currentStepIndex >= 0) {
|
||||
self.manageStep();
|
||||
}
|
||||
};
|
||||
goToStep();
|
||||
}
|
||||
},
|
||||
|
||||
forward: function n_forward(event) {
|
||||
var self = this;
|
||||
var goToStepForward = function() {
|
||||
self.previousStepIndex = self.currentStepIndex;
|
||||
self.currentStepIndex++;
|
||||
if (self.currentStepIndex >= self.stepCount) {
|
||||
// Try to send Newsletter here
|
||||
UIManager.sendNewsletter(function newsletterSent(result) {
|
||||
if (result) { // sending process ok, we advance
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.changeStatusBarColor(UIManager.DARK_THEME);
|
||||
UIManager.finishScreen.classList.add('show');
|
||||
UIManager.hideActivationScreenFromScreenReader();
|
||||
} else { // error on sending, we stay where we are
|
||||
self.currentStepIndex--;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
self.manageStep();
|
||||
};
|
||||
goToStepForward();
|
||||
},
|
||||
|
||||
handleExternalLinksClick: function n_handleExternalLinksClick(e) {
|
||||
var link = e.target;
|
||||
if (! link.mozMatchesSelector('a.external')) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
var href = link.href,
|
||||
title = link.getAttribute('title') || link.textContent;
|
||||
|
||||
if (navigator.onLine) {
|
||||
this.displayExternalLink(href, title);
|
||||
} else {
|
||||
UIManager.displayOfflineDialog(href, title);
|
||||
}
|
||||
},
|
||||
|
||||
displayExternalLink: function n_displayExternalLink(href, title) {
|
||||
window.open(href, '', 'dialog');
|
||||
},
|
||||
|
||||
ensureTZInitialized: function () {
|
||||
if (!this.tzInitialized) {
|
||||
return UIManager.initTZ().then(() => {
|
||||
this.skipDateTimeScreen = !UIManager.timeZoneNeedsConfirmation;
|
||||
this.tzInitialized = true;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function n_handleEvent(event) {
|
||||
var actualHash = window.location.hash;
|
||||
switch (actualHash) {
|
||||
case '#languages':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'language');
|
||||
break;
|
||||
case '#data_3g':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', '3g');
|
||||
DataMobile.
|
||||
getStatus(UIManager.updateDataConnectionStatus.bind(UIManager));
|
||||
break;
|
||||
case '#wifi':
|
||||
DataMobile.removeSVStatusObserver();
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'selectNetwork');
|
||||
UIManager.activationScreen.classList.remove('no-options');
|
||||
if (UIManager.navBar.classList.contains('secondary-menu')) {
|
||||
UIManager.navBar.classList.remove('secondary-menu');
|
||||
return;
|
||||
}
|
||||
|
||||
// This might seem like an odd place to call UIManager.initTZ, but
|
||||
// there's a reason for it. initTZ tries to determine the timezone
|
||||
// using information about the current mobile network connection.
|
||||
// We want to call initTZ as late as possible to give the mobile
|
||||
// network time to connect before the function is called.
|
||||
// But we have to call initTZ *before* the Date & Time page
|
||||
// appears so that it doesn't delay the appearance of the page.
|
||||
// This is the last good opportunity to call it.
|
||||
|
||||
WifiManager.getNetworks(networks => {
|
||||
this.ensureTZInitialized().then(() => {
|
||||
WifiUI.renderNetworks(networks);
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
case '#date_and_time':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'dateAndTime');
|
||||
break;
|
||||
case '#geolocation':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'geolocation');
|
||||
break;
|
||||
case '#import_contacts':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'importContacts3');
|
||||
// Enabling or disabling SIM import depending on card status
|
||||
SimManager.checkSIMButton();
|
||||
|
||||
// Enabling or disabling SD import depending on card status
|
||||
SdManager.checkSDButton();
|
||||
|
||||
// If we have 3G or Wifi activate online services import
|
||||
var onlineState;
|
||||
if (!WifiManager.api) {
|
||||
// Desktop
|
||||
ImportIntegration.checkImport('enabled');
|
||||
break;
|
||||
}
|
||||
|
||||
onlineState = window.navigator.onLine ? 'enabled' : 'disabled';
|
||||
ImportIntegration.checkImport(onlineState);
|
||||
break;
|
||||
case '#firefox_accounts':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'firefox-accounts');
|
||||
break;
|
||||
case '#welcome_browser':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
|
||||
|
||||
// Initialize the radio button to preselect the basic metrics.
|
||||
var settings = navigator.mozSettings;
|
||||
var basicMetrics = document.getElementById('metrics-basic');
|
||||
var enhancedMetrics = document.getElementById('metrics-enhanced');
|
||||
var noneMetrics = document.getElementById('metrics-none');
|
||||
|
||||
var defaultLevel = settings &&
|
||||
settings.createLock().get(METRICSSETTING);
|
||||
if (defaultLevel) {
|
||||
defaultLevel.onsuccess = function() {
|
||||
var result = defaultLevel.result[METRICSSETTING];
|
||||
if (result === 'Basic') {
|
||||
basicMetrics.checked = true;
|
||||
} else if (result === 'Enhanced') {
|
||||
enhancedMetrics.checked = true;
|
||||
} else if (result === 'None') {
|
||||
noneMetrics.checked = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If it's a dogfooder, we don't want them to disable the metrics.
|
||||
var dogfood = settings && settings.createLock().get(DOGFOODSETTING);
|
||||
if (dogfood) {
|
||||
dogfood.onsuccess = function() {
|
||||
if (dogfood.result[DOGFOODSETTING]) {
|
||||
basicMetrics.setAttribute('disabled', 'true');
|
||||
enhancedMetrics.setAttribute('disabled', 'true');
|
||||
noneMetrics.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
basicMetrics.removeAttribute('disabled');
|
||||
enhancedMetrics.removeAttribute('disabled');
|
||||
noneMetrics.removeAttribute('disabled');
|
||||
}
|
||||
};
|
||||
}
|
||||
break;
|
||||
case '#browser_privacy':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
|
||||
break;
|
||||
case '#SIM_mandatory':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'SIM_mandatory');
|
||||
break;
|
||||
case '#about-your-rights':
|
||||
case '#about-your-privacy':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
|
||||
UIManager.navBar.classList.add('back-only');
|
||||
break;
|
||||
case '#sharing-performance-data':
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
|
||||
UIManager.navBar.classList.add('back-only');
|
||||
break;
|
||||
}
|
||||
|
||||
// If SIM card is mandatory, we hide the button skip
|
||||
if (this.simMandatory) {
|
||||
UIManager.skipPinButton.classList.add('hidden');
|
||||
UIManager.backSimButton.classList.remove('hidden');
|
||||
} else {
|
||||
UIManager.skipPinButton.classList.remove('hidden');
|
||||
UIManager.backSimButton.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Managing options button
|
||||
var lastIndex = this.stepCount - 1;
|
||||
if (this.currentStepIndex <= lastIndex &&
|
||||
(this.stepAt(this.currentStepIndex).hash !== '#wifi')) {
|
||||
UIManager.activationScreen.classList.add('no-options');
|
||||
}
|
||||
|
||||
// Managing nav buttons when coming back from out-of-steps (privacy)
|
||||
if (this.currentStepIndex <= lastIndex &&
|
||||
this.stepAt(this.currentStepIndex).hash === actualHash) {
|
||||
UIManager.navBar.classList.remove('back-only');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Posts IAC message about FTU steps passed.
|
||||
*/
|
||||
postStepMessage: function n_postStepMessage(stepIndex) {
|
||||
if (!this.ports) {
|
||||
return;
|
||||
}
|
||||
var step = this.stepAt(stepIndex);
|
||||
this.ports.forEach(function(port) {
|
||||
port.postMessage({
|
||||
type: 'step',
|
||||
hash: step.hash
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
skipStep: function n_skipStep() {
|
||||
var stepIndex = this.currentStepIndex;
|
||||
stepIndex += stepIndex > this.previousStepIndex ? 1 : -1;
|
||||
if (stepIndex <= 0) {
|
||||
this.previousStepIndex = stepIndex = 0;
|
||||
}
|
||||
if (stepIndex > this.stepCount) {
|
||||
this.previousStepIndex = stepIndex = this.stepCount;
|
||||
}
|
||||
this.currentStepIndex = stepIndex;
|
||||
this.manageStep();
|
||||
},
|
||||
|
||||
manageStep: function n_manageStep() {
|
||||
var self = this;
|
||||
|
||||
// If we moved forward in FTU, post iac message about progress.
|
||||
if (self.currentStepIndex > self.previousStepIndex) {
|
||||
self.postStepMessage(self.previousStepIndex);
|
||||
}
|
||||
|
||||
//SV - We need remember if phone startup with SIM
|
||||
if (self.currentStepIndex >= self.stepCount) {
|
||||
OperatorVariant.setSIMOnFirstBootState();
|
||||
}
|
||||
|
||||
// Reset skip screen flags at beginning of navigation
|
||||
if (self.currentStepIndex === 0) {
|
||||
self.skipMobileDataScreen = false;
|
||||
}
|
||||
|
||||
// Retrieve future location
|
||||
var futureLocation = this.stepAt(self.currentStepIndex);
|
||||
|
||||
// If SIMcard is mandatory and no SIM, go to message window
|
||||
if (this.simMandatory &&
|
||||
!IccHelper.cardState &&
|
||||
futureLocation.requireSIM) {
|
||||
//Send to SIM Mandatory message
|
||||
futureLocation.hash = '#SIM_mandatory';
|
||||
futureLocation.requireSIM = false;
|
||||
futureLocation.onlyBackward = true;
|
||||
}
|
||||
|
||||
// Navigation bar management
|
||||
if (this.stepAt(this.currentStepIndex).onlyForward) {
|
||||
UIManager.navBar.classList.add('forward-only');
|
||||
} else {
|
||||
UIManager.navBar.classList.remove('forward-only');
|
||||
}
|
||||
var nextButton = document.getElementById('forward');
|
||||
if (this.stepAt(this.currentStepIndex).onlyBackward) {
|
||||
nextButton.setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
nextButton.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
// Substitute button content on last step
|
||||
if (this.currentStepIndex === this.stepCount) {
|
||||
nextButton.setAttribute('data-l10n-id', 'done');
|
||||
} else {
|
||||
nextButton.setAttribute('data-l10n-id', 'navbar-next');
|
||||
}
|
||||
|
||||
if (futureLocation.hash === '#firefox_accounts') {
|
||||
nextButton.setAttribute('data-l10n-id', 'skip');
|
||||
}
|
||||
|
||||
// Change hash to the right location
|
||||
window.location.hash = futureLocation.hash;
|
||||
|
||||
// SIM card management
|
||||
if (futureLocation.requireSIM) {
|
||||
var check_cardState = function(response) {
|
||||
if (!response || (!SimManager.available() &&
|
||||
// Don't skip it if next step is data 3g
|
||||
futureLocation.hash !== '#data_3g')) {
|
||||
self.skipStep();
|
||||
if (self.currentStepIndex > self.previousStepIndex) {
|
||||
self.skipMobileDataScreen = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// if we are navigating backwards, we do not want to
|
||||
// show the SIM unlock screens for the data_3g step
|
||||
var skipUnlockScreens = this.currentStepIndex < this.previousStepIndex;
|
||||
SimManager.handleCardState(check_cardState, skipUnlockScreens);
|
||||
}
|
||||
|
||||
// Only show the Date & Time screen if we couldn't determine the
|
||||
// timezone from the network. (We assume that if we can
|
||||
// determine the timezone, we can determine the time too.)
|
||||
if (this.stepAt(this.currentStepIndex).hash === '#date_and_time') {
|
||||
if (!self.tzInitialized) {
|
||||
self.skipDateTimeScreen = !UIManager.timeZoneNeedsConfirmation;
|
||||
}
|
||||
|
||||
if (self.skipDateTimeScreen) {
|
||||
self.postStepMessage(self.currentStepIndex);
|
||||
if(navigator.onLine) {
|
||||
//if you are online you can get a more accurate guess for the time
|
||||
//time you just need to trigger it
|
||||
UIManager.updateSetting(
|
||||
'time.timezone.automatic-update.enabled',
|
||||
true
|
||||
);
|
||||
UIManager.updateSetting(
|
||||
'time.clock.automatic-update.enabled',
|
||||
true
|
||||
);
|
||||
}
|
||||
self.skipStep();
|
||||
}
|
||||
}
|
||||
|
||||
// if we are not connected we should not try fxa
|
||||
if ((futureLocation.hash === '#firefox_accounts' &&
|
||||
!navigator.onLine) ||
|
||||
(futureLocation.hash === '#firefox_accounts' &&
|
||||
UIManager.skipFxA)) {
|
||||
self.postStepMessage(self.currentStepIndex);
|
||||
self.skipStep();
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
85
gaia/apps/ftu/js/operatorVariant.js
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
/* global IccHelper */
|
||||
/* exported OperatorVariant */
|
||||
'use strict';
|
||||
|
||||
var OperatorVariant = (function() {
|
||||
|
||||
var NO_SIM = '000-000';
|
||||
|
||||
/**
|
||||
* If ftu.simPresentOnFirstBoot setting has value do nothing otherwise
|
||||
* set ftu.simPresentOnFirstBoot = true if
|
||||
* IccHelper.cardState value is:
|
||||
* 'ready'.
|
||||
* otherwise set to false.
|
||||
* 'pinRequired',
|
||||
* 'pukRequired',
|
||||
* 'networkLocked',
|
||||
* 'corporateLocked',
|
||||
* 'serviceProviderLocked',
|
||||
* 'network1Locked',
|
||||
* 'network2Locked',
|
||||
* 'hrpdNetworkLocked',
|
||||
* 'ruimCorporateLocked',
|
||||
* 'ruimServiceProviderLocked'
|
||||
*/
|
||||
function setSIMOnFirstBootState() {
|
||||
var cardValue;
|
||||
|
||||
function normalizeCode(aCode) {
|
||||
var ncode = '' + aCode;
|
||||
while (ncode.length < 3) {
|
||||
ncode = '0' + ncode;
|
||||
}
|
||||
return ncode;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!IccHelper || !IccHelper.cardState) {
|
||||
cardValue = NO_SIM;
|
||||
} else {
|
||||
var mcc = IccHelper.iccInfo.mcc;
|
||||
var mnc = IccHelper.iccInfo.mnc;
|
||||
if ((mcc !== undefined) && (mcc !== null) &&
|
||||
(mnc !== undefined) && (mnc !== null)) {
|
||||
cardValue = normalizeCode(mcc) + '-' + normalizeCode(mnc);
|
||||
} else {
|
||||
cardValue = NO_SIM;
|
||||
}
|
||||
}
|
||||
|
||||
var settings = navigator.mozSettings;
|
||||
if (!settings) {
|
||||
console.log('Settings is not available');
|
||||
return;
|
||||
}
|
||||
|
||||
var req = settings.createLock().get('ftu.simPresentOnFirstBoot');
|
||||
|
||||
req.onsuccess = function ov_onsuccess() {
|
||||
var currentStatus = req.result['ftu.simPresentOnFirstBoot'];
|
||||
if (currentStatus === undefined || currentStatus === null) {
|
||||
var result = navigator.mozSettings.createLock().set(
|
||||
{ 'ftu.simPresentOnFirstBoot' : cardValue });
|
||||
|
||||
result.onerror = function ov_error() {
|
||||
console.error('An error occurre setting ' +
|
||||
'ftu.simPresentOnFirstBoot: ' + cardValue);
|
||||
};
|
||||
}
|
||||
cardValue = null;
|
||||
};
|
||||
|
||||
req.onerror = function ov_error() {
|
||||
console.error('Error retrieving ftu.simPresentOnFirstBoot');
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
console.error('Error setSIMOnFirstBootState. ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setSIMOnFirstBootState: setSIMOnFirstBootState
|
||||
};
|
||||
})();
|
||||
95
gaia/apps/ftu/js/services_launcher.js
Executable file
|
|
@ -0,0 +1,95 @@
|
|||
/* global utils */
|
||||
/* exported ServicesLauncher */
|
||||
'use strict';
|
||||
|
||||
var FTU_APP_ORIGIN = document.location.protocol + '//' +
|
||||
document.location.host;
|
||||
|
||||
var ServicesLauncher = (function(document) {
|
||||
var extensionFrame = document.querySelector('#iframe_extensions');
|
||||
var oauthFrame = document.querySelector('#iframe_oauth');
|
||||
|
||||
var BASE_IMPORT = '/shared/pages/import/import.html?ftu=1';
|
||||
function getServiceURI(service) {
|
||||
return BASE_IMPORT + '&service=' + service;
|
||||
}
|
||||
|
||||
var access_token, currentURI, currentService;
|
||||
|
||||
|
||||
function open() {
|
||||
extensionFrame.className = 'opening';
|
||||
}
|
||||
|
||||
function load(targetService) {
|
||||
currentService = targetService;
|
||||
currentURI = getServiceURI(targetService);
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
oauthFrame.contentWindow.postMessage({
|
||||
type: 'start',
|
||||
data: {
|
||||
from: 'friends',
|
||||
service: targetService
|
||||
}
|
||||
}, FTU_APP_ORIGIN);
|
||||
}
|
||||
|
||||
function unload() {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
extensionFrame.src = null;
|
||||
}
|
||||
|
||||
function close(messageId) {
|
||||
extensionFrame.addEventListener('transitionend', function tclose() {
|
||||
extensionFrame.removeEventListener('transitionend', tclose);
|
||||
extensionFrame.src = null;
|
||||
if (messageId) {
|
||||
utils.status.show(messageId);
|
||||
}
|
||||
});
|
||||
extensionFrame.className = 'closing';
|
||||
window.removeEventListener('message', messageHandler);
|
||||
}
|
||||
|
||||
|
||||
// This function can also be executed when other messages arrive
|
||||
// That's why we cannot call notifySettings outside the switch block
|
||||
function messageHandler(e) {
|
||||
if (e.origin !== FTU_APP_ORIGIN) {
|
||||
return;
|
||||
}
|
||||
var data = e.data;
|
||||
|
||||
switch (data.type) {
|
||||
case 'ready':
|
||||
open();
|
||||
break;
|
||||
|
||||
case 'abort':
|
||||
unload();
|
||||
break;
|
||||
|
||||
case 'window_close':
|
||||
close(data.messageId);
|
||||
break;
|
||||
|
||||
case 'authenticated':
|
||||
extensionFrame.src = currentURI;
|
||||
access_token = data.data;
|
||||
break;
|
||||
|
||||
case 'messaging_ready':
|
||||
extensionFrame.contentWindow.postMessage({
|
||||
type: 'token',
|
||||
data: access_token
|
||||
}, FTU_APP_ORIGIN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
start: load
|
||||
};
|
||||
|
||||
})(document);
|
||||
840
gaia/apps/ftu/js/sim_manager.js
Executable file
|
|
@ -0,0 +1,840 @@
|
|||
/* global ConfirmDialog,
|
||||
MobileOperator,
|
||||
Navigation,
|
||||
SimContactsImporter,
|
||||
UIManager,
|
||||
utils */
|
||||
|
||||
'use strict';
|
||||
|
||||
var SimManager = (function() {
|
||||
|
||||
/*
|
||||
* Icc helper object to track FTE related SIM state
|
||||
*/
|
||||
function Icc(mozIcc) {
|
||||
this.mozIcc = mozIcc;
|
||||
}
|
||||
Icc.prototype = {
|
||||
skipped: false,
|
||||
unlocked: false,
|
||||
alreadyImported: false,
|
||||
mozIcc: null,
|
||||
isLocked: function() {
|
||||
return !this.unlocked && lockStates.indexOf(this.mozIcc.cardState) !== -1;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Update ui element with a retry count message for the given sim
|
||||
*/
|
||||
function showRetryCount(icc, lockType, uiElement) {
|
||||
var request = icc.mozIcc.getCardLockRetryCount(lockType);
|
||||
request.onsuccess = function() {
|
||||
var retryCount = request.result.retryCount;
|
||||
if (retryCount) {
|
||||
var l10nArgs = {n: retryCount};
|
||||
document.l10n.setAttributes(uiElement,'inputCodeRetriesLeft',
|
||||
l10nArgs);
|
||||
uiElement.classList.remove('hidden');
|
||||
}
|
||||
};
|
||||
request.onerror = function() {
|
||||
console.error('Could not fetch CardLockRetryCount', request.error.name);
|
||||
};
|
||||
}
|
||||
|
||||
// mozIcc.cardState values for a locked SIM
|
||||
var lockStates = ['pinRequired', 'pukRequired', 'networkLocked',
|
||||
'corporateLocked', 'serviceProviderLocked', 'network1Locked',
|
||||
'network2Locked', 'hrpdNetworkLocked', 'ruimCorporateLocked',
|
||||
'ruimServiceProviderLocked'];
|
||||
|
||||
return {
|
||||
icc0: null,
|
||||
icc1: null,
|
||||
simSlots: window.navigator.mozMobileConnections ?
|
||||
window.navigator.mozMobileConnections.length : 0,
|
||||
|
||||
// track the SIM that is currently being unlocked
|
||||
_unlockingIcc: null,
|
||||
|
||||
init: function sm_init() {
|
||||
this.mobConn = window.navigator.mozMobileConnections;
|
||||
if (!this.mobConn) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iccManager = window.navigator.mozIccManager;
|
||||
if (!this.iccManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
// keep track of sim card info in slot 0 and 1
|
||||
this.icc0 = null;
|
||||
this.icc1 = null;
|
||||
|
||||
if (this.iccManager.iccIds[0]) {
|
||||
this.updateIccState(this.iccManager.iccIds[0]);
|
||||
}
|
||||
if (this.iccManager.iccIds[1]) {
|
||||
this.updateIccState(this.iccManager.iccIds[1]);
|
||||
}
|
||||
|
||||
this.iccManager.addEventListener('iccdetected',
|
||||
this.handleIccState.bind(this));
|
||||
|
||||
this.alreadyImported = false;
|
||||
},
|
||||
|
||||
handleUnlockError: function sm_handleUnlockError(lockType, retryCount) {
|
||||
var l10nArgs = {n: retryCount};
|
||||
switch (lockType) {
|
||||
case 'pin':
|
||||
if (retryCount === 0) {
|
||||
this.showPukScreen(this._unlockingIcc);
|
||||
break;
|
||||
}
|
||||
UIManager.pinInput.value = '';
|
||||
UIManager.pinInput.classList.add('onerror');
|
||||
UIManager.pinError.classList.remove('hidden');
|
||||
document.l10n.setAttributes(UIManager.pinRetriesLeft,
|
||||
'inputCodeRetriesLeft', l10nArgs);
|
||||
UIManager.pinRetriesLeft.classList.remove('hidden');
|
||||
if (retryCount === 1) {
|
||||
UIManager.pinError.
|
||||
querySelector('.lastchance').classList.remove('hidden');
|
||||
} else {
|
||||
document.l10n.setAttributes(
|
||||
UIManager.pinError.querySelector('.main'),
|
||||
'pinAttemptMsg2',
|
||||
l10nArgs
|
||||
);
|
||||
UIManager.pinError.querySelector('.main').classList.remove('hidden');
|
||||
}
|
||||
break;
|
||||
case 'puk':
|
||||
UIManager.pukInput.value = '';
|
||||
UIManager.pukInput.classList.add('onerror');
|
||||
UIManager.pukError.classList.remove('hidden');
|
||||
UIManager.pukInfo.classList.add('hidden');
|
||||
document.l10n.setAttributes(UIManager.pukRetriesLeft,
|
||||
'inputCodeRetriesLeft', l10nArgs);
|
||||
UIManager.pukRetriesLeft.classList.remove('hidden');
|
||||
if (retryCount === 1) {
|
||||
UIManager.pukError.
|
||||
querySelector('.lastchance').classList.remove('hidden');
|
||||
} else {
|
||||
document.l10n.setAttributes(
|
||||
UIManager.pukError.querySelector('.main'),
|
||||
'pukAttemptMsg2',
|
||||
l10nArgs
|
||||
);
|
||||
UIManager.pukError.querySelector('.main').classList.remove('hidden');
|
||||
}
|
||||
// TODO what if counter gets to 0 ??
|
||||
break;
|
||||
case 'nck':
|
||||
case 'cck':
|
||||
case 'spck':
|
||||
UIManager.xckInput.value = '';
|
||||
UIManager.xckInput.classList.add('onerror');
|
||||
UIManager.xckError.classList.remove('hidden');
|
||||
document.l10n.setAttributes(UIManager.xckRetriesLeft,
|
||||
'inputCodeRetriesLeft', l10nArgs);
|
||||
UIManager.xckRetriesLeft.classList.remove('hidden');
|
||||
if (retryCount == 1) {
|
||||
UIManager.xckError.
|
||||
querySelector('.lastchance').classList.remove('hidden');
|
||||
} else {
|
||||
document.l10n.setAttributes(
|
||||
UIManager.xckError.querySelector('.main'),
|
||||
'nckAttemptMsg2',
|
||||
l10nArgs
|
||||
);
|
||||
UIManager.xckError.querySelector('.main').classList.remove('hidden');
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
available: function sm_available() {
|
||||
var icc = this.guessIcc();
|
||||
return (icc && icc.cardState === 'ready');
|
||||
},
|
||||
|
||||
handleIccState: function sm_handleIccState(event) {
|
||||
this.updateIccState(event.iccId);
|
||||
},
|
||||
|
||||
updateIccState: function(iccId) {
|
||||
var iccInfo = this.iccManager.getIccById(iccId);
|
||||
if (!iccInfo) {
|
||||
throw new Error('Unrecognized iccID: ' + iccId);
|
||||
}
|
||||
|
||||
// determine SIM slot number
|
||||
if (this.mobConn[0] && iccId === this.mobConn[0].iccId) {
|
||||
this.icc0 = new Icc(iccInfo);
|
||||
} else if (this.mobConn[1] && iccId === this.mobConn[1].iccId) {
|
||||
this.icc1 = new Icc(iccInfo);
|
||||
} else {
|
||||
console.warn('ICC detected in unsupported slot', iccId);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Possible values:
|
||||
* null,
|
||||
* 'unknown',
|
||||
* 'pinRequired',
|
||||
* 'pukRequired',
|
||||
* 'networkLocked',
|
||||
* 'corporateLocked',
|
||||
* 'serviceProviderLocked',
|
||||
* 'network1Locked',
|
||||
* 'network2Locked',
|
||||
* 'hrpdNetworkLocked',
|
||||
* 'ruimCorporateLocked',
|
||||
* 'ruimServiceProviderLocked'
|
||||
* 'ready'.
|
||||
*/
|
||||
handleCardState: function sm_handleCardState(callback, skipUnlockScreen) {
|
||||
// used to track which SIM's PIN unlock
|
||||
// screen we are currently displaying
|
||||
this._unlockingIcc = null;
|
||||
|
||||
SimManager.checkSIMButton();
|
||||
if (typeof callback === 'function') {
|
||||
this.finishCallback = callback;
|
||||
}
|
||||
|
||||
if (this.shouldShowUnlockScreen(this.icc0) && !skipUnlockScreen) {
|
||||
this.showUnlockScreen(this.icc0);
|
||||
} else if (this.shouldShowUnlockScreen(this.icc1) && !skipUnlockScreen) {
|
||||
this.showUnlockScreen(this.icc1);
|
||||
} else if (this.shouldShowSIMInfoScreen()) {
|
||||
// reset skipped states so if we navigate back
|
||||
// we will redisplay the SIM unlock screens
|
||||
this.resetSkipped();
|
||||
this.showSIMInfoScreen();
|
||||
} else {
|
||||
this.resetSkipped();
|
||||
this.finish();
|
||||
}
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
this.hideScreen();
|
||||
this.hideSIMInfoScreen();
|
||||
// card state has been handled, so return
|
||||
// to normal navigation, and only show cell
|
||||
// data step if we have an unlocked sim
|
||||
var showCellData = (this.icc0 && !this.icc0.isLocked()) ||
|
||||
(this.icc1 && !this.icc1.isLocked());
|
||||
this.finishCallback && this.finishCallback(showCellData);
|
||||
},
|
||||
|
||||
shouldShowUnlockScreen: function sm_shouldShowLockScreen(icc) {
|
||||
return icc && !icc.skipped && icc.isLocked();
|
||||
},
|
||||
|
||||
// only show sim info screen if we have two SIMs inserted
|
||||
shouldShowSIMInfoScreen: function sm_shouldShowSIMInfoScreen() {
|
||||
return (this.icc0 && this.icc1);
|
||||
},
|
||||
|
||||
showUnlockScreen: function sm_showUnlockScreen(icc) {
|
||||
if (icc.unlocked) {
|
||||
return;
|
||||
}
|
||||
this._unlockingIcc = icc;
|
||||
|
||||
switch (icc.mozIcc.cardState) {
|
||||
case 'pinRequired':
|
||||
this.showPinScreen(icc);
|
||||
break;
|
||||
case 'pukRequired':
|
||||
this.showPukScreen(icc);
|
||||
break;
|
||||
case 'networkLocked':
|
||||
case 'corporateLocked':
|
||||
case 'serviceProviderLocked':
|
||||
case 'network1Locked':
|
||||
case 'network2Locked':
|
||||
case 'hrpdNetworkLocked':
|
||||
case 'ruimCorporateLocked':
|
||||
case 'ruimServiceProviderLocked':
|
||||
this.showXckScreen(icc);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Cannot show SIM unlock screen, unknown cardState ' +
|
||||
icc.mozIcc.cardState);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A container of event listeners to avoid listening several times to the same
|
||||
* event/object pair.
|
||||
*/
|
||||
voiceChangeListeners: [],
|
||||
|
||||
updateSIMInfoText: function sm_updateSIMInfoText(icc) {
|
||||
var iccNumber = (icc === this.icc0) ? 1 : 2;
|
||||
if (icc && icc.isLocked()) {
|
||||
UIManager['simInfo' + iccNumber].classList.add('locked');
|
||||
UIManager['simCarrier' + iccNumber].setAttribute('data-l10n-id',
|
||||
'simPinLocked');
|
||||
UIManager['simNumber' + iccNumber].removeAttribute('data-l10n-id');
|
||||
UIManager['simNumber' + iccNumber].textContent = '';
|
||||
} else {
|
||||
UIManager['simInfo' + iccNumber].classList.remove('locked');
|
||||
|
||||
var mobConn = this.mobConn[iccNumber - 1];
|
||||
var operator = MobileOperator
|
||||
.userFacingInfo(mobConn)
|
||||
.operator;
|
||||
|
||||
if (!operator && !this.voiceChangeListeners[iccNumber - 1]) {
|
||||
// The operator is not yet populated, let's listen to the `voicechange`
|
||||
// event once and update the UI later.
|
||||
this.voiceChangeListeners[iccNumber - 1] = function(evt) {
|
||||
var operator = MobileOperator
|
||||
.userFacingInfo(mobConn)
|
||||
.operator;
|
||||
|
||||
if (operator) {
|
||||
// If we have an operator, we update the UI and stop listening.
|
||||
UIManager['simCarrier' + iccNumber].removeAttribute('data-l10n-id');
|
||||
UIManager['simCarrier' + iccNumber].textContent = operator;
|
||||
mobConn.removeEventListener(
|
||||
'voicechange', this.voiceChangeListeners[iccNumber - 1]);
|
||||
this.voiceChangeListeners[iccNumber - 1] = null;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
mobConn.addEventListener(
|
||||
'voicechange', this.voiceChangeListeners[iccNumber - 1]);
|
||||
}
|
||||
|
||||
if (operator) {
|
||||
UIManager['simCarrier' + iccNumber].removeAttribute('data-l10n-id');
|
||||
UIManager['simCarrier' + iccNumber].textContent = operator;
|
||||
} else {
|
||||
UIManager['simCarrier' + iccNumber].setAttribute(
|
||||
'data-l10n-id', 'searchingOperator'
|
||||
);
|
||||
}
|
||||
var number = icc.mozIcc.iccInfo.msisdn ||
|
||||
icc.mozIcc.iccInfo.mdn || '';
|
||||
if (number) {
|
||||
UIManager['simNumber' + iccNumber].textContent = number;
|
||||
UIManager['simInfo' + iccNumber].classList.remove('no-number');
|
||||
} else {
|
||||
UIManager['simInfo' + iccNumber].classList.add('no-number');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showSIMInfoScreen: function sm_showSIMInfoScreen() {
|
||||
this.updateSIMInfoText(this.icc0);
|
||||
this.updateSIMInfoText(this.icc1);
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.simInfoScreen.classList.add('show');
|
||||
},
|
||||
|
||||
hideSIMInfoScreen: function sm_hidescreen() {
|
||||
UIManager.simInfoScreen.classList.remove('show');
|
||||
UIManager.activationScreen.classList.add('show');
|
||||
},
|
||||
|
||||
checkSIMButton: function sm_checkSIMButton() {
|
||||
if (!this.mobConn) {
|
||||
UIManager.simImport.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
var simOption = UIManager.simImportButton;
|
||||
// If there is an unlocked SIM we activate import from SIM
|
||||
if (!SimManager.alreadyImported && SimManager.available()) {
|
||||
simOption.removeAttribute('disabled');
|
||||
UIManager.noSim.classList.add('hidden');
|
||||
} else {
|
||||
simOption.setAttribute('disabled', 'disabled');
|
||||
if (!SimManager.alreadyImported) {
|
||||
UIManager.noSim.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showPinScreen: function sm_showPinScreen(icc) {
|
||||
showRetryCount(icc, 'pin', UIManager.pinRetriesLeft);
|
||||
// Button management
|
||||
UIManager.unlockSimButton.disabled = true;
|
||||
UIManager.pinInput.addEventListener('input', function sm_checkInput(event) {
|
||||
UIManager.unlockSimButton.disabled = (event.target.value.length < 4);
|
||||
});
|
||||
// Screen management
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.unlockSimScreen.classList.add('show');
|
||||
UIManager.pincodeScreen.classList.add('show');
|
||||
UIManager.xckcodeScreen.classList.remove('show');
|
||||
|
||||
UIManager.unlockSimHeader.setAttribute('data-l10n-id', 'pincode2');
|
||||
if (this.simSlots > 1) {
|
||||
var simNumber = icc === this.icc0 ? 1 : 2;
|
||||
document.l10n.setAttributes(UIManager.pinLabel,
|
||||
'pincodeLabel', {n: simNumber});
|
||||
} else {
|
||||
UIManager.pinLabel.setAttribute('data-l10n-id', 'type_pin');
|
||||
}
|
||||
UIManager.pinInput.focus();
|
||||
},
|
||||
|
||||
showPukScreen: function sm_showPukScreen(icc) {
|
||||
showRetryCount(icc, 'puk', UIManager.pukRetriesLeft);
|
||||
|
||||
UIManager.unlockSimScreen.classList.add('show');
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.pincodeScreen.classList.remove('show');
|
||||
UIManager.pukcodeScreen.classList.add('show');
|
||||
UIManager.xckcodeScreen.classList.remove('show');
|
||||
|
||||
UIManager.unlockSimHeader.setAttribute('data-l10n-id', 'pukcode');
|
||||
if (this.simSlots > 1) {
|
||||
var simNumber = icc === this.icc0 ? 1 : 2;
|
||||
document.l10n.setAttributes(UIManager.pukLabel,
|
||||
'pukcodeLabel', {n: simNumber});
|
||||
} else {
|
||||
UIManager.pukLabel.setAttribute('data-l10n-id', 'type_puk');
|
||||
}
|
||||
UIManager.pukInput.focus();
|
||||
},
|
||||
|
||||
showXckScreen: function sm_showXckScreen(icc) {
|
||||
var lockType;
|
||||
|
||||
switch (icc.mozIcc.cardState) {
|
||||
case 'networkLocked':
|
||||
lockType = 'nck';
|
||||
break;
|
||||
case 'corporateLocked':
|
||||
lockType = 'cck';
|
||||
break;
|
||||
case 'serviceProviderLocked':
|
||||
lockType = 'spck';
|
||||
break;
|
||||
case 'network1Locked':
|
||||
lockType = 'nck1';
|
||||
break;
|
||||
case 'network2Locked':
|
||||
lockType = 'nck2';
|
||||
break;
|
||||
case 'hrpdNetworkLocked':
|
||||
lockType = 'hnck';
|
||||
break;
|
||||
case 'ruimCorporateLocked':
|
||||
lockType = 'rcck';
|
||||
break;
|
||||
case 'ruimServiceProviderLocked':
|
||||
lockType = 'rspck';
|
||||
break;
|
||||
default:
|
||||
return; // We shouldn't be here.
|
||||
}
|
||||
|
||||
showRetryCount(icc, lockType, UIManager.xckRetriesLeft);
|
||||
|
||||
UIManager.unlockSimScreen.classList.add('show');
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.pincodeScreen.classList.remove('show');
|
||||
UIManager.pukcodeScreen.classList.remove('show');
|
||||
UIManager.xckcodeScreen.classList.add('show');
|
||||
|
||||
var simNumber = icc === this.icc0 ? 1 : 2;
|
||||
switch (icc.mozIcc.cardState) {
|
||||
case 'networkLocked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'nckcodeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'nckcodeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'corporateLocked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'cckcodeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'cckcodeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'serviceProviderLocked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'spckcodeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'spckcodeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'network1Locked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'nck1codeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'nck1codeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'network2Locked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'nck2codeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'nck2codeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'hrpdNetworkLocked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'hnckcodeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'hnckcodeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'ruimCorporateLocked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'rcckcodeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'rcckcodeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
case 'ruimServiceProviderLocked':
|
||||
document.l10n.setAttributes(UIManager.unlockSimHeader,
|
||||
'rspckcodeTitle', {n: simNumber});
|
||||
document.l10n.setAttributes(UIManager.xckLabel, 'rspckcodeLabel',
|
||||
{n: simNumber});
|
||||
break;
|
||||
}
|
||||
UIManager.xckInput.focus();
|
||||
},
|
||||
|
||||
resetForm: function sm_clearInputs() {
|
||||
this.clearFields();
|
||||
UIManager.newpinInput.value = '';
|
||||
UIManager.confirmNewpinInput.value = '';
|
||||
UIManager.pinInput.value = '';
|
||||
UIManager.pukInput.value = '';
|
||||
UIManager.pukInfo.classList.remove('hidden');
|
||||
UIManager.xckInput.value = '';
|
||||
UIManager.unlockSimButton.disabled = false;
|
||||
},
|
||||
|
||||
hideScreen: function sm_hideScreen() {
|
||||
UIManager.unlockSimScreen.classList.remove('show');
|
||||
UIManager.pincodeScreen.classList.remove('show');
|
||||
UIManager.pukcodeScreen.classList.remove('show');
|
||||
UIManager.xckcodeScreen.classList.remove('show');
|
||||
UIManager.activationScreen.classList.add('show');
|
||||
},
|
||||
|
||||
skip: function sm_skip() {
|
||||
if (this._unlockingIcc) {
|
||||
this._unlockingIcc.skipped = true;
|
||||
}
|
||||
this.resetForm();
|
||||
this.hideScreen();
|
||||
this.handleCardState();
|
||||
},
|
||||
|
||||
resetSkipped: function sm_resetSkipped() {
|
||||
if (this.icc0) {
|
||||
this.icc0.skipped = false;
|
||||
}
|
||||
if (this.icc1) {
|
||||
this.icc1.skipped = false;
|
||||
}
|
||||
},
|
||||
|
||||
back: function sm_back() {
|
||||
this.resetForm();
|
||||
this.resetSkipped();
|
||||
this.hideScreen();
|
||||
this.hideSIMInfoScreen();
|
||||
Navigation.back();
|
||||
},
|
||||
|
||||
simUnlockBack: function sm_simUnlockBack() {
|
||||
if (this.icc0 && this.icc0.skipped) {
|
||||
this.icc0.skipped = false;
|
||||
this.hideScreen();
|
||||
this.handleCardState();
|
||||
} else {
|
||||
this.back();
|
||||
}
|
||||
},
|
||||
|
||||
unlock: function sm_unlock() {
|
||||
var icc = this._unlockingIcc;
|
||||
if (!icc) {
|
||||
throw new Error('Cannot unlock SIM, no current ICC');
|
||||
}
|
||||
this.clearFields();
|
||||
|
||||
switch (icc.mozIcc.cardState) {
|
||||
case 'pinRequired':
|
||||
this.unlockPin(icc);
|
||||
break;
|
||||
case 'pukRequired':
|
||||
this.unlockPuk(icc);
|
||||
break;
|
||||
case 'networkLocked':
|
||||
case 'corporateLocked':
|
||||
case 'serviceProviderLocked':
|
||||
case 'network1Locked':
|
||||
case 'network2Locked':
|
||||
case 'hrpdNetworkLocked':
|
||||
case 'ruimCorporateLocked':
|
||||
case 'ruimServiceProviderLocked':
|
||||
this.unlockXck(icc);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
unlockPin: function sm_unlockPin(icc) {
|
||||
var pin = UIManager.pinInput.value;
|
||||
if (pin.length < 4 || pin.length > 8) {
|
||||
UIManager.pinError.
|
||||
querySelector('.main').setAttribute('data-l10n-id', 'pinValidation');
|
||||
UIManager.pinInput.classList.add('onerror');
|
||||
UIManager.pinError.classList.remove('hidden');
|
||||
UIManager.pinInput.focus();
|
||||
return;
|
||||
} else {
|
||||
UIManager.pinInput.classList.remove('onerror');
|
||||
UIManager.pinError.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Unlock SIM
|
||||
this.attemptUnlock(icc, {lockType: 'pin', pin: pin });
|
||||
},
|
||||
|
||||
clearFields: function sm_clearFields() {
|
||||
UIManager.pukInput.classList.remove('onerror');
|
||||
UIManager.pukError.classList.add('hidden');
|
||||
|
||||
UIManager.newpinInput.classList.remove('onerror');
|
||||
UIManager.newpinError.classList.add('hidden');
|
||||
|
||||
UIManager.confirmNewpinInput.classList.remove('onerror');
|
||||
UIManager.confirmNewpinError.classList.add('hidden');
|
||||
|
||||
UIManager.pinError.classList.add('hidden');
|
||||
UIManager.pinError.querySelector('.main').classList.add('hidden');
|
||||
UIManager.pinError.querySelector('.lastchance').classList.add('hidden');
|
||||
UIManager.pinRetriesLeft.classList.add('hidden');
|
||||
|
||||
UIManager.pukError.classList.add('hidden');
|
||||
UIManager.pukError.querySelector('.main').classList.add('hidden');
|
||||
UIManager.pukError.querySelector('.lastchance').classList.add('hidden');
|
||||
UIManager.pukRetriesLeft.classList.add('hidden');
|
||||
|
||||
UIManager.xckError.classList.add('hidden');
|
||||
UIManager.xckError.querySelector('.main').classList.add('hidden');
|
||||
UIManager.xckError.querySelector('.lastchance').classList.add('hidden');
|
||||
UIManager.xckRetriesLeft.classList.add('hidden');
|
||||
},
|
||||
|
||||
unlockPuk: function sm_unlockPuk(icc) {
|
||||
var pukCode = UIManager.pukInput.value;
|
||||
if (pukCode.length !== 8) {
|
||||
UIManager.pukError.querySelector('.main')
|
||||
.setAttribute('data-l10n-id', 'pukValidation');
|
||||
UIManager.pukError.classList.remove('hidden');
|
||||
UIManager.pukInfo.classList.add('hidden');
|
||||
UIManager.pukInput.classList.add('onerror');
|
||||
UIManager.pukError.focus();
|
||||
return;
|
||||
}
|
||||
var newpinCode = UIManager.newpinInput.value;
|
||||
var confirmNewpin = UIManager.confirmNewpinInput.value;
|
||||
if (newpinCode.length < 4 || newpinCode.length > 8) {
|
||||
UIManager.newpinError.setAttribute('data-l10n-id', 'pinValidation');
|
||||
UIManager.newpinError.classList.remove('hidden');
|
||||
UIManager.newpinInput.classList.add('onerror');
|
||||
UIManager.newpinError.focus();
|
||||
return;
|
||||
}
|
||||
if (newpinCode != confirmNewpin) {
|
||||
UIManager.confirmNewpinError.setAttribute('data-l10n-id',
|
||||
'newpinConfirmation');
|
||||
UIManager.confirmNewpinError.classList.remove('hidden');
|
||||
UIManager.newpinInput.classList.add('onerror');
|
||||
UIManager.confirmNewpinInput.classList.add('onerror');
|
||||
UIManager.confirmNewpinError.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock SIM with PUK and new PIN
|
||||
var options = {lockType: 'puk', puk: pukCode, newPin: newpinCode };
|
||||
this.attemptUnlock(icc, options);
|
||||
},
|
||||
|
||||
unlockXck: function sm_unlockXck(icc) {
|
||||
var xck = UIManager.xckInput.value;
|
||||
var lockType;
|
||||
switch (icc.mozIcc.cardState) {
|
||||
case 'networkLocked':
|
||||
lockType = 'nck';
|
||||
break;
|
||||
case 'corporateLocked':
|
||||
lockType = 'cck';
|
||||
break;
|
||||
case 'serviceProviderLocked':
|
||||
lockType = 'spck';
|
||||
break;
|
||||
case 'network1Locked':
|
||||
lockType = 'nck1';
|
||||
break;
|
||||
case 'network2Locked':
|
||||
lockType = 'nck2';
|
||||
break;
|
||||
case 'hrpdNetworkLocked':
|
||||
lockType = 'hnck';
|
||||
break;
|
||||
case 'ruimCorporateLocked':
|
||||
lockType = 'rcck';
|
||||
break;
|
||||
case 'ruimServiceProviderLocked':
|
||||
lockType = 'rspck';
|
||||
break;
|
||||
}
|
||||
if (xck.length < 8 || xck.length > 16) {
|
||||
UIManager.xckInput.classList.add('onerror');
|
||||
UIManager.xckError.classList.remove('hidden');
|
||||
UIManager.xckError.querySelector('.main').
|
||||
setAttribute('data-l10n-id', lockType + 'Validation');
|
||||
UIManager.xckInput.focus();
|
||||
return;
|
||||
} else {
|
||||
UIManager.pinInput.classList.remove('onerror');
|
||||
UIManager.pinError.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Unlock SIM
|
||||
var options = {lockType: lockType, pin: xck };
|
||||
this.attemptUnlock(icc, options);
|
||||
},
|
||||
|
||||
attemptUnlock: function sm_attemptUnlock(icc, options) {
|
||||
var req = icc.mozIcc.unlockCardLock(options);
|
||||
req.onsuccess = (function sm_unlockSuccess() {
|
||||
icc.unlocked = true;
|
||||
this.resetForm();
|
||||
this.hideScreen();
|
||||
this.handleCardState();
|
||||
}).bind(this);
|
||||
req.onerror = (function sm_unlockError() {
|
||||
this.handleUnlockError(options.lockType, req.error.retryCount);
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
// Try to infer whats the default SIM
|
||||
guessIcc: function guessIcc() {
|
||||
var tempIcc = null;
|
||||
if (navigator.mozMobileConnections) {
|
||||
// New multi-sim api, use mozMobileConnections to guess
|
||||
// the first inserted sim
|
||||
for (var i = 0;
|
||||
i < navigator.mozMobileConnections.length && tempIcc === null; i++) {
|
||||
if (navigator.mozMobileConnections[i] !== null &&
|
||||
navigator.mozMobileConnections[i].iccId !== null) {
|
||||
tempIcc = navigator.mozIccManager.getIccById(
|
||||
navigator.mozMobileConnections[i].iccId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tempIcc = navigator.mozIccManager;
|
||||
}
|
||||
|
||||
return tempIcc;
|
||||
},
|
||||
|
||||
importContacts: function sm_importContacts() {
|
||||
// Delay for showing feedback to the user after importing
|
||||
var DELAY_FEEDBACK = 300;
|
||||
UIManager.navBar.setAttribute('aria-disabled', 'true');
|
||||
var progress = utils.overlay.show('simContacts-reading',
|
||||
'activityBar');
|
||||
|
||||
var importButton = UIManager.simImportButton;
|
||||
var cancelled = false,
|
||||
contactsRead = false;
|
||||
var importer = new SimContactsImporter(SimManager.guessIcc());
|
||||
utils.overlay.showMenu();
|
||||
utils.overlay.oncancel = function oncancel() {
|
||||
cancelled = true;
|
||||
importer.finish();
|
||||
if (contactsRead) {
|
||||
// A message about canceling will be displayed while the current chunk
|
||||
// is being cooked
|
||||
progress.setClass('activityBar');
|
||||
utils.overlay.hideMenu();
|
||||
progress.setHeaderMsg('messageCanceling');
|
||||
} else {
|
||||
importer.onfinish(); // Early return while reading contacts
|
||||
}
|
||||
};
|
||||
var importedContacts = 0;
|
||||
|
||||
importer.onread = function sim_import_read(n) {
|
||||
contactsRead = true;
|
||||
if (n > 0) {
|
||||
progress.setClass('progressBar');
|
||||
progress.setHeaderMsg('simContacts-importing');
|
||||
progress.setTotal(n);
|
||||
}
|
||||
};
|
||||
|
||||
importer.onimported = function imported_contact() {
|
||||
importedContacts++;
|
||||
if (!cancelled) {
|
||||
progress.update();
|
||||
}
|
||||
};
|
||||
|
||||
importer.onfinish = function sim_import_finish(numDupsMerged, iccId) {
|
||||
window.setTimeout(function do_sim_import_finish() {
|
||||
UIManager.navBar.removeAttribute('aria-disabled');
|
||||
utils.overlay.hide();
|
||||
if (importedContacts > 0) {
|
||||
utils.misc.setTimestamp('sim-' + iccId);
|
||||
}
|
||||
if (!cancelled) {
|
||||
SimManager.alreadyImported = true;
|
||||
importButton.setAttribute('disabled', 'disabled');
|
||||
utils.status.show({
|
||||
id: 'simContacts-imported3',
|
||||
args: {n: importedContacts}
|
||||
});
|
||||
}
|
||||
}, DELAY_FEEDBACK);
|
||||
|
||||
importer.onfinish = null;
|
||||
};
|
||||
|
||||
importer.onerror = function sim_import_error() {
|
||||
UIManager.navBar.removeAttribute('aria-disabled');
|
||||
utils.overlay.hide();
|
||||
// Just in case the user decides to do so later
|
||||
importButton.removeAttribute('disabled');
|
||||
|
||||
// Showing error message allowing user to retry
|
||||
var cancel = {
|
||||
title: 'cancel',
|
||||
callback: function() {
|
||||
ConfirmDialog.hide();
|
||||
}
|
||||
};
|
||||
var retry = {
|
||||
title: 'retry',
|
||||
isRecommend: true,
|
||||
callback: function() {
|
||||
ConfirmDialog.hide();
|
||||
// And now the action is reproduced one more time
|
||||
importButton.click();
|
||||
}
|
||||
};
|
||||
ConfirmDialog.show(null, 'simContacts-error', cancel, retry);
|
||||
}; // importer.onerror
|
||||
|
||||
importer.start();
|
||||
}
|
||||
};
|
||||
})();
|
||||
35
gaia/apps/ftu/js/time.js
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
/* exported TimeManager */
|
||||
'use strict';
|
||||
|
||||
var TimeManager = {
|
||||
elementIDs: [
|
||||
'tz-region',
|
||||
'tz-city',
|
||||
'date-configuration',
|
||||
'time-configuration'
|
||||
],
|
||||
|
||||
init: function tm_init() {
|
||||
if (window.navigator.mozTime) {
|
||||
this.time = window.navigator.mozTime;
|
||||
} else {
|
||||
console.log('There is no mozTime available in window');
|
||||
}
|
||||
|
||||
this.elementIDs.forEach(function(elementID) {
|
||||
document.getElementById(elementID + '-button').addEventListener('click',
|
||||
function(evt) {
|
||||
evt.preventDefault();
|
||||
var input = document.getElementById(elementID);
|
||||
// Inspired by app/clock/js/form_button.js
|
||||
// It seems like setting focus instantly does not trigger the input/
|
||||
// select dialog.
|
||||
setTimeout(input.focus.bind(input), 10);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
set: function tm_set(date) {
|
||||
this.time.set(date);
|
||||
}
|
||||
};
|
||||
383
gaia/apps/ftu/js/tutorial.js
Executable file
|
|
@ -0,0 +1,383 @@
|
|||
/* global ScreenLayout, Promise,
|
||||
Utils, FinishScreen, LazyLoader, TutorialUtils */
|
||||
/* exported Tutorial */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
// default layout
|
||||
var currentLayout = 'tiny';
|
||||
|
||||
// Most used DOM elements
|
||||
var dom = {};
|
||||
|
||||
/**
|
||||
* Manages and controls the configuration, content and state of the tutorial
|
||||
* @module Tutorial
|
||||
*/
|
||||
var elementIDs = [
|
||||
'tutorial',
|
||||
'tutorial-step-title',
|
||||
'tutorial-step-media',
|
||||
'tutorial-step-image',
|
||||
'tutorial-step-video',
|
||||
'forward-tutorial',
|
||||
'back-tutorial'
|
||||
];
|
||||
|
||||
var Tutorial = {
|
||||
// A configuration object.
|
||||
config: null,
|
||||
|
||||
// Config for the current tutorial steps
|
||||
_stepsConfig: {},
|
||||
|
||||
// Which tutorial steps to use
|
||||
_stepsKey: '',
|
||||
|
||||
// Keeps track of the current step
|
||||
_currentStep: 1,
|
||||
|
||||
// Track initialized state of tutorial
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Initialize the tutorial. This is async as a config file must be loaded.
|
||||
* A Sequence (array of sync or async functions) is used to manage the init
|
||||
* tasks.
|
||||
* When complete, the tutorial is ready to be shown via the 'start' method
|
||||
*
|
||||
* @param {String} stepsKey a key into the tutorial config object, i
|
||||
* i.e. a version or version delta like 1.3..2.0
|
||||
* @param {Function} onLoaded optional callback for when init is complete
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
init: function(stepsKey, onLoaded) {
|
||||
// init is async
|
||||
// need to load config, then load the first step and its assets.
|
||||
if (this._initialized || this._initialization) {
|
||||
// init already underway
|
||||
return;
|
||||
}
|
||||
|
||||
var initTasks = this._initialization = new TutorialUtils.Sequence(
|
||||
// config should load or already be loaded.
|
||||
// failure should abort
|
||||
this.loadConfig.bind(this),
|
||||
this._initWithConfig.bind(this, stepsKey)
|
||||
);
|
||||
initTasks.onabort = this._onAbortInitialization.bind(this);
|
||||
initTasks.oncomplete =
|
||||
this._onCompleteInitialization.bind(this, onLoaded);
|
||||
|
||||
// first time here: cache DOM elements
|
||||
elementIDs.forEach(function(name) {
|
||||
dom[Utils.camelCase(name)] = document.getElementById(name);
|
||||
if (!dom[Utils.camelCase(name)]) {
|
||||
console.error('Cache DOM elements: couldnt cache: ' + name);
|
||||
}
|
||||
}, this);
|
||||
|
||||
initTasks.next();
|
||||
|
||||
// watch a ton of video events as context for the tutorial
|
||||
// media loading/playing
|
||||
var mediaEvents = this._mediaEvents = ['abort',
|
||||
'canplay',
|
||||
'canplaythrough',
|
||||
'durationchange',
|
||||
'emptied',
|
||||
'ended',
|
||||
'error',
|
||||
'interruptbegin',
|
||||
'interruptend',
|
||||
'loadeddata',
|
||||
'loadedmetadata',
|
||||
'loadstart',
|
||||
'mozaudioavailable',
|
||||
'pause',
|
||||
'play',
|
||||
'playing',
|
||||
'stalled',
|
||||
'suspend',
|
||||
'waiting'];
|
||||
|
||||
this._debugEventHandler = {
|
||||
handleEvent: function(evt) {
|
||||
var errCodes = {
|
||||
'1': 'MEDIA_ERR_ABORTED',
|
||||
'2': 'MEDIA_ERR_NETWORK',
|
||||
'3': 'MEDIA_ERR_DECODE',
|
||||
'4': 'MEDIA_ERR_SRC_NOT_SUPPORTED'
|
||||
};
|
||||
console.log('TUTORIAL: media-event: '+ evt.type);
|
||||
if (evt.type == 'error') {
|
||||
var errtype = errCodes[evt.target.error.code] || 'unknown';
|
||||
console.log('TUTORIAL: media-event, ' + errtype + ' error');
|
||||
}
|
||||
}
|
||||
};
|
||||
mediaEvents.forEach(name => {
|
||||
dom.tutorialStepVideo.addEventListener(name, this._debugEventHandler);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the tutorial and play the first step
|
||||
* May be called during or after the init process
|
||||
* We defer rasing the 'tutorialinitialized' event until this point
|
||||
* as it signals the tutorial step content is loaded and displayed
|
||||
*
|
||||
* @param {Function} onReady optional callback for when start is complete
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
start: function(onReady) {
|
||||
var sequence;
|
||||
var initInProgress = false;
|
||||
if (this._initialization) {
|
||||
// init still underway, tack steps onto existing sequence
|
||||
sequence = this._initialization;
|
||||
initInProgress = true;
|
||||
} else {
|
||||
// init already complete, create new sequence
|
||||
this._initialization = sequence = new TutorialUtils.Sequence();
|
||||
sequence.onabort = this._onAbortInitialization.bind(this);
|
||||
sequence.oncomplete =
|
||||
this._onCompleteInitialization.bind(this);
|
||||
}
|
||||
sequence.push(function setInitialStep() {
|
||||
// setStep should return promise given by _loadMedia
|
||||
console.log('TUTORIAL: setInitialStep');
|
||||
return this._setStep();
|
||||
}.bind(this));
|
||||
|
||||
sequence.push(function showTutorialAndFinishInit() {
|
||||
// Show the panel
|
||||
dom.tutorial.classList.add('show');
|
||||
// Custom event that can be used to apply (screen reader) visibility
|
||||
// changes.
|
||||
console.log('TUTORIAL: dispatching tutorialinitialized');
|
||||
window.dispatchEvent(new CustomEvent('tutorialinitialized'));
|
||||
});
|
||||
|
||||
if(typeof onReady === 'function') {
|
||||
sequence.push(onReady);
|
||||
}
|
||||
|
||||
if (!initInProgress) {
|
||||
// init done, starting start sequence
|
||||
sequence.next();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Continue initialization once config data is loaded
|
||||
* Called as a part of the init sequence
|
||||
*
|
||||
* @param {String} stepsKey a key into the tutorial config object, i
|
||||
* i.e. a version or version delta like 1.3..2.0
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
_initWithConfig: function(stepsKey) {
|
||||
stepsKey = stepsKey || 'default';
|
||||
this._stepsKey = stepsKey;
|
||||
this._stepsConfig = this.config[stepsKey] || this.config['default'];
|
||||
|
||||
// Add event listeners
|
||||
dom.forwardTutorial.addEventListener('click', this);
|
||||
dom.backTutorial.addEventListener('click', this);
|
||||
|
||||
// Set the first step
|
||||
this._currentStep = 1;
|
||||
},
|
||||
_onAbortInitialization: function() {
|
||||
this._initialization = null;
|
||||
},
|
||||
_onCompleteInitialization: function(onReady) {
|
||||
this._initialization = null;
|
||||
this._initialized = true;
|
||||
if(typeof onReady === 'function') {
|
||||
onReady();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Advance the tutorial to the given step (first step if no value given)
|
||||
*
|
||||
* @param {Number} value number of the step to show (1-based)
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
_setStep: function (value) {
|
||||
// If value is bigger than the max, show finish screen
|
||||
value = (typeof value === 'number') ? value : this._currentStep;
|
||||
var stepIndex = value - 1;
|
||||
if (stepIndex >= this._stepsConfig.steps.length) {
|
||||
return Promise.resolve().then(function() {
|
||||
Tutorial.done();
|
||||
});
|
||||
}
|
||||
|
||||
var stepData = this._stepsConfig.steps[stepIndex];
|
||||
if (!stepData) {
|
||||
return Promise.reject('No data for step: ' + value);
|
||||
}
|
||||
// Set the step
|
||||
dom.tutorial.dataset.step = this._currentStep;
|
||||
|
||||
// Internationalize
|
||||
document.l10n.setAttributes(
|
||||
dom.tutorialStepTitle,
|
||||
stepData.l10nKey
|
||||
);
|
||||
|
||||
// Update the image/video
|
||||
var imgElement = dom.tutorialStepImage,
|
||||
videoElement = dom.tutorialStepVideo;
|
||||
|
||||
var stepPromise;
|
||||
if (stepData.video) {
|
||||
stepPromise = TutorialUtils.getBestAssetForDirection(stepData.video)
|
||||
.then((bestSrc) => {
|
||||
return TutorialUtils.loadAndPlayMedia(videoElement, bestSrc);
|
||||
});
|
||||
videoElement.hidden = false;
|
||||
imgElement.hidden = true;
|
||||
} else {
|
||||
imgElement.hidden = false;
|
||||
stepPromise = TutorialUtils.loadMedia(imgElement, stepData.image);
|
||||
imgElement.hidden = false;
|
||||
videoElement.hidden = true;
|
||||
}
|
||||
return stepPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* DOM Event handler
|
||||
*
|
||||
* @param {DOMEvent} evt Event object
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
handleEvent: function(evt) {
|
||||
if (evt.type === 'click') {
|
||||
switch(evt.target) {
|
||||
case dom.forwardTutorial:
|
||||
this.next(evt);
|
||||
break;
|
||||
case dom.backTutorial:
|
||||
this.back(evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Advance to the next step in the tutorial
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
next: function() {
|
||||
return this._setStep(++this._currentStep);
|
||||
},
|
||||
|
||||
/**
|
||||
* Go back to the previous step in the tutorial
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
back: function() {
|
||||
return this._setStep(--this._currentStep);
|
||||
},
|
||||
|
||||
/**
|
||||
* Tutorial complete
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
done: function() {
|
||||
var isUpgrade = this._stepsKey && this._stepsKey !== 'default';
|
||||
FinishScreen.init(isUpgrade);
|
||||
dom.tutorial.classList.remove('show');
|
||||
dom.tutorialStepVideo.removeAttribute('src');
|
||||
dom.tutorialStepImage.removeAttribute('src');
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the config with steps and associated resources for the tutorial
|
||||
* We have different config files for each screen category:
|
||||
* phone is 'tiny', tablet is 'large'
|
||||
* and corresponding tiny.json, large.json
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
loadConfig: function() {
|
||||
if (!this._configPromise) {
|
||||
// Update the value of the layout if needed
|
||||
// 'ScreenLayout' give us 4 different values
|
||||
// tiny: '(max-width: 767px)',
|
||||
// small: '(min-width: 768px) and (max-width: 991px)',
|
||||
// medium: '(min-width: 992px) and (max-width: 1200px)',
|
||||
// large: '(min-width: 1201px)',
|
||||
//
|
||||
// Currently we are taking into account only 'tiny', and we are
|
||||
// going to consider 'tablet' as 'large'. If we want to add more
|
||||
// or specific features for 'small' & 'medium', we should add more
|
||||
// logic here.
|
||||
|
||||
currentLayout = ScreenLayout.getCurrentLayout() === 'tiny' ?
|
||||
'tiny' : 'large';
|
||||
|
||||
var configUrl = '/config/' + currentLayout + '.json';
|
||||
|
||||
this._configPromise = LazyLoader.getJSON(configUrl)
|
||||
.then(function(json) {
|
||||
Tutorial.config = json;
|
||||
return Tutorial.config;
|
||||
}, function() {
|
||||
return new Error('Tutorial config failed to load from: ' + configUrl);
|
||||
});
|
||||
}
|
||||
return this._configPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Test helper to reset the tutorial to its pre-initialized state
|
||||
* to allow init to be called again
|
||||
* @memberof Tutorial
|
||||
*/
|
||||
reset: function() {
|
||||
var resetPromise = Promise.resolve();
|
||||
if (dom.tutorialStepVideo) {
|
||||
this._mediaEvents.forEach(name => {
|
||||
dom.tutorialStepVideo.removeEventListener(
|
||||
name,
|
||||
this._debugEventHandler
|
||||
);
|
||||
});
|
||||
dom.tutorialStepVideo.hidden = true;
|
||||
if (dom.tutorialStepVideo.src) {
|
||||
resetPromise = new Promise((resolve, reject) => {
|
||||
dom.tutorialStepVideo.addEventListener('emptied', () => {
|
||||
resolve();
|
||||
});
|
||||
dom.tutorialStepVideo.removeAttribute('src');
|
||||
dom.tutorialStepVideo.load();
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this._initialization) {
|
||||
this._initialization.abort();
|
||||
this._initialization = null;
|
||||
}
|
||||
this._configPromise = null;
|
||||
this._currentStep = 1;
|
||||
this._stepsConfig = this.config = null;
|
||||
if (this._initialized) {
|
||||
dom.tutorial.classList.remove('show');
|
||||
this._initialized = false;
|
||||
}
|
||||
document.getElementById('tutorial').classList.remove('show');
|
||||
return resetPromise;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Tutorial = Tutorial;
|
||||
|
||||
})(this);
|
||||
179
gaia/apps/ftu/js/tutorial_utils.js
Executable file
|
|
@ -0,0 +1,179 @@
|
|||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var TutorialUtils = {
|
||||
/**
|
||||
* Helper function to load images and video
|
||||
* @param {DOMNode} mediaElement video or image to assign new src to
|
||||
* @param {String} src URL for video/image resource
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loadMedia: function (mediaElement, src) {
|
||||
console.log('loadMedia, got mediaElement: ', mediaElement);
|
||||
var isVideo = (mediaElement.nodeName === 'VIDEO');
|
||||
console.log('TutorialUtils.loadMedia, isVideo: ',
|
||||
isVideo, mediaElement.nodeName);
|
||||
return new Promise(function(resolve, reject) {
|
||||
function onMediaLoadOrError(evt) {
|
||||
console.log('TutorialUtils.loadMedia, onMediaLoadOrError for ' +
|
||||
'evt.type: ' + (evt && evt.type));
|
||||
evt.target.removeEventListener('error', onMediaLoadOrError);
|
||||
if (isVideo) {
|
||||
evt.target.removeEventListener('canplay', onMediaLoadOrError);
|
||||
evt.target.removeEventListener('abort', onMediaLoadOrError);
|
||||
} else {
|
||||
evt.target.removeEventListener('load', onMediaLoadOrError);
|
||||
}
|
||||
// Dont block progress on failure to load media
|
||||
if (evt.type === 'error') {
|
||||
console.error('Failed to load tutorial media: ' + src);
|
||||
} else if (evt.type === 'abort') {
|
||||
console.error('Loading of tutorial media aborted: ' + src);
|
||||
}
|
||||
resolve(evt);
|
||||
}
|
||||
function onVideoUnloaded(evt) {
|
||||
console.log('TutorialUtils.loadMedia, onVideoUnloaded for evt.type: ' +
|
||||
(evt && evt.type));
|
||||
mediaElement.removeEventListener('emptied', onVideoUnloaded);
|
||||
mediaElement.addEventListener('canplay', onMediaLoadOrError);
|
||||
mediaElement.addEventListener('abort', onMediaLoadOrError);
|
||||
mediaElement.addEventListener('error', onMediaLoadOrError);
|
||||
console.log('TutorialUtils.loadMedia, onVideoUnloaded, ' +
|
||||
'assigning src: ' + src);
|
||||
mediaElement.src = src;
|
||||
mediaElement.load();
|
||||
}
|
||||
if (isVideo) {
|
||||
// must unload video and force load before switching to new source
|
||||
if (mediaElement.src) {
|
||||
console.log('TutorialUtils.loadMedia, removing src attribute');
|
||||
mediaElement.removeAttribute('src');
|
||||
mediaElement.addEventListener('emptied', onVideoUnloaded, false);
|
||||
console.log('TutorialUtils.loadMedia, calling load');
|
||||
mediaElement.load();
|
||||
} else {
|
||||
console.log('TutorialUtils.loadMedia, no src, ' +
|
||||
'just calling onVideoUnloaded');
|
||||
onVideoUnloaded();
|
||||
}
|
||||
} else {
|
||||
console.log('TutorialUtils.loadMedia, not a video, ' +
|
||||
'listen for load/error only');
|
||||
mediaElement.addEventListener('load', onMediaLoadOrError, false);
|
||||
mediaElement.addEventListener('error', onMediaLoadOrError, false);
|
||||
mediaElement.src = src;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience to load and then play video
|
||||
* @param {DOMNode} mediaElement video or image to assign new src to
|
||||
* @param {String} src URL for video/image resource
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loadAndPlayMedia: function(mediaElement, src) {
|
||||
return this.loadMedia(mediaElement, src).then((evt) => {
|
||||
if (evt.type !== 'error' && mediaElement.nodeName === 'VIDEO') {
|
||||
mediaElement.play();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to check (via HEAD request) a given resource exists
|
||||
* The returned promise resolves with a true|false value
|
||||
*
|
||||
* @param {String} src URL for video/image resource
|
||||
* @returns {Promise}
|
||||
*/
|
||||
fileExists: function (src) {
|
||||
return new Promise((resolve) => {
|
||||
window.fetch(src, { method: 'HEAD' }).then((response) => {
|
||||
resolve(response.ok);
|
||||
}).catch(() => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to get best-match resource url for the current document.dir
|
||||
* @param {String} src URL for video/image resource
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getBestAssetForDirection: function(src) {
|
||||
var dir = document.dir || 'ltr';
|
||||
var re =
|
||||
/(-rtl|ltr)?\.(png|gif|jpg|webm|mp4|m4v|ogg|ogv)$/;
|
||||
var directionSrc = src.replace(re, '-'+dir+'.$2');
|
||||
return this.fileExists(directionSrc).then((result) => {
|
||||
return result ? directionSrc : src;
|
||||
}).catch((e) => {
|
||||
console.log('Exception from getBestAssetForDirection: ', e);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Private helper class to manage a series of sync or async functions
|
||||
*
|
||||
* The array may be manipulated using standard array methods while the
|
||||
* sequence runs. The sequence completes when there are no more functions or
|
||||
* an exception is raised.
|
||||
* At the end of the sequence, any 'oncomplete' assigned will be called with
|
||||
* the return value from the last function
|
||||
* Functions may return a 'thenable' to indicate async return
|
||||
* Exceptions will be passed into the oncomplete function
|
||||
* A Sequence may be cleanly aborted by calling abort() - no callbacks will
|
||||
* be fired
|
||||
* @class Sequence
|
||||
*/
|
||||
Sequence: function() {
|
||||
var sequence = Array.slice(arguments);
|
||||
var aborted = false;
|
||||
sequence.abort = function() {
|
||||
aborted = true;
|
||||
this.length = 0;
|
||||
if (typeof this.onabort === 'function') {
|
||||
this.onabort();
|
||||
}
|
||||
};
|
||||
sequence.complete = function(result) {
|
||||
if(!aborted && typeof this.oncomplete === 'function') {
|
||||
this.oncomplete(result);
|
||||
}
|
||||
};
|
||||
sequence.fail = function(reason) {
|
||||
this.complete(reason);
|
||||
};
|
||||
sequence.next = function(previousTaskResult) {
|
||||
var result, exception;
|
||||
if (aborted) {
|
||||
return;
|
||||
}
|
||||
var task = this.shift();
|
||||
if (task) {
|
||||
try {
|
||||
result = task.apply(null, arguments);
|
||||
} catch(e) {
|
||||
exception = e;
|
||||
}
|
||||
if (exception) {
|
||||
this.fail(exception);
|
||||
} else if (result && typeof result.then === 'function') {
|
||||
result.then(this.next.bind(this), this.fail.bind(this));
|
||||
} else {
|
||||
this.next(result);
|
||||
}
|
||||
} else {
|
||||
this.complete(previousTaskResult);
|
||||
}
|
||||
};
|
||||
return sequence;
|
||||
}
|
||||
};
|
||||
|
||||
exports.TutorialUtils = TutorialUtils;
|
||||
|
||||
})(window);
|
||||
650
gaia/apps/ftu/js/ui.js
Executable file
|
|
@ -0,0 +1,650 @@
|
|||
/* global utils, tzSelect, FinishScreen,
|
||||
Basket, ConfirmDialog, ScreenLayout,
|
||||
DataMobile, SimManager, SdManager,
|
||||
Tutorial, TimeManager, WifiManager,
|
||||
WifiUI, WifiHelper, FxAccountsIACHelper, SettingsListener */
|
||||
/* exported UIManager */
|
||||
'use strict';
|
||||
|
||||
var UIManager = {
|
||||
// As in other Gaia apps, we store all the dom selectors in one
|
||||
// place and then camelCase them and attach to the main object,
|
||||
// eg. instead of calling document.getElementById('splash-screen')
|
||||
// we can access this.splashScreen in our code.
|
||||
domSelectors: [
|
||||
'container',
|
||||
'splash-screen',
|
||||
'activation-screen',
|
||||
'finish-screen',
|
||||
'update-screen',
|
||||
'nav-bar',
|
||||
'main-title',
|
||||
// Unlock SIM Screen
|
||||
'unlock-sim-screen',
|
||||
'unlock-sim-header',
|
||||
'unlock-sim-action',
|
||||
// PIN Screen
|
||||
'pincode-screen',
|
||||
'pin-label',
|
||||
'pin-retries-left',
|
||||
'pin-input',
|
||||
'back-sim-button',
|
||||
'pin-error',
|
||||
'skip-pin-button',
|
||||
'unlock-sim-button',
|
||||
// PUK Screen
|
||||
'pukcode-screen',
|
||||
'puk-label',
|
||||
'puk-retries-left',
|
||||
'puk-input',
|
||||
'puk-info',
|
||||
'puk-error',
|
||||
'newpin-input',
|
||||
'newpin-error',
|
||||
'confirm-newpin-input',
|
||||
'confirm-newpin-error',
|
||||
// XCK Screen
|
||||
'xckcode-screen',
|
||||
'xck-label',
|
||||
'xck-retries-left',
|
||||
'xck-input',
|
||||
'xck-error',
|
||||
// SIM info
|
||||
'sim-info-screen',
|
||||
'sim-info-back',
|
||||
'sim-info-forward',
|
||||
'sim-info-1',
|
||||
'sim-info-2',
|
||||
'sim-number-1',
|
||||
'sim-number-2',
|
||||
'sim-carrier-1',
|
||||
'sim-carrier-2',
|
||||
// Import contacts
|
||||
'sim-import',
|
||||
'sim-import-button',
|
||||
'no-sim',
|
||||
'sd-import-button',
|
||||
'no-memorycard',
|
||||
// Fxa Intro
|
||||
'fxa-create-account',
|
||||
'fxa-intro',
|
||||
// Wifi
|
||||
'networks',
|
||||
'wifi-refresh-button',
|
||||
'wifi-join-button',
|
||||
'join-hidden-button',
|
||||
// Hidden Wifi
|
||||
'hidden-wifi-authentication',
|
||||
'hidden-wifi-ssid',
|
||||
'hidden-wifi-security',
|
||||
'hidden-wifi-password',
|
||||
'hidden-wifi-password-box',
|
||||
'hidden-wifi-identity',
|
||||
'hidden-wifi-identity-box',
|
||||
'hidden-wifi-show-password',
|
||||
//Date & Time
|
||||
'date-configuration',
|
||||
'time-configuration',
|
||||
'date-configuration-label',
|
||||
'time-configuration-label',
|
||||
'time-form',
|
||||
// 3G
|
||||
'data-connection-switch',
|
||||
// Geolocation
|
||||
'geolocation-switch',
|
||||
// Tutorial
|
||||
'lets-go-button',
|
||||
'update-lets-go-button',
|
||||
'skip-tutorial-button',
|
||||
'update-skip-tutorial-button',
|
||||
// Privacy Settings
|
||||
'metrics-basic',
|
||||
'metrics-enhanced',
|
||||
'metrics-none',
|
||||
'offline-error-dialog',
|
||||
// Browser privacy newsletter subscription
|
||||
'newsletter-form',
|
||||
'newsletter-input',
|
||||
'newsletter-success-screen',
|
||||
'offline-newsletter-error-dialog',
|
||||
'invalid-email-error-dialog'
|
||||
],
|
||||
|
||||
dataConnectionChangedByUsr: false,
|
||||
timeZoneNeedsConfirmation: true,
|
||||
DARK_THEME: '#242d33',
|
||||
LIGHT_THEME: '#eeeeee',
|
||||
|
||||
init: function ui_init() {
|
||||
// Initialization of the DOM selectors
|
||||
this.domSelectors.forEach(function createElementRef(name) {
|
||||
this[toCamelCase(name)] = document.getElementById(name);
|
||||
}.bind(this));
|
||||
|
||||
// Setup settings observers
|
||||
this._settingsObserveHandler = {
|
||||
'geolocation.enabled': {
|
||||
// the "checked" attribute in the DOM is currently the source of truth
|
||||
// for default value, when the setting is not initially defined
|
||||
defaultValue: this.geolocationSwitch.checked,
|
||||
callback: function(value) {
|
||||
var isEnabled = !!value;
|
||||
if (this.geolocationSwitch.checked !== isEnabled) {
|
||||
this.geolocationSwitch.checked = isEnabled;
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
};
|
||||
|
||||
for (var name in this._settingsObserveHandler) {
|
||||
SettingsListener.observe(
|
||||
name,
|
||||
this._settingsObserveHandler[name].defaultValue,
|
||||
this._settingsObserveHandler[name].callback
|
||||
);
|
||||
}
|
||||
|
||||
var currentDate = new Date();
|
||||
this.timeConfigurationLabel.textContent = currentDate.toLocaleString(
|
||||
navigator.languages,
|
||||
{
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric'
|
||||
}
|
||||
);
|
||||
this.dateConfigurationLabel.textContent = currentDate.toLocaleString(
|
||||
navigator.languages,
|
||||
{
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric'
|
||||
}
|
||||
);
|
||||
|
||||
// Add events to DOM
|
||||
this.simImportButton.addEventListener('click', this);
|
||||
this.sdImportButton.addEventListener('click', this);
|
||||
this.skipPinButton.addEventListener('click', this);
|
||||
this.backSimButton.addEventListener('click', this);
|
||||
this.unlockSimButton.addEventListener('click', this);
|
||||
this.unlockSimAction.addEventListener('action', this);
|
||||
this.simInfoBack.addEventListener('click', this);
|
||||
this.simInfoForward.addEventListener('click', this);
|
||||
|
||||
this.dataConnectionSwitch.addEventListener('change', this);
|
||||
|
||||
this.wifiRefreshButton.addEventListener('click', this);
|
||||
this.wifiJoinButton.addEventListener('click', this);
|
||||
this.networks.addEventListener('click', this);
|
||||
|
||||
this.joinHiddenButton.addEventListener('click', this);
|
||||
this.hiddenWifiSecurity.addEventListener('change', this);
|
||||
this.wifiJoinButton.disabled = true;
|
||||
|
||||
var checkHiddenWifiJoin = function() {
|
||||
this.wifiJoinButton.disabled = this.hiddenWifiSsid.value === '' ||
|
||||
!WifiHelper.isValidInput(this.hiddenWifiSecurity.value,
|
||||
this.hiddenWifiPassword.value,
|
||||
this.hiddenWifiIdentity.value
|
||||
);
|
||||
}.bind(this);
|
||||
|
||||
this.hiddenWifiSsid.addEventListener('keyup', checkHiddenWifiJoin);
|
||||
this.hiddenWifiIdentity.addEventListener('keyup', checkHiddenWifiJoin);
|
||||
this.hiddenWifiPassword.addEventListener('keyup', checkHiddenWifiJoin);
|
||||
this.hiddenWifiSecurity.addEventListener('change', checkHiddenWifiJoin);
|
||||
|
||||
this.hiddenWifiShowPassword.onchange = function togglePasswordVisibility() {
|
||||
UIManager.hiddenWifiPassword.type = this.checked ? 'text' : 'password';
|
||||
};
|
||||
|
||||
this.timeConfiguration.addEventListener('input', this);
|
||||
this.dateConfiguration.addEventListener('input', this);
|
||||
|
||||
this.geolocationSwitch.addEventListener('change', this);
|
||||
|
||||
this.fxaCreateAccount.addEventListener('click', this);
|
||||
|
||||
// Prevent form submit in case something tries to send it
|
||||
this.timeForm.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Input scroll workaround
|
||||
this.newsletterInput.addEventListener('focus', function() {
|
||||
window.addEventListener('resize', function resize() {
|
||||
window.removeEventListener('resize', resize);
|
||||
// Need to wait till resize is done
|
||||
setTimeout(function() {
|
||||
var page = document.getElementById('browser_privacy');
|
||||
UIManager.scrollToElement(page, UIManager.newsletterInput);
|
||||
}, 30);
|
||||
});
|
||||
});
|
||||
|
||||
this.offlineNewsletterErrorDialog
|
||||
.querySelector('button')
|
||||
.addEventListener('click',
|
||||
function offlineDialogClick() {
|
||||
this.offlineNewsletterErrorDialog.classList.remove('visible');
|
||||
}.bind(this));
|
||||
|
||||
this.invalidEmailErrorDialog
|
||||
.querySelector('button')
|
||||
.addEventListener('click',
|
||||
function invalidEmailDialogClick() {
|
||||
this.invalidEmailErrorDialog.classList.remove('visible');
|
||||
}.bind(this));
|
||||
|
||||
var skipTutorialAction = function() {
|
||||
// Stop Wifi Manager
|
||||
WifiManager.finish();
|
||||
// For tiny devices
|
||||
if (ScreenLayout.getCurrentLayout() === 'tiny') {
|
||||
window.close();
|
||||
} else {
|
||||
// for large devices
|
||||
FinishScreen.init();
|
||||
}
|
||||
};
|
||||
|
||||
var startTutorialAction = function(evt) {
|
||||
// Stop Wifi Manager
|
||||
WifiManager.finish();
|
||||
|
||||
// Play the tutorial steps as soon as config is done loading
|
||||
Tutorial.start(function onTutorialLoaded() {
|
||||
UIManager.activationScreen.classList.remove('show');
|
||||
UIManager.updateScreen.classList.remove('show');
|
||||
UIManager.finishScreen.classList.remove('show');
|
||||
});
|
||||
};
|
||||
|
||||
this.skipTutorialButton.addEventListener('click', skipTutorialAction);
|
||||
this.updateSkipTutorialButton.addEventListener('click', skipTutorialAction);
|
||||
|
||||
this.letsGoButton.addEventListener('click', startTutorialAction);
|
||||
this.updateLetsGoButton.addEventListener('click', startTutorialAction);
|
||||
|
||||
// Enable saving metrics level choice to settings.
|
||||
this.metricsBasic.addEventListener('change', this);
|
||||
this.metricsEnhanced.addEventListener('change', this);
|
||||
this.metricsNone.addEventListener('change', this);
|
||||
var button = this.offlineErrorDialog.querySelector('button');
|
||||
button.addEventListener('click',
|
||||
this.onOfflineDialogButtonClick.bind(this));
|
||||
|
||||
// Handle activation screen visibility.
|
||||
['confirmdialogshowing',
|
||||
'loadingoverlayshowing',
|
||||
'tutorialinitialized'].forEach(function(event) {
|
||||
window.addEventListener(event,
|
||||
this.hideActivationScreenFromScreenReader.bind(this));
|
||||
}, this);
|
||||
|
||||
['confirmdialoghiding',
|
||||
'loadingoverlayhiding'].forEach(function(event) {
|
||||
window.addEventListener(event,
|
||||
this.showActivationScreenToScreenReader.bind(this));
|
||||
}, this);
|
||||
|
||||
this.checkInitialFxAStatus();
|
||||
},
|
||||
|
||||
scrollToElement: function ui_scrollToElement(container, element) {
|
||||
container.scrollTop = element.offsetTop;
|
||||
},
|
||||
|
||||
sendNewsletter: function ui_sendNewsletter(callback) {
|
||||
var self = this;
|
||||
var emailValue = self.newsletterInput.value;
|
||||
if (emailValue === '') {
|
||||
return callback(true);
|
||||
} else {
|
||||
utils.overlay.show('email-loading', 'spinner');
|
||||
if (self.newsletterInput.checkValidity()) {
|
||||
if (window.navigator.onLine) {
|
||||
Basket.send(emailValue, function emailSent(err, data) {
|
||||
if (err) {
|
||||
if (err.code && err.code === Basket.errors.INVALID_EMAIL) {
|
||||
ConfirmDialog.show('invalid-email-dialog-title',
|
||||
'invalid-email-dialog-text',
|
||||
{
|
||||
title: 'cancel',
|
||||
callback: function ok() {
|
||||
ConfirmDialog.hide();
|
||||
}
|
||||
});
|
||||
utils.overlay.hide();
|
||||
return callback(false);
|
||||
} else {
|
||||
Basket.store(emailValue);
|
||||
}
|
||||
}
|
||||
utils.overlay.hide();
|
||||
return callback(true);
|
||||
});
|
||||
} else {
|
||||
Basket.store(emailValue, function stored(errorStoring) {
|
||||
utils.overlay.hide();
|
||||
return callback(!errorStoring);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
utils.overlay.hide();
|
||||
ConfirmDialog.show('invalid-email-dialog-title',
|
||||
'invalid-email-dialog-text',
|
||||
{
|
||||
title: 'cancel',
|
||||
callback: function ok() {
|
||||
ConfirmDialog.hide();
|
||||
}
|
||||
});
|
||||
return callback(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initTZ: function ui_initTZ() {
|
||||
// Initialize the timezone selector, see /shared/js/tz_select.js
|
||||
var self = this;
|
||||
var tzRegion = document.getElementById('tz-region');
|
||||
var tzCity = document.getElementById('tz-city');
|
||||
return new Promise(function(resolve) {
|
||||
var onchange = self.setTimeZone.bind(self);
|
||||
var onload = function() {
|
||||
self.setTimeZone.apply(self, arguments);
|
||||
resolve();
|
||||
};
|
||||
tzSelect(tzRegion, tzCity, onchange, onload);
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent: function ui_handleEvent(event) {
|
||||
switch (event.target.id) {
|
||||
// SIM
|
||||
case 'skip-pin-button':
|
||||
SimManager.skip();
|
||||
break;
|
||||
case 'back-sim-button':
|
||||
case 'sim-info-back':
|
||||
SimManager.back();
|
||||
break;
|
||||
case 'unlock-sim-button':
|
||||
SimManager.unlock();
|
||||
break;
|
||||
case 'unlock-sim-action':
|
||||
SimManager.simUnlockBack();
|
||||
break;
|
||||
case 'sim-info-forward':
|
||||
SimManager.finish();
|
||||
break;
|
||||
case 'sim-import-button':
|
||||
// Needed to give the browser the opportunity to properly refresh the UI
|
||||
// Particularly the button toggling cycle (from inactive to active)
|
||||
window.setTimeout(SimManager.importContacts, 0);
|
||||
break;
|
||||
case 'sd-import-button':
|
||||
// Needed to give the browser the opportunity to properly refresh the UI
|
||||
// Particularly the button toggling cycle (from inactive to active)
|
||||
window.setTimeout(SdManager.importContacts, 0);
|
||||
break;
|
||||
// 3G
|
||||
case 'data-connection-switch':
|
||||
this.dataConnectionChangedByUsr = true;
|
||||
var status = event.target.checked;
|
||||
DataMobile.toggle(status);
|
||||
break;
|
||||
// WIFI
|
||||
case 'wifi-refresh-button':
|
||||
WifiManager.scan(WifiUI.renderNetworks);
|
||||
break;
|
||||
case 'wifi-join-button':
|
||||
if (window.location.hash === '#hidden-wifi-authentication') {
|
||||
WifiUI.joinHiddenNetwork();
|
||||
} else {
|
||||
WifiUI.joinNetwork();
|
||||
}
|
||||
break;
|
||||
case 'join-hidden-button':
|
||||
WifiUI.addHiddenNetwork();
|
||||
break;
|
||||
case 'hidden-wifi-security':
|
||||
// Assuming that [0] is None, we prefer '' for collision on translations
|
||||
var securityType = event.target.selectedIndex ? event.target.value : '';
|
||||
WifiUI.handleHiddenWifiSecurity(securityType);
|
||||
break;
|
||||
// Date & Time
|
||||
case 'time-configuration':
|
||||
this.setTime();
|
||||
break;
|
||||
case 'date-configuration':
|
||||
this.setDate();
|
||||
break;
|
||||
// Geolocation
|
||||
case 'geolocation-switch':
|
||||
this.updateSetting(event.target.name, event.target.checked);
|
||||
break;
|
||||
// Privacy
|
||||
case 'metrics-enhanced':
|
||||
this.updateSetting('hud.hide', (event.target.value === 'Enhanced'));
|
||||
this.updateSetting('devtools.overlay',
|
||||
(event.target.value === 'Enhanced'));
|
||||
/* falls through */
|
||||
case 'metrics-basic':
|
||||
case 'metrics-none':
|
||||
this.updateSetting('metrics.selectedMetrics.level', event.target.value);
|
||||
break;
|
||||
case 'fxa-create-account':
|
||||
this.createFirefoxAccount();
|
||||
break;
|
||||
default:
|
||||
// wifi selection
|
||||
if (event.target.parentNode.id === 'networks-list') {
|
||||
WifiUI.chooseNetwork(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
updateSetting: function ui_updateSetting(name, value) {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
var cset = {}; cset[name] = value;
|
||||
return SettingsListener.getSettingsLock().set(cset);
|
||||
},
|
||||
|
||||
setForwardButtonLabel: function ui_setForwardButtonLabel(label) {
|
||||
var nextButton = document.getElementById('forward');
|
||||
nextButton.setAttribute('data-l10n-id', label);
|
||||
},
|
||||
|
||||
checkInitialFxAStatus: function ui_checkInitialFxAStatus() {
|
||||
// It is possible that we enter the FTU after the user aborted a FTU
|
||||
// session where she logged into her FxA. In that case, we show the
|
||||
// information of her account if possible. If there is any reason why we
|
||||
// can't get the account information, we try to log her out.
|
||||
// It is quite unlikely that logging out fails, but in that case, we simply
|
||||
// hide the FxA panel to avoid potential errors such as the one reported on
|
||||
// bug 1113551. In any case, the user should be able to manage her account
|
||||
// from the Settings app afterwards.
|
||||
this.skipFxA = true;
|
||||
FxAccountsIACHelper.getAccount((account) => {
|
||||
this.skipFxA = false;
|
||||
this.onFxALogin(account);
|
||||
}, () => {
|
||||
FxAccountsIACHelper.logout(() => {
|
||||
this.skipFxA = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
createFirefoxAccount: function ui_createFirefoxAccount() {
|
||||
FxAccountsIACHelper.openFlow(UIManager.onFxAFlowDone,
|
||||
UIManager.onFxAError);
|
||||
},
|
||||
|
||||
onFxAFlowDone: function ui_onFxAFlowDone() {
|
||||
FxAccountsIACHelper.getAccount((account) => {
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
UIManager.onFxALogin(account);
|
||||
UIManager.setForwardButtonLabel('navbar-next');
|
||||
}, UIManager.onFxAError);
|
||||
},
|
||||
|
||||
onFxALogin: function ui_onFxALogin(account) {
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
// Update the email
|
||||
UIManager.newsletterInput.value = account.email;
|
||||
// Update the string
|
||||
UIManager.fxaIntro.innerHTML = '';
|
||||
document.l10n.setAttributes(
|
||||
UIManager.fxaIntro,
|
||||
account.verified ? 'fxa-signed-in' : 'fxa-email-sent',
|
||||
{
|
||||
email: account.email
|
||||
}
|
||||
);
|
||||
// Disable the button
|
||||
UIManager.fxaCreateAccount.disabled = true;
|
||||
},
|
||||
|
||||
onFxAError: function ui_onFxAError(response) {
|
||||
console.error('Create FxA Error: ' + JSON.stringify(response));
|
||||
// Clean fields
|
||||
UIManager.newsletterInput.value = '';
|
||||
// Reset the field
|
||||
document.l10n.setAttributes(
|
||||
UIManager.fxaIntro,
|
||||
'fxa-upsell'
|
||||
);
|
||||
// Enable the button
|
||||
UIManager.fxaCreateAccount.disabled = false;
|
||||
// Change the forward button label
|
||||
UIManager.setForwardButtonLabel('skip');
|
||||
},
|
||||
|
||||
displayOfflineDialog: function ui_displayOfflineDialog(href, title) {
|
||||
var dialog = this.offlineErrorDialog;
|
||||
document.l10n.setAttributes(dialog.querySelector('small'),
|
||||
'offline-dialog-text', { url: href });
|
||||
dialog.classList.add('visible');
|
||||
this.hideActivationScreenFromScreenReader();
|
||||
},
|
||||
|
||||
onOfflineDialogButtonClick: function ui_onOfflineDialogButtonClick(e) {
|
||||
this.offlineErrorDialog.classList.remove('visible');
|
||||
this.showActivationScreenToScreenReader();
|
||||
},
|
||||
|
||||
hideActivationScreenFromScreenReader:
|
||||
function ui_hideActivationScreenFromScreenReader() {
|
||||
this.activationScreen.setAttribute('aria-hidden', true);
|
||||
},
|
||||
|
||||
showActivationScreenToScreenReader:
|
||||
function ui_showActivationScreenToScreenReader() {
|
||||
this.activationScreen.setAttribute('aria-hidden', false);
|
||||
},
|
||||
|
||||
setDate: function ui_sd() {
|
||||
if (!!this.lock) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Current time
|
||||
var now = new Date();
|
||||
// Format: 2012-09-01
|
||||
var currentDate = this.dateConfiguration.value;
|
||||
var currentTime = now.toLocaleFormat('%H:%M');
|
||||
var timeToSet = new Date(currentDate + 'T' + currentTime);
|
||||
TimeManager.set(timeToSet);
|
||||
this.dateConfigurationLabel.textContent = timeToSet.toLocaleString(
|
||||
navigator.languages,
|
||||
{
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric'
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
setTime: function ui_st() {
|
||||
if (!!this.lock) {
|
||||
return;
|
||||
}
|
||||
var timeLabel = document.getElementById('time-configuration-label');
|
||||
// Current time
|
||||
var now = new Date();
|
||||
// Format: 2012-09-01
|
||||
var currentTime = document.getElementById('time-configuration').value;
|
||||
if (currentTime.indexOf(':') === 1) { // Format: 8:05 --> 08:05
|
||||
currentTime = '0' + currentTime;
|
||||
}
|
||||
var currentDate = now.toLocaleFormat('%Y-%m-%d');
|
||||
var timeToSet = new Date(currentDate + 'T' + currentTime);
|
||||
// Set date through API
|
||||
TimeManager.set(timeToSet);
|
||||
// Set DATE properly
|
||||
timeLabel.textContent = timeToSet.toLocaleString(
|
||||
navigator.languages,
|
||||
{
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric'
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
setTimeZone: function ui_stz(timezone, needsConfirmation) {
|
||||
this.timeZoneNeedsConfirmation = !!needsConfirmation;
|
||||
|
||||
var utcOffset = timezone.utcOffset;
|
||||
document.getElementById('time_zone_overlay').className =
|
||||
'UTC' + utcOffset.replace(/[+:]/g, '');
|
||||
var timezoneTitle = document.getElementById('time-zone-title');
|
||||
document.l10n.setAttributes(timezoneTitle, 'timezoneTitle', {
|
||||
utcOffset: utcOffset,
|
||||
region: timezone.region,
|
||||
city: timezone.city
|
||||
});
|
||||
document.getElementById('tz-region-label').textContent = timezone.region;
|
||||
document.getElementById('tz-city-label').textContent = timezone.city;
|
||||
|
||||
var now = new Date();
|
||||
var timeLabel = document.getElementById('time-configuration-label');
|
||||
timeLabel.textContent = now.toLocaleString(
|
||||
navigator.languages,
|
||||
{
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric'
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
updateDataConnectionStatus: function ui_udcs(status) {
|
||||
this.dataConnectionSwitch.checked = status;
|
||||
},
|
||||
|
||||
changeStatusBarColor: function ui_csbc(color) {
|
||||
var themeMeta = document.head.querySelector('meta[name="theme-color"]');
|
||||
themeMeta.setAttribute('content', color);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function toCamelCase(str) {
|
||||
return str.replace(/\-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
16
gaia/apps/ftu/js/utils.js
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var rdashes = /-(.)/g;
|
||||
|
||||
var Utils = {
|
||||
camelCase: function ut_camelCase(str) {
|
||||
return str.replace(rdashes, function(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.Utils = Utils;
|
||||
|
||||
}(this));
|
||||
485
gaia/apps/ftu/js/wifi.js
Executable file
|
|
@ -0,0 +1,485 @@
|
|||
/* global utils, UIManager, WifiHelper, WifiUI:true */
|
||||
/* exported WifiManager, WifiUI */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const WIFI_STATUS = {
|
||||
connecting: 'connecting',
|
||||
associated: 'associated',
|
||||
connected: 'connected',
|
||||
connectingfailed: 'connectingfailed',
|
||||
disconnected: 'disconnected'
|
||||
};
|
||||
|
||||
var WifiManager = {
|
||||
STATUS: Object.freeze(WIFI_STATUS),
|
||||
init: function wn_init() {
|
||||
this.api = WifiHelper.getWifiManager();
|
||||
this.changeStatus();
|
||||
// Ensure that wifi is on.
|
||||
if (navigator.mozSettings) {
|
||||
var lock = window.navigator.mozSettings.createLock();
|
||||
this.enable(lock);
|
||||
this.enableDebugging(lock);
|
||||
}
|
||||
|
||||
this.gCurrentNetwork = this.api ? this.api.connection.network : null;
|
||||
|
||||
if (this.gCurrentNetwork !== null) {
|
||||
this.api.forget(this.gCurrentNetwork);
|
||||
this.gCurrentNetwork = null;
|
||||
}
|
||||
},
|
||||
|
||||
getNetworks: function wn_getNetworks(callback) {
|
||||
this.networks ? callback(this.networks) : this.scan(callback);
|
||||
},
|
||||
|
||||
scan: function wn_scan(callback) {
|
||||
if (this._scanning) {
|
||||
return;
|
||||
}
|
||||
this._scanning = true;
|
||||
utils.overlay.show('scanningNetworks', 'spinner');
|
||||
var SCAN_TIMEOUT = 10000;
|
||||
|
||||
var self = this;
|
||||
self.onScan = callback;
|
||||
|
||||
var req = this.api ? this.api.getNetworks() : null;
|
||||
if (!req) {
|
||||
// When no wifi API is available (ie. shimless B2G-desktop),
|
||||
// we will yield to the event loop before calling the callback
|
||||
// to prevent a race condition with the wifi overlay.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=957769#c5
|
||||
setTimeout(function() {
|
||||
self._scanning = false;
|
||||
callback();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var handleRequest = function handleRequest() {
|
||||
self._scanning = false;
|
||||
clearTimeout(self.scanTimeout);
|
||||
self.scanTimeout = null;
|
||||
self.onScan(self.networks);
|
||||
self.onScan = null;
|
||||
};
|
||||
|
||||
req.onsuccess = function onScanSuccess() {
|
||||
self.networks = req.result;
|
||||
handleRequest();
|
||||
};
|
||||
|
||||
req.onerror = function onScanError() {
|
||||
console.error('Error reading networks: ' + req.error.name);
|
||||
self.networks = [];
|
||||
handleRequest();
|
||||
};
|
||||
|
||||
// Timeout in case of scanning errors not thrown by the API
|
||||
// We can't block the user in the screen (bug 889623)
|
||||
if (!self.scanTimeout) {
|
||||
self.scanTimeout = setTimeout(function() {
|
||||
self._scanning = false;
|
||||
console.warn('Timeout while reading networks');
|
||||
self.onScan();
|
||||
}, SCAN_TIMEOUT);
|
||||
}
|
||||
},
|
||||
|
||||
enable: function wn_enable(lock) {
|
||||
lock.set({'wifi.enabled': true});
|
||||
},
|
||||
|
||||
enableDebugging: function wn_enableDebugging(lock) {
|
||||
// For bug 819947: turn on wifi debugging output to help track down a bug
|
||||
// in wifi. We turn on wifi output only while the FTU app is active.
|
||||
this._prevDebuggingValue = false;
|
||||
var req = lock.get('wifi.debugging.enabled');
|
||||
req.onsuccess = function wn_getDebuggingSuccess() {
|
||||
this._prevDebuggingValue = req.result['wifi.debugging.enabled'];
|
||||
};
|
||||
lock.set({ 'wifi.debugging.enabled': true });
|
||||
},
|
||||
|
||||
finish: function wn_finish() {
|
||||
if (!this._prevDebuggingValue && navigator.mozSettings) {
|
||||
var resetLock = window.navigator.mozSettings.createLock();
|
||||
resetLock.set({'wifi.debugging.enabled': false});
|
||||
}
|
||||
},
|
||||
|
||||
getNetwork: function wm_gn(ssid) {
|
||||
var list = this.networks;
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i].ssid === ssid) {
|
||||
return list[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
connect: function wn_connect(ssid, password, user) {
|
||||
var network = this.getNetwork(ssid);
|
||||
if (!network) {
|
||||
console.error('Network not found');
|
||||
return;
|
||||
}
|
||||
this.ssid = ssid;
|
||||
// TODO: Hardcoded for resolving bug 1019146, replace hardcoded eap
|
||||
// method with user selected eap method after bug 1036829.
|
||||
WifiHelper.setPassword(network, password, user, 'PEAP');
|
||||
this.gCurrentNetwork = network;
|
||||
this.api.associate(network);
|
||||
},
|
||||
|
||||
changeStatus: function wn_cs(callback) {
|
||||
/**
|
||||
* mozWifiManager status
|
||||
* see dom/wifi/nsIWifi.idl -- the 4 possible statuses are:
|
||||
* - connecting:
|
||||
* fires when we start the process of connecting to a network.
|
||||
* - associated:
|
||||
* fires when we have connected to an access point but do not yet
|
||||
* have an IP address.
|
||||
* - connected:
|
||||
* fires once we are fully connected to an access point.
|
||||
* - connectingfailed:
|
||||
* fires when we fail to connect to an access point.
|
||||
* - disconnected:
|
||||
* fires when we were connected to a network but have been
|
||||
* disconnected.
|
||||
*/
|
||||
var self = this;
|
||||
if (WifiManager.api) {
|
||||
WifiManager.api.onstatuschange = function(event) {
|
||||
if (event.status === WifiManager.STATUS.disconnected && self.onScan) {
|
||||
self.scan(self.onScan);
|
||||
} else {
|
||||
WifiUI.updateNetworkStatus(event.network.ssid, event.status);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var WifiUI = {
|
||||
joinNetwork: function wui_jn() {
|
||||
var password = document.getElementById('wifi_password').value;
|
||||
var user = document.getElementById('wifi_user').value;
|
||||
var ssid = document.getElementById('wifi_ssid').value;
|
||||
WifiUI.connect(ssid, password, user);
|
||||
window.history.back();
|
||||
},
|
||||
|
||||
joinHiddenNetwork: function wui_jhn() {
|
||||
var password = UIManager.hiddenWifiPassword.value;
|
||||
var user = UIManager.hiddenWifiIdentity.value;
|
||||
var ssid = UIManager.hiddenWifiSsid.value;
|
||||
var security = UIManager.hiddenWifiSecurity.value;
|
||||
var network;
|
||||
|
||||
if (ssid.length) {
|
||||
if (!Array.isArray(WifiManager.networks)) {
|
||||
WifiManager.networks = [];
|
||||
}
|
||||
network = new window.MozWifiNetwork({
|
||||
ssid: ssid,
|
||||
capabilities: [],
|
||||
security: [security],
|
||||
relSignalStrength: 0,
|
||||
hidden: true
|
||||
});
|
||||
WifiManager.networks.push(network);
|
||||
this.renderNetworks(WifiManager.networks);
|
||||
WifiUI.connect(ssid, password, user);
|
||||
}
|
||||
|
||||
// like in Settings: if we don't provide correct
|
||||
// network data it just get back to the wifi screen
|
||||
window.history.back();
|
||||
},
|
||||
|
||||
_selectNetworkItem: function(ssid, isSelected=true) {
|
||||
var elem = document.getElementById(ssid);
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
if (isSelected) {
|
||||
// We should update the state to 'connecting' in here to show the change
|
||||
// to the user, instead of waiting for the api (takes longer).
|
||||
// But currently
|
||||
// there's no feedback from the api when connecting to a hidden wifi fails
|
||||
// so we skip the visual changes
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=1107431#c25
|
||||
elem.setAttribute('data-wifi-selected', true);
|
||||
} else {
|
||||
var icon = elem.querySelector('aside');
|
||||
elem.removeAttribute('data-wifi-selected');
|
||||
elem.classList.remove('connected');
|
||||
icon.classList.remove('connecting');
|
||||
icon.classList.remove('connected');
|
||||
|
||||
var security = elem.dataset.security;
|
||||
var securityLevelDOM = elem.querySelector('p[data-security-level]');
|
||||
securityLevelDOM.setAttribute('data-l10n-id',
|
||||
security ? security : 'securityOpen');
|
||||
}
|
||||
},
|
||||
|
||||
connect: function wui_connect(ssid, password, user) {
|
||||
// First we check if there is a previous selected network
|
||||
// and we remove their status
|
||||
// And then end we update the selected network
|
||||
var newWifi = document.getElementById(ssid);
|
||||
Array.from(document.querySelectorAll('li[data-wifi-selected]'))
|
||||
.forEach((networkSelected) => {
|
||||
this._selectNetworkItem(networkSelected,
|
||||
networkSelected === newWifi);
|
||||
});
|
||||
|
||||
// Finally we try to connect to the network
|
||||
WifiManager.connect(ssid, password, user);
|
||||
},
|
||||
|
||||
chooseNetwork: function wui_cn(event) {
|
||||
// Retrieve SSID from dataset
|
||||
var ssid = event.target.dataset.ssid;
|
||||
var selectedNetwork = WifiManager.getNetwork(ssid);
|
||||
if (!selectedNetwork) {
|
||||
console.error('Network not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Do we need to type password?
|
||||
if (WifiHelper.isOpen(selectedNetwork)) {
|
||||
WifiUI.connect(ssid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove refresh option
|
||||
UIManager.activationScreen.classList.add('no-options');
|
||||
// Update title
|
||||
UIManager.mainTitle.textContent = ssid;
|
||||
|
||||
// Update network values
|
||||
var ssidHeader = document.getElementById('wifi_ssid');
|
||||
var userInput = document.getElementById('wifi_user');
|
||||
var passwordInput = document.getElementById('wifi_password');
|
||||
var showPassword = document.querySelector(
|
||||
'gaia-checkbox[name=show_password]');
|
||||
var joinButton = UIManager.wifiJoinButton;
|
||||
|
||||
joinButton.disabled = true;
|
||||
passwordInput.addEventListener('input', function validatePassword() {
|
||||
// disable the "Join" button if the password is on wrong format
|
||||
joinButton.disabled =
|
||||
!WifiHelper.isValidInput(WifiHelper.getKeyManagement(selectedNetwork),
|
||||
passwordInput.value, userInput.value);
|
||||
});
|
||||
|
||||
// Show / Hide password
|
||||
passwordInput.type = 'password';
|
||||
passwordInput.value = '';
|
||||
showPassword.checked = false;
|
||||
showPassword.addEventListener('change', e => {
|
||||
passwordInput.type = e.target.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
// Update form
|
||||
passwordInput.value = '';
|
||||
ssidHeader.value = ssid;
|
||||
|
||||
// Activate secondary menu
|
||||
UIManager.navBar.classList.add('secondary-menu');
|
||||
// Update changes in form
|
||||
if (WifiHelper.isEap(selectedNetwork)) {
|
||||
userInput.parentNode.classList.remove('hidden');
|
||||
} else {
|
||||
userInput.parentNode.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Change hash
|
||||
window.location.hash = '#configure_network';
|
||||
},
|
||||
|
||||
addHiddenNetwork: function wui_addHiddenNetwork() {
|
||||
// Remove refresh option
|
||||
UIManager.activationScreen.classList.add('no-options');
|
||||
// Update title
|
||||
UIManager.mainTitle.setAttribute('data-l10n-id', 'authentication');
|
||||
UIManager.navBar.classList.add('secondary-menu');
|
||||
// Clean input contents
|
||||
UIManager.hiddenWifiSsid.value = '';
|
||||
UIManager.hiddenWifiPassword.value = '';
|
||||
UIManager.hiddenWifiIdentity.value = '';
|
||||
// Reset join button state
|
||||
UIManager.wifiJoinButton.disabled = true;
|
||||
window.location.hash = '#hidden-wifi-authentication';
|
||||
},
|
||||
|
||||
handleHiddenWifiSecurity: function wui_handleSecurity(securityType) {
|
||||
// no need for password if network is open
|
||||
if (securityType === '') {
|
||||
UIManager.hiddenWifiPasswordBox.classList.add('hidden');
|
||||
} else {
|
||||
UIManager.hiddenWifiPasswordBox.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// need of username is security is WPA-EAP
|
||||
if (securityType.indexOf('EAP') !== -1) {
|
||||
UIManager.hiddenWifiIdentityBox.classList.remove('hidden');
|
||||
} else {
|
||||
UIManager.hiddenWifiIdentityBox.classList.add('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
renderNetworks: function wui_rn(networks) {
|
||||
var networksDOM = document.getElementById('networks');
|
||||
networksDOM.innerHTML = '';
|
||||
var networksList;
|
||||
|
||||
if (!networks || networks.length === 0) {
|
||||
var noResult = '<div id="no-result-container">' +
|
||||
' <div id="no-result-message">' +
|
||||
' <p data-l10n-id="noWifiFound3"></p>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
networksDOM.innerHTML = noResult;
|
||||
} else {
|
||||
networksList = document.createElement('ul');
|
||||
networksList.id = 'networks-list';
|
||||
networksList.setAttribute('role', 'listbox');
|
||||
networksList.setAttribute('data-l10n-id', 'networksList');
|
||||
var networksShown = [];
|
||||
networks.sort(function(a, b) {
|
||||
return b.relSignalStrength - a.relSignalStrength;
|
||||
});
|
||||
// Add detected networks
|
||||
for (var i = 0, max = networks.length; i < max; i++) {
|
||||
// Retrieve the network
|
||||
var network = networks[i];
|
||||
// Check if is shown
|
||||
if (networksShown.indexOf(network.ssid) === -1) {
|
||||
// Create dom elements
|
||||
var li = document.createElement('li');
|
||||
var icon = document.createElement('aside');
|
||||
var ssidp = document.createElement('p');
|
||||
ssidp.setAttribute('dir', 'auto');
|
||||
var small = document.createElement('p');
|
||||
|
||||
small.dataset.securityLevel = true;
|
||||
|
||||
// Set Icon
|
||||
icon.classList.add('pack-end');
|
||||
icon.classList.add('wifi-icon');
|
||||
var level = Math.min(Math.floor(network.relSignalStrength / 20), 4);
|
||||
icon.classList.add('level-' + level);
|
||||
document.l10n.setAttributes(icon, 'wifiLevel', {level: level});
|
||||
icon.setAttribute('role', 'presentation');
|
||||
// Set SSID
|
||||
ssidp.textContent = network.ssid;
|
||||
li.dataset.ssid = network.ssid;
|
||||
// Show authentication method
|
||||
var keys = WifiHelper.getSecurity(network);
|
||||
|
||||
li.dataset.security = keys;
|
||||
if (!WifiHelper.isOpen(network)) {
|
||||
small.textContent = keys.join(', ');
|
||||
icon.classList.add('secured');
|
||||
} else {
|
||||
small.setAttribute('data-l10n-id', 'securityOpen');
|
||||
}
|
||||
// Show connection status
|
||||
icon.classList.add('wifi-signal');
|
||||
var connectionStatus = WifiHelper.getNetworkStatus(network) ||
|
||||
WifiManager.STATUS.disconnected;
|
||||
li.dataset.status = connectionStatus;
|
||||
|
||||
if (connectionStatus === WifiManager.STATUS.connected) {
|
||||
small.setAttribute('data-l10n-id', 'shortStatus-connected');
|
||||
small.removeAttribute('aria-label');
|
||||
icon.classList.add('connected');
|
||||
li.classList.add('connected');
|
||||
li.dataset.wifiSelected = true;
|
||||
}
|
||||
|
||||
// Update list of shown networks
|
||||
networksShown.push(network.ssid);
|
||||
// Append the elements to li
|
||||
li.setAttribute('id', network.ssid);
|
||||
li.setAttribute('role', 'option');
|
||||
li.setAttribute('aria-live', true);
|
||||
li.setAttribute('aria-relevant', 'text');
|
||||
li.appendChild(ssidp);
|
||||
li.appendChild(small);
|
||||
li.appendChild(icon);
|
||||
// Append to DOM
|
||||
if (WifiHelper.isConnected(network)) {
|
||||
networksList.insertBefore(li, networksList.firstChild);
|
||||
} else {
|
||||
networksList.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
networksList.dataset.type = 'list';
|
||||
networksDOM.appendChild(networksList);
|
||||
}
|
||||
utils.overlay.hide();
|
||||
},
|
||||
|
||||
updateNetworkStatus: function wui_uns(ssid, status) {
|
||||
var element = document.getElementById(ssid);
|
||||
// Check if element exists and it's the selected network
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (status === WifiManager.STATUS.connecting && ssid !== WifiManager.ssid) {
|
||||
// bogus event, see bug 1179703/1216221
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the element
|
||||
element.dataset.status = status;
|
||||
if (status !== WifiManager.STATUS.disconnected) {
|
||||
element.querySelector('p[data-security-level]').setAttribute(
|
||||
'data-l10n-id', 'shortStatus-' + status);
|
||||
} else {
|
||||
var security = element.dataset.security || 'Open';
|
||||
|
||||
element.querySelector('p[data-security-level]').setAttribute(
|
||||
'data-l10n-id', 'security' + security);
|
||||
element.classList.remove('connected');
|
||||
}
|
||||
|
||||
// Animate icon if connecting, stop animation if
|
||||
// failed/connected/disconnected
|
||||
var icon = element.querySelector('aside');
|
||||
switch (status) {
|
||||
case WifiManager.STATUS.connecting:
|
||||
case WifiManager.STATUS.associated:
|
||||
icon.classList.add('connecting');
|
||||
break;
|
||||
case WifiManager.STATUS.connected:
|
||||
icon.classList.remove('connecting');
|
||||
var networksList = document.getElementById('networks-list');
|
||||
icon.classList.add('connected');
|
||||
element.classList.add('connected');
|
||||
networksList.removeChild(element);
|
||||
networksList.insertBefore(element, networksList.firstChild);
|
||||
break;
|
||||
default:
|
||||
icon.classList.remove('connecting');
|
||||
icon.classList.remove('connected');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.WifiManager = WifiManager;
|
||||
exports.WifiUI = WifiUI;
|
||||
|
||||
})(window);
|
||||
18
gaia/apps/ftu/jsdoc.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "ftu",
|
||||
"tags": {
|
||||
"dictionaries": ["jsdoc"]
|
||||
},
|
||||
"source": {
|
||||
"include": ["apps/ftu/js/**/*.js"]
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"plugins/summarize"
|
||||
],
|
||||
"opts": {
|
||||
"private": true,
|
||||
"recurse": true,
|
||||
"destination": "docs/ftu"
|
||||
}
|
||||
}
|
||||
342
gaia/apps/ftu/locales/ftu.en-US.properties
Executable file
|
|
@ -0,0 +1,342 @@
|
|||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Unlock Codes
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
pincode2=Enter PIN code
|
||||
type_pin=Type your PIN code
|
||||
pincodeLabel=SIM {{n}} PIN
|
||||
pinValidation=PIN must have between 4 and 8 characters.
|
||||
pinError=The PIN was incorrect.
|
||||
pinAttemptMsg2={[ plural(n) ]}
|
||||
pinAttemptMsg2[one]=You have one try left to enter the correct code before locking the SIM card.
|
||||
pinAttemptMsg2[two]=You have {{n}} tries left to enter the correct code before locking the SIM card.
|
||||
pinAttemptMsg2[few]=You have {{n}} tries left to enter the correct code before locking the SIM card.
|
||||
pinAttemptMsg2[many]=You have {{n}} tries left to enter the correct code before locking the SIM card.
|
||||
pinAttemptMsg2[other]=You have {{n}} tries left to enter the correct code before locking the SIM card.
|
||||
pinLastChanceMsg=This is your last chance to enter the correct PIN. Otherwise, you must enter the PUK code to use this SIM card.
|
||||
|
||||
pukcode=Enter PUK code
|
||||
pukcodeTitle=Enter SIM {{n}} PUK
|
||||
pukcodeLabel=SIM {{n}} PUK
|
||||
type_puk=PUK code
|
||||
pukValidation=PUK code must have exactly 8 characters.
|
||||
pukInfo=You must enter the Personal Unlocking Key (PUK) Code for the SIM card. Refer to your SIM card documentation or contact your carrier for more information.
|
||||
pukError=The PUK was incorrect.
|
||||
pukAttemptMsg2={[ plural(n) ]}
|
||||
pukAttemptMsg2[one]=You have one try left to enter the correct code before this SIM card will be permanently unusable. Refer to your SIM card documentation or contact your carrier for more information.
|
||||
pukAttemptMsg2[two]=You have {{n}} tries left to enter the correct code before this SIM card will be permanently unusable. Refer to your SIM card documentation or contact your carrier for more information.
|
||||
pukAttemptMsg2[few]=You have {{n}} tries left to enter the correct code before this SIM card will be permanently unusable. Refer to your SIM card documentation or contact your carrier for more information.
|
||||
pukAttemptMsg2[many]=You have {{n}} tries left to enter the correct code before this SIM card will be permanently unusable. Refer to your SIM card documentation or contact your carrier for more information.
|
||||
pukAttemptMsg2[other]=You have {{n}} tries left to enter the correct code before this SIM card will be permanently unusable. Refer to your SIM card documentation or contact your carrier for more information.
|
||||
pukLastChanceMsg=This is your last chance to enter the correct PUK code. Your SIM card will be permanently unusable if you enter in the wrong PUK code.
|
||||
create_newpin=Create new PIN (4 to 8 digits)
|
||||
confirm_newpin=Confirm new PIN
|
||||
newpinConfirmation=PINs don’t match
|
||||
|
||||
nckcode=Enter NCK code
|
||||
cckcode=Enter CCK code
|
||||
spckcode=Enter SPCK code
|
||||
nck1code=Enter NCK1 code
|
||||
nck2code=Enter NCK2 code
|
||||
hnckcode=Enter HNCK code
|
||||
rcckcode=Enter RCCK code
|
||||
rspckcode=Enter RSPCK code
|
||||
nckcodeTitle=Enter SIM {{n}} NCK
|
||||
cckcodeTitle=Enter SIM {{n}} CCK
|
||||
spckcodeTitle=Enter SIM {{n}} SPCK
|
||||
nck1codeTitle=Enter SIM {{n}} NCK1
|
||||
nck2codeTitle=Enter SIM {{n}} NCK2
|
||||
hnckcodeTitle=Enter SIM {{n}} HNCK
|
||||
rcckcodeTitle=Enter SIM {{n}} RCCK
|
||||
rspckcodeTitle=Enter SIM {{n}} RSPCK
|
||||
nckcodeLabel=SIM {{n}} NCK
|
||||
cckcodeLabel=SIM {{n}} CCK
|
||||
spckcodeLabel=SIM {{n}} SPCK
|
||||
nck1codeLabel=SIM {{n}} NCK1
|
||||
nck2codeLabel=SIM {{n}} NCK2
|
||||
hnckcodeLabel=SIM {{n}} HNCK
|
||||
rcckcodeLabel=SIM {{n}} RCCK
|
||||
rspckcodeLabel=SIM {{n}} RSPCK
|
||||
type_nck=Type your NCK code
|
||||
type_cck=Type your CCK code
|
||||
type_spck=Type your SPCK code
|
||||
type_nck1=Type your NCK1 code
|
||||
type_nck2=Type your NCK2 code
|
||||
type_hnck=Type your HNCK code
|
||||
type_rcck=Type your RCCK code
|
||||
type_rspck=Type your RSPCK code
|
||||
nckValidation=NCK must have between 8 and 16 characters.
|
||||
cckValidation=CCK must have between 8 and 16 characters.
|
||||
spckValidation=SPCK must have between 8 and 16 characters.
|
||||
nck1Validation=NCK1 must have between 8 and 16 characters.
|
||||
nck2Validation=NCK2 must have between 8 and 16 characters.
|
||||
hnckValidation=HNCK must have between 8 and 16 characters.
|
||||
rcckValidation=RCCK must have between 8 and 16 characters.
|
||||
rspckValidation=RSPCK must have between 8 and 16 characters.
|
||||
nckErrorMsg=The NCK code is incorrect.
|
||||
nckAttemptMsg2={[ plural(n) ]}
|
||||
nckAttemptMsg2[one]=Network depersonalization request failure. You have one try left to enter the correct code. Refer to your device documentation or contact your carrier for more information.
|
||||
nckAttemptMsg2[two]=Network depersonalization request failure. You have {{n}} tries left to enter the correct code. Refer to your device documentation or contact your carrier for more information.
|
||||
nckAttemptMsg2[few]=Network depersonalization request failure. You have {{n}} tries left to enter the correct code. Refer to your device documentation or contact your carrier for more information.
|
||||
nckAttemptMsg2[many]=Network depersonalization request failure. You have {{n}} tries left to enter the correct code. Refer to your device documentation or contact your carrier for more information.
|
||||
nckAttemptMsg2[other]=Network depersonalization request failure. You have {{n}} tries left to enter the correct code. Refer to your device documentation or contact your carrier for more information.
|
||||
nckLastChanceMsg=Network depersonalization request failure. Last chance to enter the correct NCK code. Refer to your device documentation or contact your carrier for more information.
|
||||
|
||||
skip=Skip
|
||||
send=Send
|
||||
ok=OK
|
||||
simLocked=The SIM card is locked
|
||||
inputCodeRetriesLeft ={[ plural(n) ]}
|
||||
inputCodeRetriesLeft[one] =Last try.
|
||||
inputCodeRetriesLeft[two] ={{n}} tries left.
|
||||
inputCodeRetriesLeft[few] ={{n}} tries left.
|
||||
inputCodeRetriesLeft[many] ={{n}} tries left.
|
||||
inputCodeRetriesLeft[other] ={{n}} tries left.
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# SIM Manager
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
simManager=SIM Manager
|
||||
simDetail=SIM {{n}}
|
||||
simDefault=(default)
|
||||
simDefaultMessage=Outgoing calls, outgoing messages and data connection are set to SIM 1 as default
|
||||
simSettingsMessage=You can change default settings at Settings > SIM manager
|
||||
simPinLocked=SIM PIN locked
|
||||
noOperator=No operator
|
||||
searchingOperator=Searching
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Language
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
language=Language
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# 3g
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
3g = Cellular Data
|
||||
dataConnection = Enable Data
|
||||
dataConnectionInfo = Enabling data connection now will allow you to set up your phone without a Wi-Fi connection. You may also enable it later on in Settings. Just make sure you have a data plan with your carrier to avoid potentially hefty charges.
|
||||
caution = Warning
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Geolocation
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
geolocation = Geolocation
|
||||
geolocationCheckbox = Enable Geolocation
|
||||
geolocationInfo2 = {{brandShortName}} uses data such as GPS, Wi-Fi, and mobile networks near you to help determine your approximate location.
|
||||
geolocationInfoMore = Data is sent to Mozilla and other service providers and will be used in the aggregate to improve those location databases. Data may be stored on your device and data collection may occur even when no apps are running.
|
||||
notice = Notice
|
||||
aboutYourPrivacy = More about your privacy
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Wifi
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
scanningNetworks = Scanning for networks
|
||||
selectNetwork = Select a network
|
||||
shortStatus-connecting = Connecting…
|
||||
shortStatus-associated = Obtaining an IP address…
|
||||
shortStatus-connected = Connected
|
||||
shortStatus-connectingfailed = Connection failed
|
||||
shortStatus-disconnected = Offline
|
||||
securedBy=secured by {{capabilities}}
|
||||
security = Security
|
||||
securityNone = None
|
||||
securityWEP = WEP
|
||||
securityWPA-PSK = WPA-PSK
|
||||
securityWPA-EAP = WPA-EAP
|
||||
securityOpen = Open
|
||||
securityOpen.ariaLabel=Security
|
||||
identity=User
|
||||
identityInput.placeholder=User
|
||||
password=Password
|
||||
passwordInput.placeholder=Password
|
||||
showPassword=Show Password
|
||||
refresh = Refresh
|
||||
noWifiFound3 = No wireless networks found
|
||||
join-hidden-button=Join hidden network
|
||||
authentication=Authentication
|
||||
ssid-name=SSID Network Name
|
||||
# The following strings are spoken by screen readers and not shown on the
|
||||
# screen.
|
||||
# LOCALIZATION NOTE(wifiLevel): {{level}} will be replaced at run-time by a
|
||||
# number ranging from 1 to 4.
|
||||
wifiLevel.ariaLabel={{level}} of 4 bars
|
||||
networksList.ariaLabel=Networks found
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Date & Time
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
dateAndTime = Date & Time
|
||||
# LOCALIZATION NOTE(timezoneTitle): Parameters in this label will be replaced
|
||||
# at runtime, use this string to reorder elements, translate "UTC", or use
|
||||
# different separator characters.
|
||||
# Example of a resulting string for English: "UTC+01:00 Europe/Paris"
|
||||
timezoneTitle = UTC{{utcOffset}} {{region}}/{{city}}
|
||||
timeZone = Time zone
|
||||
change = Change
|
||||
region.ariaLabel = Region
|
||||
city.ariaLabel = City
|
||||
date.ariaLabel = Date
|
||||
time.ariaLabel = Time
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Import contacts
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
importContacts3 = Import contacts
|
||||
importSim2 = SIM card
|
||||
importOutlook = Outlook
|
||||
importGmail = Gmail
|
||||
notImportedYet = Not imported yet
|
||||
otherAccount = Other accounts
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Fxa Intro
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
firefox-accounts = Firefox Accounts
|
||||
fxa-upsell = A Firefox Account lets you find a lost phone and buy apps from the Firefox Marketplace.
|
||||
fxa-create-account-or-sign-in = Create Account or Sign In
|
||||
fxa-email-sent = A verification link has been sent to {{email}}
|
||||
# Copy borrowed fxa-logged-in-text string in settings app
|
||||
fxa-signed-in = Signed in as {{email}}
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# About your rights
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
about-your-rights-0 = {{brandShortName}} is built on free and open source software by a community of thousands from all over the world. There are a few things you should know:
|
||||
about-your-rights-1 = {{brandShortName}} is made available to you under the terms of several open source licenses including the Mozilla Public License. A device running {{brandShortName}} may also contain proprietary software from third parties. Any code provided under open licenses give you the right to modify the source code and distribute your modified versions as long as you comply with their terms.
|
||||
about-your-rights-2 = You are not granted any trademark rights or licenses to the trademarks of the Mozilla Foundation or any party, including without limitation the {{brandShortName}} name or logo.
|
||||
about-your-rights-3 = Some features in {{brandShortName}}, such as the Crash Reporter, give you the option to provide feedback to Mozilla. By choosing to submit feedback, you give Mozilla permission to use the feedback to improve its products, to publish the feedback on its websites, and to distribute the feedback.
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# SIM mandatory
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
SIM_mandatory = SIM Card Warning
|
||||
SIM_mandatory_warm = Please, insert a SIM card and reboot your phone.
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Sharing Performance Data
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
telemetry-sends2 = Telemetry sends Mozilla data about performance, hardware, configurations, customizations and app usage in your {{brandShortName}} device. We use this to make our products better, faster and more responsive.
|
||||
learn-more-telemetry2 = Learn more at <a>{{url}}</a>
|
||||
learn-more-information2 = Learn how Mozilla handles your information at <a>{{url}}</a>
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# OS privacy
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
htmlWelcome2 = Welcome to <a>{{brandShortName}}</a>, the free and open source operating system from Mozilla. Our mission is to promote openness, innovation and opportunity by keeping the power of the Web in your hands.
|
||||
|
||||
helpImprove2 = You can help us improve our products and services by automatically sending analytics data. See our <a>Privacy Notice</a> to learn how we handle data from your device.
|
||||
helpImprove3 = Help us improve our products and services by automatically sending analytics data.
|
||||
|
||||
learn-more-privacy2 = Learn how Mozilla handles your information in our <a>privacy policy</a>.
|
||||
learn-more-privacy3 = Read our <a>Privacy Notice</a>
|
||||
aboutBrowser = About {{brandShortName}}
|
||||
aboutYourPrivacy2 = Your Privacy
|
||||
mozillaCommunity2 = When you use {{brandShortName}}, you become part of a global community helping to build a brighter future for the Web. If you’d like to know more about the Mozilla community, our other products and events near you, please enter your email address below.
|
||||
sharePerformanceAndUsageData2 = Yes! Send data.
|
||||
emailAddress.placeholder = Email address
|
||||
email-successfully-added=Email successfully added
|
||||
email-loading=Saving your email…
|
||||
privacy-b2g={{brandShortName}}
|
||||
privacy-b2g.title={{brandShortName}}
|
||||
privacy-marketplace=Marketplace
|
||||
# please don't translate the brand name
|
||||
privacy-marketplace.title={{privacy-marketplace}}
|
||||
basicMetricsRadio = Basic
|
||||
enhancedMetricsRadio = Enhanced
|
||||
noneMetricsRadio = None
|
||||
basicMetricsExplain = Send app usage and error reports.
|
||||
enhancedMetricsExplain = Send Basic report plus diagnostic information.
|
||||
noneMetricsExplain = Do not send any data.
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Navigation bar
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
navbar-back=Back
|
||||
navbar-next=Next
|
||||
navbar-join=Join
|
||||
done=Done
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Welcome OS
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
thatsIt = That’s it!
|
||||
startYourPhone = Start your phone tour!
|
||||
startYourTablet = Start your tablet tour!
|
||||
joinTutorial = Join us for a quick tutorial about your new phone.
|
||||
joinTutorialUpdated = Join us for a quick tutorial about the updates to your phone!
|
||||
enjoyYourPhone = You’re ready to enjoy your new phone.
|
||||
enjoyYourPhoneUpdated = You’re ready to enjoy your updated phone.
|
||||
optionsAvailable = You can change the language, date/time and Wi-Fi connection in settings.
|
||||
letsGo = Let’s go
|
||||
startTour = Start tour
|
||||
|
||||
thatsIt1Tablet-2 = You can always swipe from the bottom edge to enter home screen.
|
||||
thatsIt2Tablet-2 = Let’s do it!
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Late Customization
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
late-customization = Free Apps
|
||||
late-customization-downloading-message = {{carrierName}} is offering free apps to help get you started. They will download <em>at no charge</em> while you finish setting up your phone.
|
||||
late-customization-download-later = {{carrierName}} is offering free apps to help get you started. You can download them <em>at no charge</em>.
|
||||
late-customization-downloading-message-default = Your carrier is offering free apps to help get you started. They will download <em>at no charge</em> while you finish setting up your phone.
|
||||
late-customization-download-later-default = Your carrier is offering free apps to help get you started. You can download them <em>at no charge</em>.
|
||||
late-customization-download-later-marketplace = Find these apps and many more when you visit Marketplace!
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Tutorial
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
# LOCALIZATION NOTE(tutorial): The following string is spoken by screen readers
|
||||
# and not shown on the screen.
|
||||
tutorial = Tutorial
|
||||
|
||||
tutorial-step1-large-3 = Swipe down to access recent notifications, usage information and settings.
|
||||
tutorial-step2-large-2 = Swipe left or right to discover new apps on home screen.
|
||||
tutorial-step3-large-2 = Tap and hold on an icon to delete or move it.
|
||||
tutorial-step4-large-2 = Two fingers swipe up from the bottom edge to trigger card view.
|
||||
|
||||
tutorial-homescreen-tiny = Swipe up and down to browse your apps. Tap and hold an icon to delete, move, or edit it.
|
||||
tutorial-notifications-v2-tiny = Swipe down to access recent notifications, usage information and settings.
|
||||
tutorial-sheets-tiny = Drag from the left edge of your screen to return to recently used apps.
|
||||
tutorial-rocketbar-tiny = Tap on the search box anytime to start a search or go to a website.
|
||||
tutorial-bookmarks-migration-tiny = Your bookmarks have been moved to the homescreen. When editing, tap a bookmark to edit or rename it.
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Offline Error Dialog
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
offline-dialog-title=You must be connected to the Internet to view the page.
|
||||
offline-dialog-text=Please go back and connect to the Internet. You can also view the page later from Settings or by entering {{url}} in any browser.
|
||||
OK=OK
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Newsletter Error Dialogs
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
offline-newsletter-dialog-title=You must be connected to the Internet to subscribe to the newsletter.
|
||||
offline-newsletter-dialog-text=Please go back and connect to the Internet.
|
||||
invalid-email-dialog-title=Your email is not valid
|
||||
invalid-email-dialog-text=Please go back and correct your entry.
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Memory Card Contacts Import Error Dialog
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
retry=Retry
|
||||
|
||||
cancel = Cancel
|
||||
|
||||
# The following strings are spoken by screen readers and not shown on the
|
||||
# screen.
|
||||
progressbar = Step {{step}} of {{total}}
|
||||
progressbar.ariaLabel = First Time Experience
|
||||
76
gaia/apps/ftu/manifest.webapp
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"name": "FTU",
|
||||
"description": "First Time Experience",
|
||||
"type": "certified",
|
||||
"launch_path": "/index.html",
|
||||
"role": "system",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"locales": {
|
||||
"ar": {
|
||||
"name": "FTU",
|
||||
"description": "First Time Experience"
|
||||
},
|
||||
"en-US": {
|
||||
"name": "FTU",
|
||||
"description": "First Time Experience"
|
||||
},
|
||||
"fr": {
|
||||
"name": "Tutoriel de prise en main",
|
||||
"description": "Tutoriel de prise en main"
|
||||
},
|
||||
"zh-TW": {
|
||||
"name": "FTU",
|
||||
"description": "First Time Experience"
|
||||
}
|
||||
},
|
||||
"default_locale": "en-US",
|
||||
"permissions": {
|
||||
"themeable": {},
|
||||
"telephony": {},
|
||||
"contacts": {
|
||||
"access": "readwrite"
|
||||
},
|
||||
"mobileconnection": {},
|
||||
"settings": {
|
||||
"access": "readwrite"
|
||||
},
|
||||
"systemXHR": {},
|
||||
"wifi-manage": {},
|
||||
"time": {},
|
||||
"device-storage:sdcard": {
|
||||
"access": "readcreate"
|
||||
},
|
||||
"phonenumberservice": {},
|
||||
"webapps-manage": {},
|
||||
"moz-extremely-unstable-and-will-change-webcomponents": {}
|
||||
},
|
||||
"orientation": "default",
|
||||
"redirects": [
|
||||
{
|
||||
"from": "live",
|
||||
"to": "/shared/pages/import/redirects/redirect.html"
|
||||
},
|
||||
{
|
||||
"from": "gmail",
|
||||
"to": "/shared/pages/import/redirects/redirect.html"
|
||||
}
|
||||
],
|
||||
"datastores-owned": {
|
||||
"Basket_Newsletter": {
|
||||
"description": "Email address for sending newsletter"
|
||||
}
|
||||
},
|
||||
"datastores-access": {
|
||||
"Import_Status_Data": {
|
||||
"readonly": false,
|
||||
"description": "Import status data shared between FTU and Contacts"
|
||||
},
|
||||
"icons": {
|
||||
"readonly": true,
|
||||
"description": "Retrieve cached website icons."
|
||||
}
|
||||
}
|
||||
}
|
||||
10
gaia/apps/ftu/package.json
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"webide": {
|
||||
"prepackage": {
|
||||
"command": "make",
|
||||
"env": ["APP=ftu"],
|
||||
"cwd": "../.."
|
||||
},
|
||||
"packageDir": "../../build_stage/ftu/"
|
||||
}
|
||||
}
|
||||
BIN
gaia/apps/ftu/shared/resources/branding/vulpes-symbol.png
Normal file
|
After Width: | Height: | Size: 769 KiB |
21
gaia/apps/ftu/style/extensions.css
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#iframe_extensions {
|
||||
position: fixed;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
transition: transform .6s ease;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
#iframe_extensions.opening {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
#iframe_extensions.closing {
|
||||
transform: translateY(0);
|
||||
}
|
||||
BIN
gaia/apps/ftu/style/images/arrow_up.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
gaia/apps/ftu/style/images/bg_bubbles.png
Executable file
|
After Width: | Height: | Size: 23 KiB |
BIN
gaia/apps/ftu/style/images/bg_bubbles@1.5x.png
Executable file
|
After Width: | Height: | Size: 38 KiB |
BIN
gaia/apps/ftu/style/images/bg_bubbles@2.25x.png
Executable file
|
After Width: | Height: | Size: 55 KiB |
BIN
gaia/apps/ftu/style/images/bg_bubbles@2x.png
Executable file
|
After Width: | Height: | Size: 50 KiB |
BIN
gaia/apps/ftu/style/images/bg_nosim.png
Executable file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
gaia/apps/ftu/style/images/bg_nosim@1.5x.png
Executable file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
gaia/apps/ftu/style/images/bg_nosim@2.25x.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
gaia/apps/ftu/style/images/bg_nosim@2x.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
gaia/apps/ftu/style/images/fxa_logo.png
Executable file
|
After Width: | Height: | Size: 31 KiB |
BIN
gaia/apps/ftu/style/images/fxa_logo@1.5x.png
Executable file
|
After Width: | Height: | Size: 56 KiB |
BIN
gaia/apps/ftu/style/images/fxa_logo@2.25x.png
Executable file
|
After Width: | Height: | Size: 155 KiB |
BIN
gaia/apps/ftu/style/images/fxa_logo@2x.png
Executable file
|
After Width: | Height: | Size: 86 KiB |
BIN
gaia/apps/ftu/style/images/gmail_icon.png
Executable file
|
After Width: | Height: | Size: 291 B |
BIN
gaia/apps/ftu/style/images/gmail_icon@1.5x.png
Executable file
|
After Width: | Height: | Size: 471 B |
BIN
gaia/apps/ftu/style/images/gmail_icon@2.25x.png
Executable file
|
After Width: | Height: | Size: 718 B |
BIN
gaia/apps/ftu/style/images/gmail_icon@2x.png
Executable file
|
After Width: | Height: | Size: 431 B |
BIN
gaia/apps/ftu/style/images/icon_tour.png
Executable file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour@1.5x.png
Executable file
|
After Width: | Height: | Size: 11 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour@2.25x.png
Executable file
|
After Width: | Height: | Size: 16 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour@2x.png
Executable file
|
After Width: | Height: | Size: 14 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour_rtl.png
Executable file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour_rtl@1.5x.png
Executable file
|
After Width: | Height: | Size: 11 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour_rtl@2.25x.png
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
gaia/apps/ftu/style/images/icon_tour_rtl@2x.png
Executable file
|
After Width: | Height: | Size: 14 KiB |
BIN
gaia/apps/ftu/style/images/import_SIM@2.25x.png
Executable file
|
After Width: | Height: | Size: 349 B |
BIN
gaia/apps/ftu/style/images/import_fb.png
Executable file
|
After Width: | Height: | Size: 355 B |
BIN
gaia/apps/ftu/style/images/import_fb@1.5x.png
Executable file
|
After Width: | Height: | Size: 376 B |
BIN
gaia/apps/ftu/style/images/import_fb@2.25x.png
Executable file
|
After Width: | Height: | Size: 386 B |
BIN
gaia/apps/ftu/style/images/import_fb@2x.png
Executable file
|
After Width: | Height: | Size: 525 B |
BIN
gaia/apps/ftu/style/images/import_sd.png
Executable file
|
After Width: | Height: | Size: 228 B |
BIN
gaia/apps/ftu/style/images/import_sd@1.5x.png
Executable file
|
After Width: | Height: | Size: 290 B |
BIN
gaia/apps/ftu/style/images/import_sd@2.25x.png
Executable file
|
After Width: | Height: | Size: 499 B |
BIN
gaia/apps/ftu/style/images/import_sd@2x.png
Executable file
|
After Width: | Height: | Size: 497 B |
BIN
gaia/apps/ftu/style/images/import_sim.png
Executable file
|
After Width: | Height: | Size: 335 B |
BIN
gaia/apps/ftu/style/images/import_sim@1.5x.png
Executable file
|
After Width: | Height: | Size: 278 B |
BIN
gaia/apps/ftu/style/images/import_sim@2x.png
Executable file
|
After Width: | Height: | Size: 523 B |
1
gaia/apps/ftu/style/images/late_customization/app_install_failed.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 84 84" width="84" height="84"><path opacity=".85" fill="#E51E1E" d="M42 0c23.3 0 42 18.7 42 42S65.3 84 42 84 0 65.3 0 42 18.7 0 42 0z"/><path fill="#FFF" d="M60.7 40.1c-.9-9.3-9.3-16.8-18.7-16.8-10.3 0-18.7 8.4-18.7 18.7S31.7 60.7 42 60.7c3.7 0 7.5-.9 10.3-2.8l-2.8-3.7C46.7 56.1 44.8 57 42 57c-8.4 0-14.9-6.5-14.9-14.9s6.5-15 14.9-15c7.5 0 13.1 5.6 14 13.1h-6.5l7.5 7.5c.9.9 2.8.9 3.7 0l7.5-7.5h-7.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 464 B |
1
gaia/apps/ftu/style/images/late_customization/app_installing.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 84 84" width="84" height="84"><path opacity=".85" fill="#333" d="M42 0c23.3 0 42 18.8 42 42 0 23.3-18.8 42-42 42-23.1.1-42-18.7-42-42C0 18.8 18.9 0 42 0z"/><path fill="#FFF" d="M29.9 50c.4.9 1.3 1.5 2.4 1.5h19.3c1 0 1.9-.6 2.4-1.5H29.9zm22.3-17.8L51 33.4v.1L42.4 42c-.3.3-.7.3-1 0l-8.6-8.6v-.1l-1.2-1.2c-1.6.3-3.1.6-4.6.9.2.5.4 1 .9 1.5l11.3 11.3c1.5 1.5 3.8 1.5 5.3 0l11.3-11.3c.4-.4.7-.9.9-1.5-1.4-.3-2.9-.6-4.5-.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 480 B |
1
gaia/apps/ftu/style/images/late_customization/default_icon.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 84 84" width="84" height="84"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="42" x2="42" y2="84"><stop offset="0" stop-color="#E5EBEF"/><stop offset="1" stop-color="#EDF1F4"/></linearGradient><path fill="url(#a)" d="M42 0c23.3 0 42 18.8 42 42 0 23.3-18.8 42-42 42-23.1.1-42-18.7-42-42C0 18.8 18.9 0 42 0z"/><path fill="#9AB0C0" d="M41.5 25.9c-.9 0-2.5 2.6-3.5 5.7 1.1-.2 2.3-.3 3.6-.3 1.2 0 2.4.1 3.4.3-1-3.1-2.6-5.7-3.5-5.7M43 53.3h2.2c.3.9.4 2 .4 3.1 0 4-3.1 7.2-4.1 7.2s-4.1-3.2-4.1-7.2c0-1.1.1-2.2.4-3.1H40c-.2.5-.3 1.2-.3 1.9 0 1.8 1.4 3.3 1.9 3.3s1.9-1.5 1.9-3.3c-.2-.7-.3-1.4-.5-1.9M26.1 40l5.9-5.9c1.7-8.6 6.9-17.2 9.5-17.2s7.8 8.7 9.5 17.2l5.9 5.9v15.4h-4.1l-5.7-5.7c-.6.6-1.2 1.1-1.9 1.5h-7.3c-.7-.4-1.3-.9-1.9-1.5l-5.7 5.7h-4.1L26.1 40"/></svg>
|
||||
|
After Width: | Height: | Size: 820 B |
BIN
gaia/apps/ftu/style/images/outlook_icon.png
Executable file
|
After Width: | Height: | Size: 306 B |
BIN
gaia/apps/ftu/style/images/outlook_icon@1.5x.png
Executable file
|
After Width: | Height: | Size: 585 B |
BIN
gaia/apps/ftu/style/images/outlook_icon@2.25x.png
Executable file
|
After Width: | Height: | Size: 846 B |
BIN
gaia/apps/ftu/style/images/outlook_icon@2x.png
Executable file
|
After Width: | Height: | Size: 482 B |
BIN
gaia/apps/ftu/style/images/sim01.png
Executable file
|
After Width: | Height: | Size: 312 B |
BIN
gaia/apps/ftu/style/images/sim01@1.5x.png
Executable file
|
After Width: | Height: | Size: 326 B |
BIN
gaia/apps/ftu/style/images/sim01@2.25x.png
Executable file
|
After Width: | Height: | Size: 464 B |
BIN
gaia/apps/ftu/style/images/sim01@2x.png
Executable file
|
After Width: | Height: | Size: 408 B |
BIN
gaia/apps/ftu/style/images/sim02.png
Executable file
|
After Width: | Height: | Size: 345 B |
BIN
gaia/apps/ftu/style/images/sim02@1.5x.png
Executable file
|
After Width: | Height: | Size: 390 B |
BIN
gaia/apps/ftu/style/images/sim02@2.25x.png
Executable file
|
After Width: | Height: | Size: 572 B |
BIN
gaia/apps/ftu/style/images/sim02@2x.png
Executable file
|
After Width: | Height: | Size: 489 B |
BIN
gaia/apps/ftu/style/images/simlock01.png
Executable file
|
After Width: | Height: | Size: 376 B |
BIN
gaia/apps/ftu/style/images/simlock01@1.5x.png
Executable file
|
After Width: | Height: | Size: 392 B |
BIN
gaia/apps/ftu/style/images/simlock01@2.25x.png
Executable file
|
After Width: | Height: | Size: 542 B |
BIN
gaia/apps/ftu/style/images/simlock01@2x.png
Executable file
|
After Width: | Height: | Size: 496 B |
BIN
gaia/apps/ftu/style/images/simlock02.png
Executable file
|
After Width: | Height: | Size: 413 B |
BIN
gaia/apps/ftu/style/images/simlock02@1.5x.png
Executable file
|
After Width: | Height: | Size: 456 B |
BIN
gaia/apps/ftu/style/images/simlock02@2.25x.png
Executable file
|
After Width: | Height: | Size: 638 B |
BIN
gaia/apps/ftu/style/images/simlock02@2x.png
Executable file
|
After Width: | Height: | Size: 575 B |
BIN
gaia/apps/ftu/style/images/start_for_large.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
gaia/apps/ftu/style/images/start_for_large@2.25x.png
Executable file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
gaia/apps/ftu/style/images/thumb_up.png
Executable file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
gaia/apps/ftu/style/images/thumb_up2.png
Executable file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
gaia/apps/ftu/style/images/thumb_up@1.5x.png
Executable file
|
After Width: | Height: | Size: 12 KiB |
BIN
gaia/apps/ftu/style/images/thumb_up@2.25x.png
Executable file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
gaia/apps/ftu/style/images/thumb_up@2x.png
Executable file
|
After Width: | Height: | Size: 14 KiB |
BIN
gaia/apps/ftu/style/images/timezone/+0000.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
gaia/apps/ftu/style/images/timezone/+0000@1.5x.png
Executable file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
gaia/apps/ftu/style/images/timezone/+0000@2.25x.png
Executable file
|
After Width: | Height: | Size: 7 KiB |
BIN
gaia/apps/ftu/style/images/timezone/+0000@2x.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
gaia/apps/ftu/style/images/timezone/+0100.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
gaia/apps/ftu/style/images/timezone/+0100@1.5x.png
Executable file
|
After Width: | Height: | Size: 3.6 KiB |