1// Copyright 2013 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 5cr.define('mobile', function() { 6 7 // TODO(tbarzic): Share code with mobile_setup.js. 8 var EXTENSION_BASE_URL = 9 'chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab/'; 10 var REDIRECT_POST_PAGE_URL = EXTENSION_BASE_URL + 'redirect.html?autoPost=1'; 11 var PORTAL_OFFLINE_PAGE_URL = EXTENSION_BASE_URL + 'portal_offline.html'; 12 var INVALID_DEVICE_INFO_PAGE_URL = 13 EXTENSION_BASE_URL + 'invalid_device_info.html'; 14 15 var NetworkState = { 16 UNKNOWN: 0, 17 PORTAL_REACHABLE: 1, 18 PORTAL_UNREACHABLE: 2 19 }; 20 21 var CarrierPageType = { 22 NOT_SET: 0, 23 PORTAL_OFFLINE: 1, 24 INVALID_DEVICE_INFO: 2 25 }; 26 27 function PortalImpl() { 28 // Mobile device information. 29 this.deviceInfo_ = null; 30 this.spinnerInt_ = -1; 31 this.networkState_ = NetworkState.UNKNOWN; 32 this.portalFrameSet_ = false; 33 this.carrierPageType_ = CarrierPageType.NOT_SET; 34 } 35 36 cr.addSingletonGetter(PortalImpl); 37 38 PortalImpl.prototype = { 39 initialize: function() { 40 // Get network device info for which portal should be opened. 41 // For LTE networks, this will also start observing network connection 42 // state and raise |updatePortalReachability| messages when the portal 43 // reachability changes. 44 chrome.send('getDeviceInfo'); 45 }, 46 47 updateDeviceInfo: function(deviceInfo) { 48 this.deviceInfo_ = deviceInfo; 49 this.updateState_(); 50 }, 51 52 updateNetworkState: function(networkState) { 53 if (this.networkState_ == networkState) 54 return; 55 this.networkState_ = networkState; 56 57 // If the device info is not yet set, the state will be updated on the 58 // device info update. 59 if (this.deviceInfo_) 60 this.updateState_(); 61 }, 62 63 updateState_: function() { 64 if (!this.deviceInfo_ || this.networkState_ == NetworkState.UNKNOWN) 65 return; 66 67 if (!this.isDeviceInfoValid_()) { 68 // If the device info is not valid, hide portalFrame and show system 69 // status displaying 'invalid device info' page. 70 this.setCarrierPage_(CarrierPageType.INVALID_DEVICE_INFO); 71 $('portalFrame').hidden = true; 72 $('systemStatus').hidden = false; 73 } else if (this.networkState_ != NetworkState.PORTAL_REACHABLE) { 74 // If the portal is not reachable, hide portalFrame and show system 75 // status displaying 'offline portal' page. 76 this.setCarrierPage_(CarrierPageType.PORTAL_OFFLINE); 77 $('portalFrame').hidden = true; 78 $('systemStatus').hidden = false; 79 } else { 80 // If the portal is reachable and device info is valid, set and show 81 // portalFrame; and hide system status displaying 'offline portal' page. 82 this.setPortalFrameIfNeeded_(this.deviceInfo_); 83 $('portalFrame').hidden = false; 84 $('systemStatus').hidden = true; 85 this.stopSpinner_(); 86 } 87 }, 88 89 setCarrierPage_: function(type) { 90 // The page is already set, nothing to do. 91 if (type == this.carrierPageType_) 92 return; 93 94 switch (type) { 95 case CarrierPageType.PORTAL_OFFLINE: 96 $('carrierPage').contentWindow.location.href = 97 PORTAL_OFFLINE_PAGE_URL; 98 $('statusHeader').textContent = 99 loadTimeData.getString('portal_unreachable_header'); 100 this.startSpinner_(); 101 break; 102 case CarrierPageType.INVALID_DEVICE_INFO: 103 $('carrierPage').contentWindow.location.href = 104 INVALID_DEVICE_INFO_PAGE_URL; 105 $('statusHeader').textContent = 106 loadTimeData.getString('invalid_device_info_header'); 107 this.stopSpinner_(); 108 break; 109 case CarrierPageType.NOT_SET: 110 $('carrierPage').contentWindow.location.href = 'about:blank'; 111 $('statusHeader').textContent = ''; 112 this.stopSpinner_(); 113 break; 114 default: 115 break; 116 } 117 118 this.carrierPageType_ = type; 119 }, 120 121 setPortalFrameIfNeeded_: function(deviceInfo) { 122 // The portal should be set only once. 123 if (this.portalFrameSet_) 124 return; 125 126 var postData = ''; 127 if (deviceInfo.post_data && deviceInfo.post_data.length) 128 postData = '&post_data=' + encodeURIComponent(deviceInfo.post_data); 129 130 $('portalFrame').contentWindow.location.href = REDIRECT_POST_PAGE_URL + 131 postData + '&formUrl=' + encodeURIComponent(deviceInfo.payment_url); 132 133 this.portalFrameSet_ = true; 134 }, 135 136 isDeviceInfoValid_: function() { 137 // Device info is valid if it has mdn which doesn't contain only '0's. 138 return this.deviceInfo_ && this.deviceInfo_.MDN && 139 this.deviceInfo_.MDN.match('[^0]'); 140 }, 141 142 startSpinner_: function() { 143 this.stopSpinner_(); 144 this.spinnerInt_ = setInterval(this.drawProgress_.bind(this), 100); 145 }, 146 147 stopSpinner_: function() { 148 if (this.spinnerInt_ != -1) { 149 clearInterval(this.spinnerInt_); 150 this.spinnerInt_ = -1; 151 } 152 // Clear the spinner canvas. 153 var ctx = canvas.getContext('2d'); 154 ctx.clearRect(0, 0, canvas.width, canvas.height); 155 }, 156 157 drawProgress_: function() { 158 var ctx = canvas.getContext('2d'); 159 ctx.clearRect(0, 0, canvas.width, canvas.height); 160 161 var segmentCount = Math.min(12, canvas.width / 1.6); // Number of segments 162 var rotation = 0.75; // Counterclockwise rotation 163 164 // Rotate canvas over time 165 ctx.translate(canvas.width / 2, canvas.height / 2); 166 ctx.rotate(Math.PI * 2 / (segmentCount + rotation)); 167 ctx.translate(-canvas.width / 2, -canvas.height / 2); 168 169 var gap = canvas.width / 24; // Gap between segments 170 var oRadius = canvas.width / 2; // Outer radius 171 var iRadius = oRadius * 0.618; // Inner radius 172 var oCircumference = Math.PI * 2 * oRadius; // Outer circumference 173 var iCircumference = Math.PI * 2 * iRadius; // Inner circumference 174 var oGap = gap / oCircumference; // Gap size as fraction of outer ring 175 var iGap = gap / iCircumference; // Gap size as fraction of inner ring 176 var oArc = Math.PI * 2 * (1 / segmentCount - oGap); // Angle of outer arcs 177 var iArc = Math.PI * 2 * (1 / segmentCount - iGap); // Angle of inner arcs 178 179 for (i = 0; i < segmentCount; i++) { // Draw each segment 180 var opacity = Math.pow(1.0 - i / segmentCount, 3.0); 181 opacity = (0.15 + opacity * 0.8); // Vary from 0.15 to 0.95 182 var angle = - Math.PI * 2 * i / segmentCount; 183 184 ctx.beginPath(); 185 ctx.arc(canvas.width / 2, canvas.height / 2, oRadius, 186 angle - oArc / 2, angle + oArc / 2, false); 187 ctx.arc(canvas.width / 2, canvas.height / 2, iRadius, 188 angle + iArc / 2, angle - iArc / 2, true); 189 ctx.closePath(); 190 ctx.fillStyle = 'rgba(240, 30, 29, ' + opacity + ')'; 191 ctx.fill(); 192 } 193 } 194 }; 195 196 function MobileSetupPortal() {} 197 198 MobileSetupPortal.loadPage = function() { 199 PortalImpl.getInstance().initialize(); 200 }; 201 202 MobileSetupPortal.onGotDeviceInfo = function(deviceInfo) { 203 PortalImpl.getInstance().updateDeviceInfo(deviceInfo); 204 }; 205 206 MobileSetupPortal.onConnectivityChanged = function(portalReachable) { 207 PortalImpl.getInstance().updateNetworkState( 208 portalReachable ? NetworkState.PORTAL_REACHABLE : 209 NetworkState.PORTAL_UNREACHABLE); 210 }; 211 212 // Export 213 return { 214 MobileSetupPortal: MobileSetupPortal 215 }; 216}); 217 218document.addEventListener('DOMContentLoaded', 219 mobile.MobileSetupPortal.loadPage); 220