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