1 // Copyright 2015 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.
5 /** @suppress {duplicate} */
6 var remoting = remoting || {};
13 * A helper class for implementing dialogs with an input field using
16 * @param {remoting.AppMode} mode
17 * @param {HTMLElement} formElement
18 * @param {HTMLElement} inputField
19 * @param {HTMLElement} cancelButton
23 remoting.InputDialog = function(mode, formElement, inputField, cancelButton) {
27 this.formElement_ = formElement;
29 this.cancelButton_ = cancelButton;
31 this.inputField_ = inputField;
32 /** @private {base.Deferred} */
33 this.deferred_ = null;
34 /** @private {base.Disposables} */
35 this.eventHooks_ = null;
39 * @return {Promise<string>} Promise that resolves with the value of the
40 * inputField or rejects with |remoting.Error.CANCELLED| if the user clicks
41 * on the cancel button.
43 remoting.InputDialog.prototype.show = function() {
44 var onCancel = this.createFormEventHandler_(this.onCancel_.bind(this));
45 var onOk = this.createFormEventHandler_(this.onSubmit_.bind(this));
47 this.eventHooks_ = new base.Disposables(
48 new base.DomEventHook(this.formElement_, 'submit', onOk, false),
49 new base.DomEventHook(this.cancelButton_, 'click', onCancel, false));
50 console.assert(this.deferred_ === null, 'No deferred Promise found.');
51 this.deferred_ = new base.Deferred();
52 remoting.setMode(this.appMode_);
53 return this.deferred_.promise();
56 /** @return {HTMLElement} */
57 remoting.InputDialog.prototype.inputField = function() {
58 return this.inputField_;
62 remoting.InputDialog.prototype.onSubmit_ = function() {
63 this.deferred_.resolve(this.inputField_.value);
67 remoting.InputDialog.prototype.onCancel_ = function() {
68 this.deferred_.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
72 * @param {function():void} handler
76 remoting.InputDialog.prototype.createFormEventHandler_ = function(handler) {
78 return function (/** Event */ e) {
79 // Prevents form submission from reloading the v1 app.
82 // Set the focus away from the password field. This has to be done
83 // before the password field gets hidden, to work around a Blink
84 // clipboard-handling bug - http://crbug.com/281523.
85 that.cancelButton_.focus();
87 base.dispose(that.eventHooks_);
88 that.eventHooks_ = null;
89 that.deferred_ = null;
94 * A helper class for implementing MessageDialog with a primary and
95 * and secondary button using remoting.setMode().
97 * @param {remoting.AppMode} mode
98 * @param {HTMLElement} primaryButton
99 * @param {HTMLElement=} opt_secondaryButton
102 * @implements {base.Disposable}
104 remoting.MessageDialog = function(mode, primaryButton, opt_secondaryButton) {
105 /** @private @const */
107 /** @private @const */
108 this.primaryButton_ = primaryButton;
109 /** @private @const */
110 this.secondaryButton_ = opt_secondaryButton;
111 /** @private {base.Deferred} */
112 this.deferred_ = null;
113 /** @private {base.Disposables} */
114 this.eventHooks_ = null;
118 * @return {Promise<remoting.MessageDialog.Result>} Promise that resolves with
119 * the button clicked.
121 remoting.MessageDialog.prototype.show = function() {
122 console.assert(this.eventHooks_ === null, 'Duplicate show() invocation.');
123 this.eventHooks_ = new base.Disposables(new base.DomEventHook(
124 this.primaryButton_, 'click',
125 this.onClicked_.bind(this, remoting.MessageDialog.Result.PRIMARY),
128 if (this.secondaryButton_) {
129 this.eventHooks_.add(new base.DomEventHook(
130 this.secondaryButton_, 'click',
131 this.onClicked_.bind(this, remoting.MessageDialog.Result.SECONDARY),
135 console.assert(this.deferred_ === null, 'No deferred Promise found.');
136 this.deferred_ = new base.Deferred();
137 remoting.setMode(this.mode_);
138 return this.deferred_.promise();
141 remoting.MessageDialog.prototype.dispose = function() {
142 base.dispose(this.eventHooks_);
143 this.eventHooks_ = null;
144 if (this.deferred_) {
145 this.deferred_.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
147 this.deferred_ = null;
151 * @param {remoting.MessageDialog.Result} result
155 remoting.MessageDialog.prototype.onClicked_ = function(result) {
156 this.deferred_.resolve(result);
157 this.deferred_ = null;
164 * A promise-based dialog implementation using the <dialog> element.
166 * @param {remoting.Html5ModalDialog.Params} params
169 * @implements {remoting.WindowShape.ClientUI}
170 * @implements {base.Disposable}
172 remoting.Html5ModalDialog = function(params) {
174 this.dialog_ = params.dialog;
176 /** @private {base.Disposables} */
177 this.eventHooks_ = new base.Disposables(
178 new base.DomEventHook(
179 this.dialog_, 'cancel', this.onCancel_.bind(this), false),
180 new base.DomEventHook(
181 params.primaryButton, 'click',
182 this.close.bind(this, remoting.MessageDialog.Result.PRIMARY), false)
185 if (params.secondaryButton) {
186 this.eventHooks_.add(new base.DomEventHook(
187 params.secondaryButton, 'click',
188 this.close.bind(this, remoting.MessageDialog.Result.SECONDARY), false));
192 this.closeOnEscape_ = Boolean(params.closeOnEscape);
195 this.windowShape_ = params.windowShape;
197 /** @private {base.Deferred} */
198 this.deferred_ = null;
201 remoting.Html5ModalDialog.prototype.dispose = function() {
202 if (this.dialog_.open) {
203 this.close(remoting.MessageDialog.Result.CANCEL);
205 base.dispose(this.eventHooks_);
206 this.eventHookes_ = null;
210 * @return {Promise<remoting.MessageDialog.Result>} Promise that resolves with
211 * the button clicked.
213 remoting.Html5ModalDialog.prototype.show = function() {
214 console.assert(this.deferred_ === null, 'No deferred Promise found.');
215 this.deferred_ = new base.Deferred();
216 this.dialog_.showModal();
217 if (this.windowShape_) {
218 this.windowShape_.registerClientUI(this);
219 this.windowShape_.centerToDesktop(this.dialog_);
221 return this.deferred_.promise();
224 /** @param {Event} e */
225 remoting.Html5ModalDialog.prototype.onCancel_ = function(e) {
227 if (this.closeOnEscape_) {
228 this.close(remoting.MessageDialog.Result.CANCEL);
233 * @param {remoting.MessageDialog.Result} result
235 remoting.Html5ModalDialog.prototype.close = function(result) {
236 if (!this.dialog_.open) {
239 this.dialog_.close();
240 this.deferred_.resolve(result);
241 this.deferred_ = null;
242 if (this.windowShape_) {
243 this.windowShape_.unregisterClientUI(this);
247 remoting.Html5ModalDialog.prototype.addToRegion = function(rects) {
248 var rect = /** @type {ClientRect} */(this.dialog_.getBoundingClientRect());
250 // If the dialog is repositioned by setting the left and top, it takes a while
251 // for getBoundingClientRect() to update the rectangle.
252 var left = this.dialog_.style.left;
253 var top = this.dialog_.style.top;
256 left: (left === '') ? rect.left : parseInt(left, 10),
257 top: (top === '') ? rect.top : parseInt(top, 10),
265 * @param {Function} cancelCallback The callback to invoke when the user clicks
266 * on the cancel button.
269 remoting.ConnectingDialog = function(cancelCallback) {
271 this.dialog_ = new remoting.MessageDialog(
272 remoting.AppMode.CLIENT_CONNECTING,
273 document.getElementById('cancel-connect-button'));
275 this.onCancel_ = cancelCallback;
278 remoting.ConnectingDialog.prototype.show = function() {
280 this.dialog_.show().then(function() {
281 remoting.setMode(remoting.AppMode.HOME);
283 // The promise rejects when the dialog is hidden. Don't report that as error.
284 }).catch(remoting.Error.handler(base.doNothing));
287 remoting.ConnectingDialog.prototype.hide = function() {
288 this.dialog_.dispose();
292 * A factory object for the modal dialogs. The factory will be stubbed out in
293 * unit test to avoid UI dependencies on remoting.setMode().
297 remoting.ModalDialogFactory = function() {};
300 * @param {Function} cancelCallback
301 * @return {remoting.ConnectingDialog}
303 remoting.ModalDialogFactory.prototype.createConnectingDialog =
304 function(cancelCallback) {
305 return new remoting.ConnectingDialog(cancelCallback);
309 * @param {remoting.Html5ModalDialog.Params} params
310 * @return {remoting.Html5ModalDialog}
312 remoting.ModalDialogFactory.prototype.createHtml5ModalDialog =
314 return new remoting.Html5ModalDialog(params);
318 * @param {remoting.AppMode} mode
319 * @param {HTMLElement} primaryButton
320 * @param {HTMLElement=} opt_secondaryButton
321 * @return {remoting.MessageDialog}
323 remoting.ModalDialogFactory.prototype.createMessageDialog =
324 function(mode, primaryButton, opt_secondaryButton) {
325 return new remoting.MessageDialog(mode, primaryButton, opt_secondaryButton);
329 * @param {remoting.AppMode} mode
330 * @param {HTMLElement} formElement
331 * @param {HTMLElement} inputField
332 * @param {HTMLElement} cancelButton
333 * @return {remoting.InputDialog}
335 remoting.ModalDialogFactory.prototype.createInputDialog =
336 function(mode, formElement, inputField, cancelButton) {
337 return new remoting.InputDialog(mode, formElement, inputField, cancelButton);
340 /** @type {remoting.ModalDialogFactory} */
341 remoting.modalDialogFactory = new remoting.ModalDialogFactory();
346 * Define the enum at the end of file as JSCompile doesn't understand enums that
347 * are defined within an IIFE (Immediately Invoked Function Expression).
350 remoting.MessageDialog.Result = {
353 CANCEL: 2 // User presses the escape button.
357 * Parameters for the remoting.Html5ModalDialog constructor. Unless otherwise
358 * noted, all parameters are optional.
360 * dialog: (required) The HTML dialog element.
362 * primaryButton: (required) The HTML element of the primary button.
364 * secondaryButton: The HTML element of the secondary button.
366 * closeOnEscape: Whether the user can dismiss the dialog by pressing the escape
367 * key. Default to false.
370 * dialog: HTMLDialogElement,
371 * primaryButton:HTMLElement,
372 * secondaryButton:(HTMLElement|undefined),
373 * closeOnEscape:(boolean|undefined),
374 * windowShape:(remoting.WindowShape|undefined)
377 remoting.Html5ModalDialog.Params;