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