1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5"use strict";
6
7const Cc = Components.classes;
8const Ci = Components.interfaces;
9const Cu = Components.utils;
10
11const DEBUG = false;
12function debug(aStr) {
13  if (DEBUG) {
14    dump("-*- DateTimePickerHelper: " + aStr + "\n");
15  }
16}
17
18this.EXPORTED_SYMBOLS = [
19  "DateTimePickerHelper"
20];
21
22Cu.import("resource://gre/modules/XPCOMUtils.jsm");
23Cu.import("resource://gre/modules/Services.jsm");
24
25/*
26 * DateTimePickerHelper receives message from content side (input box) and
27 * is reposible for opening, closing and updating the picker. Similary,
28 * DateTimePickerHelper listens for picker's events and notifies the content
29 * side (input box) about them.
30 */
31this.DateTimePickerHelper = {
32  picker: null,
33  weakBrowser: null,
34
35  MESSAGES: [
36    "FormDateTime:OpenPicker",
37    "FormDateTime:ClosePicker",
38    "FormDateTime:UpdatePicker"
39  ],
40
41  init: function() {
42    for (let msg of this.MESSAGES) {
43      Services.mm.addMessageListener(msg, this);
44    }
45  },
46
47  uninit: function() {
48    for (let msg of this.MESSAGES) {
49      Services.mm.removeMessageListener(msg, this);
50    }
51  },
52
53  // nsIMessageListener
54  receiveMessage: function(aMessage) {
55    debug("receiveMessage: " + aMessage.name);
56    switch (aMessage.name) {
57      case "FormDateTime:OpenPicker": {
58        this.showPicker(aMessage.target, aMessage.data);
59        break;
60      }
61      case "FormDateTime:ClosePicker": {
62        if (!this.picker) {
63          return;
64        }
65        this.picker.closePicker();
66        break;
67      }
68      case "FormDateTime:UpdatePicker": {
69        this.picker.setPopupValue(aMessage.data);
70        break;
71      }
72      default:
73        break;
74    }
75  },
76
77  // nsIDOMEventListener
78  handleEvent: function(aEvent) {
79    debug("handleEvent: " + aEvent.type);
80    switch (aEvent.type) {
81      case "DateTimePickerValueChanged": {
82        this.updateInputBoxValue(aEvent);
83        break;
84      }
85      case "popuphidden": {
86        let browser = this.weakBrowser ? this.weakBrowser.get() : null;
87        if (browser) {
88          browser.messageManager.sendAsyncMessage("FormDateTime:PickerClosed");
89        }
90        this.close();
91        break;
92      }
93      default:
94        break;
95    }
96  },
97
98  // Called when picker value has changed, notify input box about it.
99  updateInputBoxValue: function(aEvent) {
100    // TODO: parse data based on input type.
101    const { hour, minute } = aEvent.detail;
102    debug("hour: " + hour + ", minute: " + minute);
103    let browser = this.weakBrowser ? this.weakBrowser.get() : null;
104    if (browser) {
105      browser.messageManager.sendAsyncMessage(
106        "FormDateTime:PickerValueChanged", { hour, minute });
107    }
108  },
109
110  // Get picker from browser and show it anchored to the input box.
111  showPicker: function(aBrowser, aData) {
112    let rect = aData.rect;
113    let dir = aData.dir;
114    let type = aData.type;
115    let detail = aData.detail;
116
117    this._anchor = aBrowser.ownerGlobal.gBrowser.popupAnchor;
118    this._anchor.left = rect.left;
119    this._anchor.top = rect.top;
120    this._anchor.width = rect.width;
121    this._anchor.height = rect.height;
122    this._anchor.hidden = false;
123
124    debug("Opening picker with details: " + JSON.stringify(detail));
125
126    let window = aBrowser.ownerDocument.defaultView;
127    let tabbrowser = window.gBrowser;
128    if (Services.focus.activeWindow != window ||
129        tabbrowser.selectedBrowser != aBrowser) {
130      // We were sent a message from a window or tab that went into the
131      // background, so we'll ignore it for now.
132      return;
133    }
134
135    this.weakBrowser = Cu.getWeakReference(aBrowser);
136    this.picker = aBrowser.dateTimePicker;
137    if (!this.picker) {
138      debug("aBrowser.dateTimePicker not found, exiting now.");
139      return;
140    }
141    this.picker.loadPicker(type, detail);
142    // The arrow panel needs an anchor to work. The popupAnchor (this._anchor)
143    // is a transparent div that the arrow can point to.
144    this.picker.openPopup(this._anchor, "after_start", rect.left, rect.top);
145
146    this.addPickerListeners();
147  },
148
149  // Picker is closed, do some cleanup.
150  close: function() {
151    this.removePickerListeners();
152    this.picker = null;
153    this.weakBrowser = null;
154    this._anchor.hidden = true;
155  },
156
157  // Listen to picker's event.
158  addPickerListeners: function() {
159    if (!this.picker) {
160      return;
161    }
162    this.picker.addEventListener("popuphidden", this);
163    this.picker.addEventListener("DateTimePickerValueChanged", this);
164  },
165
166  // Stop listening to picker's event.
167  removePickerListeners: function() {
168    if (!this.picker) {
169      return;
170    }
171    this.picker.removeEventListener("popuphidden", this);
172    this.picker.removeEventListener("DateTimePickerValueChanged", this);
173  },
174};
175