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