1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 cr.define('mobile', function() {
8 function MobileSetup() {
11 cr.addSingletonGetter(MobileSetup);
13 MobileSetup.PLAN_ACTIVATION_UNKNOWN = -2;
14 MobileSetup.PLAN_ACTIVATION_PAGE_LOADING = -1;
15 MobileSetup.PLAN_ACTIVATION_START = 0;
16 MobileSetup.PLAN_ACTIVATION_TRYING_OTASP = 1;
17 MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION = 3;
18 MobileSetup.PLAN_ACTIVATION_RECONNECTING = 4;
19 MobileSetup.PLAN_ACTIVATION_WAITING_FOR_CONNECTION = 5;
20 MobileSetup.PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING = 6;
21 MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT = 7;
22 MobileSetup.PLAN_ACTIVATION_RECONNECTING_PAYMENT = 8;
23 MobileSetup.PLAN_ACTIVATION_DELAY_OTASP = 9;
24 MobileSetup.PLAN_ACTIVATION_START_OTASP = 10;
25 MobileSetup.PLAN_ACTIVATION_OTASP = 11;
26 MobileSetup.PLAN_ACTIVATION_DONE = 12;
27 MobileSetup.PLAN_ACTIVATION_ERROR = 0xFF;
29 MobileSetup.EXTENSION_PAGE_URL =
30 'chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab';
31 MobileSetup.ACTIVATION_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
33 MobileSetup.PORTAL_OFFLINE_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
34 '/portal_offline.html';
35 MobileSetup.REDIRECT_POST_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
38 MobileSetup.localStrings_ = new LocalStrings();
40 MobileSetup.prototype = {
41 // Mobile device information.
45 fakedTransaction_: false,
48 frameLoadIgnored_: true,
49 carrierPageUrl_: null,
52 state_: MobileSetup.PLAN_ACTIVATION_UNKNOWN,
53 STATE_UNKNOWN_: 'unknown',
54 STATE_CONNECTING_: 'connecting',
55 STATE_ERROR_: 'error',
56 STATE_PAYMENT_: 'payment',
57 STATE_ACTIVATING_: 'activating',
58 STATE_CONNECTED_: 'connected',
60 initialize: function(frame_name, carrierPage) {
61 if (this.initialized_) {
62 console.log('calling initialize() again?');
65 this.initialized_ = true;
67 this.frameName_ = frame_name;
69 cr.ui.dialogs.BaseDialog.OK_LABEL =
70 MobileSetup.localStrings_.getString('ok_button');
71 cr.ui.dialogs.BaseDialog.CANCEL_LABEL =
72 MobileSetup.localStrings_.getString('cancel_button');
73 this.confirm_ = new cr.ui.dialogs.ConfirmDialog(document.body);
75 window.addEventListener('message', function(e) {
76 self.onMessageReceived_(e);
79 $('closeButton').addEventListener('click', function(e) {
80 $('finalStatus').classList.add('hidden');
83 // Kick off activation process.
84 chrome.send('startActivation');
87 startSpinner_: function() {
89 this.spinnerInt_ = setInterval(mobile.MobileSetup.drawProgress, 100);
92 stopSpinner_: function() {
93 if (this.spinnerInt_ != -1) {
94 clearInterval(this.spinnerInt_);
95 this.spinnerInt_ = -1;
99 onFrameLoaded_: function(success) {
100 chrome.send('paymentPortalLoad', [success ? 'ok' : 'failed']);
103 loadPaymentFrame_: function(deviceInfo) {
105 this.frameLoadError_ = 0;
106 this.deviceInfo_ = deviceInfo;
107 if (deviceInfo.post_data && deviceInfo.post_data.length) {
108 this.frameLoadIgnored_ = true;
109 $(this.frameName_).contentWindow.location.href =
110 MobileSetup.REDIRECT_POST_PAGE_URL +
111 '?post_data=' + escape(deviceInfo.post_data) +
112 '&formUrl=' + escape(deviceInfo.payment_url);
114 this.frameLoadIgnored_ = false;
115 $(this.frameName_).contentWindow.location.href =
116 deviceInfo.payment_url;
121 onMessageReceived_: function(e) {
123 this.deviceInfo_.payment_url.substring(0, e.origin.length) &&
124 e.origin != MobileSetup.EXTENSION_PAGE_URL)
127 if (e.data.type == 'requestDeviceInfoMsg') {
128 this.sendDeviceInfo_();
129 } else if (e.data.type == 'framePostReady') {
130 this.frameLoadIgnored_ = false;
131 this.sendPostFrame_(e.origin);
132 } else if (e.data.type == 'reportTransactionStatusMsg') {
133 console.log('calling setTransactionStatus from onMessageReceived_');
134 chrome.send('setTransactionStatus', [e.data.status]);
138 changeState_: function(deviceInfo) {
139 var newState = deviceInfo.state;
140 if (this.state_ == newState)
143 // The mobile setup is already in its final state.
144 if (this.state_ == MobileSetup.PLAN_ACTIVATION_DONE ||
145 this.state_ == MobileSetup.PLAN_ACTIVATION_ERROR) {
149 // Map handler state to UX.
150 var simpleActivationFlow =
151 (deviceInfo.activation_type == 'NonCellular' ||
152 deviceInfo.activation_type == 'OTA');
154 case MobileSetup.PLAN_ACTIVATION_PAGE_LOADING:
155 case MobileSetup.PLAN_ACTIVATION_START:
156 case MobileSetup.PLAN_ACTIVATION_DELAY_OTASP:
157 case MobileSetup.PLAN_ACTIVATION_START_OTASP:
158 case MobileSetup.PLAN_ACTIVATION_RECONNECTING:
159 case MobileSetup.PLAN_ACTIVATION_RECONNECTING_PAYMENT:
160 // Activation page should not be shown for the simple activation flow.
161 if (simpleActivationFlow)
164 $('statusHeader').textContent =
165 MobileSetup.localStrings_.getString('connecting_header');
166 $('auxHeader').textContent =
167 MobileSetup.localStrings_.getString('please_wait');
168 $('paymentForm').classList.add('hidden');
169 $('finalStatus').classList.add('hidden');
170 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL);
171 $('systemStatus').classList.remove('hidden');
172 $('canvas').classList.remove('hidden');
173 this.startSpinner_();
175 case MobileSetup.PLAN_ACTIVATION_TRYING_OTASP:
176 case MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION:
177 case MobileSetup.PLAN_ACTIVATION_OTASP:
178 // Activation page should not be shown for the simple activation flow.
179 if (simpleActivationFlow)
182 $('statusHeader').textContent =
183 MobileSetup.localStrings_.getString('activating_header');
184 $('auxHeader').textContent =
185 MobileSetup.localStrings_.getString('please_wait');
186 $('paymentForm').classList.add('hidden');
187 $('finalStatus').classList.add('hidden');
188 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL);
189 $('systemStatus').classList.remove('hidden');
190 $('canvas').classList.remove('hidden');
191 this.startSpinner_();
193 case MobileSetup.PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
194 // Activation page should not be shown for the simple activation flow.
195 if (!simpleActivationFlow) {
196 $('statusHeader').textContent =
197 MobileSetup.localStrings_.getString('connecting_header');
198 $('auxHeader').textContent = '';
199 $('paymentForm').classList.add('hidden');
200 $('finalStatus').classList.add('hidden');
201 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL);
202 $('systemStatus').classList.remove('hidden');
203 $('canvas').classList.remove('hidden');
205 this.loadPaymentFrame_(deviceInfo);
207 case MobileSetup.PLAN_ACTIVATION_WAITING_FOR_CONNECTION:
208 var statusHeaderText;
210 if (deviceInfo.activation_type == 'NonCellular') {
211 statusHeaderText = MobileSetup.localStrings_.getString(
212 'portal_unreachable_header');
213 carrierPage = MobileSetup.PORTAL_OFFLINE_PAGE_URL;
214 } else if (deviceInfo.activation_type == 'OTA') {
216 MobileSetup.localStrings_.getString('connecting_header');
217 carrierPage = MobileSetup.ACTIVATION_PAGE_URL;
219 $('statusHeader').textContent = statusHeaderText;
220 $('auxHeader').textContent = '';
221 $('auxHeader').classList.add('hidden');
222 $('paymentForm').classList.add('hidden');
223 $('finalStatus').classList.add('hidden');
224 $('systemStatus').classList.remove('hidden');
225 this.setCarrierPage_(carrierPage);
226 $('canvas').classList.remove('hidden');
227 this.startSpinner_();
229 case MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT:
230 $('statusHeader').textContent = '';
231 $('auxHeader').textContent = '';
232 $('finalStatus').classList.add('hidden');
233 $('systemStatus').classList.add('hidden');
234 $('paymentForm').classList.remove('hidden');
235 $('canvas').classList.add('hidden');
237 this.paymentShown_ = true;
239 case MobileSetup.PLAN_ACTIVATION_DONE:
240 $('statusHeader').textContent = '';
241 $('auxHeader').textContent = '';
242 $('finalHeader').textContent =
243 MobileSetup.localStrings_.getString('completed_header');
244 $('finalMessage').textContent =
245 MobileSetup.localStrings_.getString('completed_text');
246 $('systemStatus').classList.add('hidden');
247 $('closeButton').classList.remove('hidden');
248 $('finalStatus').classList.remove('hidden');
249 $('canvas').classList.add('hidden');
250 $('closeButton').classList.toggle('hidden', !this.paymentShown_);
251 $('paymentForm').classList.toggle('hidden', !this.paymentShown_);
254 case MobileSetup.PLAN_ACTIVATION_ERROR:
255 $('statusHeader').textContent = '';
256 $('auxHeader').textContent = '';
257 $('finalHeader').textContent =
258 MobileSetup.localStrings_.getString('error_header');
259 $('finalMessage').textContent = deviceInfo.error;
260 $('systemStatus').classList.add('hidden');
261 $('canvas').classList.add('hidden');
262 $('closeButton').classList.toggle('hidden', !this.paymentShown_);
263 $('paymentForm').classList.toggle('hidden', !this.paymentShown_);
264 $('finalStatus').classList.remove('hidden');
268 this.state_ = newState;
271 setCarrierPage_: function(url) {
272 if (this.carrierPageUrl_ == url)
274 this.carrierPageUrl_ = url;
275 $('carrierPage').contentWindow.location.href = url;
278 updateDeviceStatus_: function(deviceInfo) {
279 this.changeState_(deviceInfo);
282 portalFrameLoadError_: function(errorCode) {
283 if (this.frameLoadIgnored_)
285 console.log('Portal frame load error detected: ', errorCode);
286 this.frameLoadError_ = errorCode;
289 portalFrameLoadCompleted_: function() {
290 if (this.frameLoadIgnored_)
292 console.log('Portal frame load completed!');
293 this.onFrameLoaded_(this.frameLoadError_ == 0);
296 sendPostFrame_: function(frameUrl) {
297 var msg = { type: 'postFrame' };
298 $(this.frameName_).contentWindow.postMessage(msg, frameUrl);
301 sendDeviceInfo_: function() {
303 type: 'deviceInfoMsg',
304 domain: document.location,
306 'carrier': this.deviceInfo_.carrier,
307 'MEID': this.deviceInfo_.MEID,
308 'IMEI': this.deviceInfo_.IMEI,
309 'MDN': this.deviceInfo_.MDN
312 $(this.frameName_).contentWindow.postMessage(msg,
313 this.deviceInfo_.payment_url);
318 MobileSetup.drawProgress = function() {
319 var ctx = canvas.getContext('2d');
320 ctx.clearRect(0, 0, canvas.width, canvas.height);
322 var segmentCount = Math.min(12, canvas.width / 1.6); // Number of segments
323 var rotation = 0.75; // Counterclockwise rotation
325 // Rotate canvas over time
326 ctx.translate(canvas.width / 2, canvas.height / 2);
327 ctx.rotate(Math.PI * 2 / (segmentCount + rotation));
328 ctx.translate(-canvas.width / 2, -canvas.height / 2);
330 var gap = canvas.width / 24; // Gap between segments
331 var oRadius = canvas.width / 2; // Outer radius
332 var iRadius = oRadius * 0.618; // Inner radius
333 var oCircumference = Math.PI * 2 * oRadius; // Outer circumference
334 var iCircumference = Math.PI * 2 * iRadius; // Inner circumference
335 var oGap = gap / oCircumference; // Gap size as fraction of outer ring
336 var iGap = gap / iCircumference; // Gap size as fraction of inner ring
337 var oArc = Math.PI * 2 * (1 / segmentCount - oGap); // Angle of outer arcs
338 var iArc = Math.PI * 2 * (1 / segmentCount - iGap); // Angle of inner arcs
340 for (i = 0; i < segmentCount; i++) { // Draw each segment
341 var opacity = Math.pow(1.0 - i / segmentCount, 3.0);
342 opacity = (0.15 + opacity * 0.8); // Vary from 0.15 to 0.95
343 var angle = - Math.PI * 2 * i / segmentCount;
346 ctx.arc(canvas.width / 2, canvas.height / 2, oRadius,
347 angle - oArc / 2, angle + oArc / 2, false);
348 ctx.arc(canvas.width / 2, canvas.height / 2, iRadius,
349 angle + iArc / 2, angle - iArc / 2, true);
351 ctx.fillStyle = 'rgba(240, 30, 29, ' + opacity + ')';
356 MobileSetup.deviceStateChanged = function(deviceInfo) {
357 MobileSetup.getInstance().updateDeviceStatus_(deviceInfo);
360 MobileSetup.portalFrameLoadError = function(errorCode) {
361 MobileSetup.getInstance().portalFrameLoadError_(errorCode);
364 MobileSetup.portalFrameLoadCompleted = function() {
365 MobileSetup.getInstance().portalFrameLoadCompleted_();
368 MobileSetup.loadPage = function() {
369 mobile.MobileSetup.getInstance().initialize('paymentForm',
370 mobile.MobileSetup.ACTIVATION_PAGE_URL);
375 MobileSetup: MobileSetup
379 document.addEventListener('DOMContentLoaded', mobile.MobileSetup.loadPage);