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. 4 5 6cr.define('mobile', function() { 7 8 function MobileSetup() { 9 } 10 11 cr.addSingletonGetter(MobileSetup); 12 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; 28 29 MobileSetup.EXTENSION_PAGE_URL = 30 'chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab'; 31 MobileSetup.ACTIVATION_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL + 32 '/activation.html'; 33 MobileSetup.PORTAL_OFFLINE_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL + 34 '/portal_offline.html'; 35 MobileSetup.REDIRECT_POST_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL + 36 '/redirect.html'; 37 38 MobileSetup.localStrings_ = new LocalStrings(); 39 40 MobileSetup.prototype = { 41 // Mobile device information. 42 deviceInfo_: null, 43 frameName_: '', 44 initialized_: false, 45 fakedTransaction_: false, 46 paymentShown_: false, 47 frameLoadError_: 0, 48 frameLoadIgnored_: true, 49 carrierPageUrl_: null, 50 spinnerInt_: -1, 51 // UI states. 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', 59 60 initialize: function(frame_name, carrierPage) { 61 if (this.initialized_) { 62 console.log('calling initialize() again?'); 63 return; 64 } 65 this.initialized_ = true; 66 self = this; 67 this.frameName_ = frame_name; 68 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); 74 75 window.addEventListener('message', function(e) { 76 self.onMessageReceived_(e); 77 }); 78 79 $('closeButton').addEventListener('click', function(e) { 80 $('finalStatus').classList.add('hidden'); 81 }); 82 83 // Kick off activation process. 84 chrome.send('startActivation'); 85 }, 86 87 startSpinner_: function() { 88 this.stopSpinner_(); 89 this.spinnerInt_ = setInterval(mobile.MobileSetup.drawProgress, 100); 90 }, 91 92 stopSpinner_: function() { 93 if (this.spinnerInt_ != -1) { 94 clearInterval(this.spinnerInt_); 95 this.spinnerInt_ = -1; 96 } 97 }, 98 99 onFrameLoaded_: function(success) { 100 chrome.send('paymentPortalLoad', [success ? 'ok' : 'failed']); 101 }, 102 103 loadPaymentFrame_: function(deviceInfo) { 104 if (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); 113 } else { 114 this.frameLoadIgnored_ = false; 115 $(this.frameName_).contentWindow.location.href = 116 deviceInfo.payment_url; 117 } 118 } 119 }, 120 121 onMessageReceived_: function(e) { 122 if (e.origin != 123 this.deviceInfo_.payment_url.substring(0, e.origin.length) && 124 e.origin != MobileSetup.EXTENSION_PAGE_URL) 125 return; 126 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]); 135 } 136 }, 137 138 changeState_: function(deviceInfo) { 139 var newState = deviceInfo.state; 140 if (this.state_ == newState) 141 return; 142 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) { 146 return; 147 } 148 149 // Map handler state to UX. 150 switch (newState) { 151 case MobileSetup.PLAN_ACTIVATION_PAGE_LOADING: 152 case MobileSetup.PLAN_ACTIVATION_START: 153 case MobileSetup.PLAN_ACTIVATION_DELAY_OTASP: 154 case MobileSetup.PLAN_ACTIVATION_START_OTASP: 155 case MobileSetup.PLAN_ACTIVATION_RECONNECTING: 156 case MobileSetup.PLAN_ACTIVATION_RECONNECTING_PAYMENT: 157 // Activation page should not be shown for devices that are activated 158 // over non cellular network. 159 if (deviceInfo.activate_over_non_cellular_network) 160 break; 161 162 $('statusHeader').textContent = 163 MobileSetup.localStrings_.getString('connecting_header'); 164 $('auxHeader').textContent = 165 MobileSetup.localStrings_.getString('please_wait'); 166 $('paymentForm').classList.add('hidden'); 167 $('finalStatus').classList.add('hidden'); 168 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL); 169 $('systemStatus').classList.remove('hidden'); 170 $('canvas').classList.remove('hidden'); 171 this.startSpinner_(); 172 break; 173 case MobileSetup.PLAN_ACTIVATION_TRYING_OTASP: 174 case MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION: 175 case MobileSetup.PLAN_ACTIVATION_OTASP: 176 // Activation page should not be shown for devices that are activated 177 // over non cellular network. 178 if (deviceInfo.activate_over_non_cellular_network) 179 break; 180 181 $('statusHeader').textContent = 182 MobileSetup.localStrings_.getString('activating_header'); 183 $('auxHeader').textContent = 184 MobileSetup.localStrings_.getString('please_wait'); 185 $('paymentForm').classList.add('hidden'); 186 $('finalStatus').classList.add('hidden'); 187 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL); 188 $('systemStatus').classList.remove('hidden'); 189 $('canvas').classList.remove('hidden'); 190 this.startSpinner_(); 191 break; 192 case MobileSetup.PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING: 193 // Activation page should not be shown for devices that are activated 194 // over non cellular network. 195 if (!deviceInfo.activate_over_non_cellular_network) { 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'); 204 } 205 this.loadPaymentFrame_(deviceInfo); 206 break; 207 case MobileSetup.PLAN_ACTIVATION_WAITING_FOR_CONNECTION: 208 $('statusHeader').textContent = 209 MobileSetup.localStrings_.getString('portal_unreachable_header'); 210 $('auxHeader').textContent = ''; 211 $('auxHeader').classList.add('hidden'); 212 $('paymentForm').classList.add('hidden'); 213 $('finalStatus').classList.add('hidden'); 214 $('systemStatus').classList.remove('hidden'); 215 this.setCarrierPage_(MobileSetup.PORTAL_OFFLINE_PAGE_URL); 216 $('canvas').classList.remove('hidden'); 217 this.startSpinner_(); 218 break; 219 case MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT: 220 $('statusHeader').textContent = ''; 221 $('auxHeader').textContent = ''; 222 $('finalStatus').classList.add('hidden'); 223 $('systemStatus').classList.add('hidden'); 224 $('paymentForm').classList.remove('hidden'); 225 $('canvas').classList.add('hidden'); 226 this.stopSpinner_(); 227 this.paymentShown_ = true; 228 break; 229 case MobileSetup.PLAN_ACTIVATION_DONE: 230 $('statusHeader').textContent = ''; 231 $('auxHeader').textContent = ''; 232 $('finalHeader').textContent = 233 MobileSetup.localStrings_.getString('completed_header'); 234 $('finalMessage').textContent = 235 MobileSetup.localStrings_.getString('completed_text'); 236 $('systemStatus').classList.add('hidden'); 237 $('closeButton').classList.remove('hidden'); 238 $('finalStatus').classList.remove('hidden'); 239 $('canvas').classList.add('hidden'); 240 $('closeButton').classList.toggle('hidden', !this.paymentShown_); 241 $('paymentForm').classList.toggle('hidden', !this.paymentShown_); 242 this.stopSpinner_(); 243 break; 244 case MobileSetup.PLAN_ACTIVATION_ERROR: 245 $('statusHeader').textContent = ''; 246 $('auxHeader').textContent = ''; 247 $('finalHeader').textContent = 248 MobileSetup.localStrings_.getString('error_header'); 249 $('finalMessage').textContent = deviceInfo.error; 250 $('systemStatus').classList.add('hidden'); 251 $('canvas').classList.add('hidden'); 252 $('closeButton').classList.toggle('hidden', !this.paymentShown_); 253 $('paymentForm').classList.toggle('hidden', !this.paymentShown_); 254 $('finalStatus').classList.remove('hidden'); 255 this.stopSpinner_(); 256 break; 257 } 258 this.state_ = newState; 259 }, 260 261 setCarrierPage_: function(url) { 262 if (this.carrierPageUrl_ == url) 263 return; 264 this.carrierPageUrl_ = url; 265 $('carrierPage').contentWindow.location.href = url; 266 }, 267 268 updateDeviceStatus_: function(deviceInfo) { 269 this.changeState_(deviceInfo); 270 }, 271 272 portalFrameLoadError_: function(errorCode) { 273 if (this.frameLoadIgnored_) 274 return; 275 console.log('Portal frame load error detected: ', errorCode); 276 this.frameLoadError_ = errorCode; 277 }, 278 279 portalFrameLoadCompleted_: function() { 280 if (this.frameLoadIgnored_) 281 return; 282 console.log('Portal frame load completed!'); 283 this.onFrameLoaded_(this.frameLoadError_ == 0); 284 }, 285 286 sendPostFrame_: function(frameUrl) { 287 var msg = { type: 'postFrame' }; 288 $(this.frameName_).contentWindow.postMessage(msg, frameUrl); 289 }, 290 291 sendDeviceInfo_: function() { 292 var msg = { 293 type: 'deviceInfoMsg', 294 domain: document.location, 295 payload: { 296 'carrier': this.deviceInfo_.carrier, 297 'MEID': this.deviceInfo_.MEID, 298 'IMEI': this.deviceInfo_.IMEI, 299 'MDN': this.deviceInfo_.MDN 300 } 301 }; 302 $(this.frameName_).contentWindow.postMessage(msg, 303 this.deviceInfo_.payment_url); 304 } 305 306 }; 307 308 MobileSetup.drawProgress = function() { 309 var ctx = canvas.getContext('2d'); 310 ctx.clearRect(0, 0, canvas.width, canvas.height); 311 312 var segmentCount = Math.min(12, canvas.width / 1.6); // Number of segments 313 var rotation = 0.75; // Counterclockwise rotation 314 315 // Rotate canvas over time 316 ctx.translate(canvas.width / 2, canvas.height / 2); 317 ctx.rotate(Math.PI * 2 / (segmentCount + rotation)); 318 ctx.translate(-canvas.width / 2, -canvas.height / 2); 319 320 var gap = canvas.width / 24; // Gap between segments 321 var oRadius = canvas.width / 2; // Outer radius 322 var iRadius = oRadius * 0.618; // Inner radius 323 var oCircumference = Math.PI * 2 * oRadius; // Outer circumference 324 var iCircumference = Math.PI * 2 * iRadius; // Inner circumference 325 var oGap = gap / oCircumference; // Gap size as fraction of outer ring 326 var iGap = gap / iCircumference; // Gap size as fraction of inner ring 327 var oArc = Math.PI * 2 * (1 / segmentCount - oGap); // Angle of outer arcs 328 var iArc = Math.PI * 2 * (1 / segmentCount - iGap); // Angle of inner arcs 329 330 for (i = 0; i < segmentCount; i++) { // Draw each segment 331 var opacity = Math.pow(1.0 - i / segmentCount, 3.0); 332 opacity = (0.15 + opacity * 0.8); // Vary from 0.15 to 0.95 333 var angle = - Math.PI * 2 * i / segmentCount; 334 335 ctx.beginPath(); 336 ctx.arc(canvas.width / 2, canvas.height / 2, oRadius, 337 angle - oArc / 2, angle + oArc / 2, false); 338 ctx.arc(canvas.width / 2, canvas.height / 2, iRadius, 339 angle + iArc / 2, angle - iArc / 2, true); 340 ctx.closePath(); 341 ctx.fillStyle = 'rgba(240, 30, 29, ' + opacity + ')'; 342 ctx.fill(); 343 } 344 }; 345 346 MobileSetup.deviceStateChanged = function(deviceInfo) { 347 MobileSetup.getInstance().updateDeviceStatus_(deviceInfo); 348 }; 349 350 MobileSetup.portalFrameLoadError = function(errorCode) { 351 MobileSetup.getInstance().portalFrameLoadError_(errorCode); 352 }; 353 354 MobileSetup.portalFrameLoadCompleted = function() { 355 MobileSetup.getInstance().portalFrameLoadCompleted_(); 356 }; 357 358 MobileSetup.loadPage = function() { 359 mobile.MobileSetup.getInstance().initialize('paymentForm', 360 mobile.MobileSetup.ACTIVATION_PAGE_URL); 361 }; 362 363 // Export 364 return { 365 MobileSetup: MobileSetup 366 }; 367}); 368 369document.addEventListener('DOMContentLoaded', mobile.MobileSetup.loadPage); 370