blob: 42794017715f11700a03ae27ca247f1e13703f67 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/** @suppress {duplicate} */
var remoting = remoting || {};
(function() {
'use strict';
/**
* A helper class for implementing dialogs with an input field using
* remoting.setMode().
*
* @param {remoting.AppMode} mode
* @param {HTMLElement} formElement
* @param {HTMLElement} inputField
* @param {HTMLElement} cancelButton
*
* @constructor
*/
remoting.InputDialog = function(mode, formElement, inputField, cancelButton) {
/** @private */
this.appMode_ = mode;
/** @private */
this.formElement_ = formElement;
/** @private */
this.cancelButton_ = cancelButton;
/** @private */
this.inputField_ = inputField;
/** @private {base.Deferred} */
this.deferred_ = null;
/** @private {base.Disposables} */
this.eventHooks_ = null;
};
/**
* @return {Promise<string>} Promise that resolves with the value of the
* inputField or rejects with |remoting.Error.CANCELLED| if the user clicks
* on the cancel button.
*/
remoting.InputDialog.prototype.show = function() {
var onCancel = this.createFormEventHandler_(this.onCancel_.bind(this));
var onOk = this.createFormEventHandler_(this.onSubmit_.bind(this));
this.eventHooks_ = new base.Disposables(
new base.DomEventHook(this.formElement_, 'submit', onOk, false),
new base.DomEventHook(this.cancelButton_, 'click', onCancel, false));
console.assert(this.deferred_ === null, 'No deferred Promise found.');
this.deferred_ = new base.Deferred();
remoting.setMode(this.appMode_);
return this.deferred_.promise();
};
/** @return {HTMLElement} */
remoting.InputDialog.prototype.inputField = function() {
return this.inputField_;
};
/** @private */
remoting.InputDialog.prototype.onSubmit_ = function() {
this.deferred_.resolve(this.inputField_.value);
};
/** @private */
remoting.InputDialog.prototype.onCancel_ = function() {
this.deferred_.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
};
/**
* @param {function():void} handler
* @return {Function}
* @private
*/
remoting.InputDialog.prototype.createFormEventHandler_ = function(handler) {
var that = this;
return function (/** Event */ e) {
// Prevents form submission from reloading the v1 app.
e.preventDefault();
// Set the focus away from the password field. This has to be done
// before the password field gets hidden, to work around a Blink
// clipboard-handling bug - http://crbug.com/281523.
that.cancelButton_.focus();
handler();
base.dispose(that.eventHooks_);
that.eventHooks_ = null;
that.deferred_ = null;
};
};
/**
* A helper class for implementing MessageDialog with a primary and
* and secondary button using remoting.setMode().
*
* @param {remoting.AppMode} mode
* @param {HTMLElement} primaryButton
* @param {HTMLElement=} opt_secondaryButton
*
* @constructor
* @implements {base.Disposable}
*/
remoting.MessageDialog = function(mode, primaryButton, opt_secondaryButton) {
/** @private @const */
this.mode_ = mode;
/** @private @const */
this.primaryButton_ = primaryButton;
/** @private @const */
this.secondaryButton_ = opt_secondaryButton;
/** @private {base.Deferred} */
this.deferred_ = null;
/** @private {base.Disposables} */
this.eventHooks_ = null;
};
/**
* @return {Promise<remoting.MessageDialog.Result>} Promise that resolves with
* the button clicked.
*/
remoting.MessageDialog.prototype.show = function() {
console.assert(this.eventHooks_ === null, 'Duplicate show() invocation.');
this.eventHooks_ = new base.Disposables(new base.DomEventHook(
this.primaryButton_, 'click',
this.onClicked_.bind(this, remoting.MessageDialog.Result.PRIMARY),
false));
if (this.secondaryButton_) {
this.eventHooks_.add(new base.DomEventHook(
this.secondaryButton_, 'click',
this.onClicked_.bind(this, remoting.MessageDialog.Result.SECONDARY),
false));
}
console.assert(this.deferred_ === null, 'No deferred Promise found.');
this.deferred_ = new base.Deferred();
remoting.setMode(this.mode_);
return this.deferred_.promise();
};
remoting.MessageDialog.prototype.dispose = function() {
base.dispose(this.eventHooks_);
this.eventHooks_ = null;
if (this.deferred_) {
this.deferred_.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
}
this.deferred_ = null;
};
/**
* @param {remoting.MessageDialog.Result} result
* @private
*/
remoting.MessageDialog.prototype.onClicked_ = function(result) {
this.deferred_.resolve(result);
this.deferred_ = null;
this.dispose();
};
/**
* A promise-based dialog implementation using the <dialog> element.
*
* @param {remoting.Html5ModalDialog.Params} params
* @constructor
*
* @implements {remoting.WindowShape.ClientUI}
* @implements {base.Disposable}
*/
remoting.Html5ModalDialog = function(params) {
/** @private */
this.dialog_ = params.dialog;
/** @private {base.Disposables} */
this.eventHooks_ = new base.Disposables(
new base.DomEventHook(
this.dialog_, 'cancel', this.onCancel_.bind(this), false),
new base.DomEventHook(
params.primaryButton, 'click',
this.close.bind(this, remoting.MessageDialog.Result.PRIMARY), false)
);
if (params.secondaryButton) {
this.eventHooks_.add(new base.DomEventHook(
params.secondaryButton, 'click',
this.close.bind(this, remoting.MessageDialog.Result.SECONDARY), false));
}
/** @private */
this.closeOnEscape_ = Boolean(params.closeOnEscape);
/** @private */
this.windowShape_ = params.windowShape;
/** @private {base.Deferred} */
this.deferred_ = null;
};
remoting.Html5ModalDialog.prototype.dispose = function() {
if (this.dialog_.open) {
this.close(remoting.MessageDialog.Result.CANCEL);
}
base.dispose(this.eventHooks_);
this.eventHookes_ = null;
};
/**
* @return {Promise<remoting.MessageDialog.Result>} Promise that resolves with
* the button clicked.
*/
remoting.Html5ModalDialog.prototype.show = function() {
console.assert(this.deferred_ === null, 'No deferred Promise found.');
this.deferred_ = new base.Deferred();
this.dialog_.showModal();
if (this.windowShape_) {
this.windowShape_.registerClientUI(this);
this.windowShape_.centerToDesktop(this.dialog_);
}
return this.deferred_.promise();
};
/** @param {Event} e */
remoting.Html5ModalDialog.prototype.onCancel_ = function(e) {
e.preventDefault();
if (this.closeOnEscape_) {
this.close(remoting.MessageDialog.Result.CANCEL);
}
};
/**
* @param {remoting.MessageDialog.Result} result
*/
remoting.Html5ModalDialog.prototype.close = function(result) {
if (!this.dialog_.open) {
return;
}
this.dialog_.close();
this.deferred_.resolve(result);
this.deferred_ = null;
if (this.windowShape_) {
this.windowShape_.unregisterClientUI(this);
}
};
remoting.Html5ModalDialog.prototype.addToRegion = function(rects) {
var rect = /** @type {ClientRect} */(this.dialog_.getBoundingClientRect());
// If the dialog is repositioned by setting the left and top, it takes a while
// for getBoundingClientRect() to update the rectangle.
var left = this.dialog_.style.left;
var top = this.dialog_.style.top;
rects.push({
left: (left === '') ? rect.left : parseInt(left, 10),
top: (top === '') ? rect.top : parseInt(top, 10),
width: rect.width,
height: rect.height
});
};
/**
* @param {Function} cancelCallback The callback to invoke when the user clicks
* on the cancel button.
* @constructor
*/
remoting.ConnectingDialog = function(cancelCallback) {
/** @private */
this.dialog_ = new remoting.MessageDialog(
remoting.AppMode.CLIENT_CONNECTING,
base.getHtmlElement('cancel-connect-button'));
/** @private */
this.onCancel_ = cancelCallback;
};
remoting.ConnectingDialog.prototype.show = function() {
var that = this;
this.dialog_.show().then(function() {
remoting.setMode(remoting.AppMode.HOME);
that.onCancel_();
// The promise rejects when the dialog is hidden. Don't report that as error.
}).catch(remoting.Error.handler(base.doNothing));
};
remoting.ConnectingDialog.prototype.hide = function() {
this.dialog_.dispose();
};
/**
* A factory object for the modal dialogs. The factory will be stubbed out in
* unit test to avoid UI dependencies on remoting.setMode().
*
* @constructor
*/
remoting.ModalDialogFactory = function() {};
/**
* @param {Function} cancelCallback
* @return {remoting.ConnectingDialog}
*/
remoting.ModalDialogFactory.prototype.createConnectingDialog =
function(cancelCallback) {
return new remoting.ConnectingDialog(cancelCallback);
};
/**
* @param {remoting.Html5ModalDialog.Params} params
* @return {remoting.Html5ModalDialog}
*/
remoting.ModalDialogFactory.prototype.createHtml5ModalDialog =
function(params) {
return new remoting.Html5ModalDialog(params);
};
/**
* @param {remoting.AppMode} mode
* @param {HTMLElement} primaryButton
* @param {HTMLElement=} opt_secondaryButton
* @return {remoting.MessageDialog}
*/
remoting.ModalDialogFactory.prototype.createMessageDialog =
function(mode, primaryButton, opt_secondaryButton) {
return new remoting.MessageDialog(mode, primaryButton, opt_secondaryButton);
};
/**
* @param {remoting.AppMode} mode
* @param {HTMLElement} formElement
* @param {HTMLElement} inputField
* @param {HTMLElement} cancelButton
* @return {remoting.InputDialog}
*/
remoting.ModalDialogFactory.prototype.createInputDialog =
function(mode, formElement, inputField, cancelButton) {
return new remoting.InputDialog(mode, formElement, inputField, cancelButton);
};
/** @type {remoting.ModalDialogFactory} */
remoting.modalDialogFactory = new remoting.ModalDialogFactory();
})();
/**
* Define the enum at the end of file as JSCompile doesn't understand enums that
* are defined within an IIFE (Immediately Invoked Function Expression).
* @enum {number}
*/
remoting.MessageDialog.Result = {
PRIMARY: 0,
SECONDARY: 1,
CANCEL: 2 // User presses the escape button.
};
/**
* Parameters for the remoting.Html5ModalDialog constructor. Unless otherwise
* noted, all parameters are optional.
*
* dialog: (required) The HTML dialog element.
*
* primaryButton: (required) The HTML element of the primary button.
*
* secondaryButton: The HTML element of the secondary button.
*
* closeOnEscape: Whether the user can dismiss the dialog by pressing the escape
* key. Default to false.
*
* @typedef {{
* dialog: HTMLDialogElement,
* primaryButton:HTMLElement,
* secondaryButton:(HTMLElement|undefined),
* closeOnEscape:(boolean|undefined),
* windowShape:(remoting.WindowShape|undefined)
* }}
*/
remoting.Html5ModalDialog.Params;