1// Copyright 2014 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('settime', function() {
6  /**
7   * TimeSetter handles a dialog to check and set system time. It can also
8   * include a timezone dropdown if timezoneId is provided.
9   *
10   * TimeSetter uses the system time to populate the controls initially and
11   * update them as the system time or timezone changes, and notifies Chrome
12   * when the user changes the time or timezone.
13   * @constructor
14   */
15  function TimeSetter() {}
16
17  cr.addSingletonGetter(TimeSetter);
18
19  /** @const */ var BODY_PADDING_PX = 20;
20  /** @const */ var LABEL_PADDING_PX = 5;
21
22  TimeSetter.prototype = {
23    /**
24     * Performs initial setup.
25     */
26    initialize: function() {
27      // Store values for reverting inputs when the user's date/time is invalid.
28      this.prevValues_ = {};
29
30      // The build time doesn't include a timezone, so subtract 1 day to get a
31      // safe minimum date.
32      this.minDate_ = new Date(loadTimeData.getValue('buildTime'));
33      this.minDate_.setDate(this.minDate_.getDate() - 1);
34
35      // Set the max date to the min date plus 20 years.
36      this.maxDate_ = new Date(this.minDate_);
37      this.maxDate_.setYear(this.minDate_.getFullYear() + 20);
38
39      // Make sure the ostensible date is within this range.
40      var now = new Date();
41      if (now > this.maxDate_)
42        this.maxDate_ = now;
43      else if (now < this.minDate_)
44        this.minDate_ = now;
45
46      $('date').setAttribute('min', this.toHtmlValues_(this.minDate_).date);
47      $('date').setAttribute('max', this.toHtmlValues_(this.maxDate_).date);
48
49      this.updateTime_();
50
51      // Show the timezone select if we have a timezone ID.
52      var currentTimezoneId = loadTimeData.getValue('currentTimezoneId');
53      if (currentTimezoneId) {
54        this.setTimezone_(currentTimezoneId);
55        $('timezone-select').addEventListener(
56            'change', this.onTimezoneChange_.bind(this), false);
57        $('timezone').hidden = false;
58      }
59
60      this.sizeToFit_();
61
62      $('time').addEventListener('blur', this.onTimeBlur_.bind(this), false);
63      $('date').addEventListener('blur', this.onTimeBlur_.bind(this), false);
64
65      $('set-time').addEventListener(
66          'submit', this.onSubmit_.bind(this), false);
67    },
68
69    /**
70     * Sets the current timezone.
71     * @param {string} timezoneId The timezone ID to select.
72     * @private
73     */
74    setTimezone_: function(timezoneId) {
75      $('timezone-select').value = timezoneId;
76      this.updateTime_();
77    },
78
79    /**
80     * Updates the date/time controls to the current local time.
81     * Called initially, then called again once a minute.
82     * @private
83     */
84    updateTime_: function() {
85      var now = new Date();
86
87      // Only update time controls if neither is focused.
88      if (document.activeElement.id != 'date' &&
89          document.activeElement.id != 'time') {
90        var htmlValues = this.toHtmlValues_(now);
91        this.prevValues_.date = $('date').value = htmlValues.date;
92        this.prevValues_.time = $('time').value = htmlValues.time;
93      }
94
95      window.clearTimeout(this.timeTimeout_);
96
97      // Start timer to update these inputs every minute.
98      var secondsRemaining = 60 - now.getSeconds();
99      this.timeTimeout_ = window.setTimeout(this.updateTime_.bind(this),
100                                            secondsRemaining * 1000);
101    },
102
103    /**
104     * Sets the system time from the UI.
105     * @private
106     */
107    applyTime_: function() {
108      var date = $('date').valueAsDate;
109      date.setMilliseconds(date.getMilliseconds() + $('time').valueAsNumber);
110
111      // Add timezone offset to get real time.
112      date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
113
114      var seconds = Math.floor(date / 1000);
115      chrome.send('setTimeInSeconds', [seconds]);
116    },
117
118    /**
119     * Called when focus is lost on date/time controls.
120     * @param {Event} e The blur event.
121     * @private
122     */
123    onTimeBlur_: function(e) {
124      if (e.target.validity.valid && e.target.value) {
125        // Make this the new fallback time in case of future invalid input.
126        this.prevValues_[e.target.id] = e.target.value;
127        this.applyTime_();
128      } else {
129        // Restore previous value.
130        e.target.value = this.prevValues_[e.target.id];
131      }
132    },
133
134    /**
135     * @param {Event} e The change event.
136     * @private
137     */
138    onTimezoneChange_: function(e) {
139      chrome.send('setTimezone', [e.currentTarget.value]);
140    },
141
142    /**
143     * Closes the dialog window.
144     * @param {Event} e The submit event.
145     * @private
146     */
147    onSubmit_: function(e) {
148      e.preventDefault();
149      chrome.send('dialogClose');
150    },
151
152    /**
153     * Resizes the window if necessary to show the entire contents.
154     * @private
155     */
156    sizeToFit_: function() {
157      // Because of l10n, we should check that the vertical content can fit
158      // within the window.
159      if (window.innerHeight < document.body.scrollHeight) {
160        // Resize window to fit scrollHeight and the title bar.
161        var newHeight = document.body.scrollHeight +
162                        window.outerHeight - window.innerHeight;
163        window.resizeTo(window.outerWidth, newHeight);
164      }
165    },
166
167    /**
168     * Builds date and time strings suitable for the values of HTML date and
169     * time elements.
170     * @param {Date} date The date object to represent.
171     * @return {{date: string, time: string}} Date is an RFC 3339 formatted date
172     *     and time is an HH:MM formatted time.
173     * @private
174     */
175    toHtmlValues_: function(date) {
176      // Get the current time and subtract the timezone offset, so the
177      // JSON string is in local time.
178      var localDate = new Date(date);
179      localDate.setMinutes(date.getMinutes() - date.getTimezoneOffset());
180      return {date: localDate.toISOString().slice(0, 10),
181              time: localDate.toISOString().slice(11, 16)};
182    },
183  };
184
185  TimeSetter.setTimezone = function(timezoneId) {
186    TimeSetter.getInstance().setTimezone_(timezoneId);
187  };
188
189  TimeSetter.updateTime = function() {
190    TimeSetter.getInstance().updateTime_();
191  };
192
193  return {
194    TimeSetter: TimeSetter
195  };
196});
197
198document.addEventListener('DOMContentLoaded', function() {
199  settime.TimeSetter.getInstance().initialize();
200});
201