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
5'use strict';
6
7/**
8 * @constructor
9 */
10function MessageWindowImpl() {
11  /**
12   * Used to prevent multiple responses due to the closeWindow handler.
13   *
14   * @type {boolean}
15   * @private
16   */
17  this.sentReply_ = false;
18
19  window.addEventListener('message', this.onMessage_.bind(this), false);
20};
21
22/**
23 * @param {Window} parentWindow The id of the window that showed the message.
24 * @param {string} messageId The identifier of the message, as supplied by the
25 *     parent.
26 * @param {number} result 0 if window was closed without pressing a button;
27 *     otherwise the index of the button pressed (e.g., 1 = primary).
28 * @private
29 */
30MessageWindowImpl.prototype.sendReply_ = function(
31    parentWindow, messageId, result) {
32  // Only forward the first reply that we receive.
33  if (!this.sentReply_) {
34    var message = {
35      command: 'messageWindowResult',
36      id: messageId,
37      result: result
38    };
39    parentWindow.postMessage(message, '*');
40    this.sentReply_ = true;
41  } else {
42    // Make sure that the reply we're ignoring is from the window close.
43    base.debug.assert(result == 0);
44  }
45};
46
47/**
48 * Size the window to its content vertically.
49 * @private
50 */
51MessageWindowImpl.prototype.updateSize_ = function() {
52  var borderY = window.outerHeight - window.innerHeight;
53  window.resizeTo(window.outerWidth, document.body.clientHeight + borderY);
54};
55
56/**
57 * Initializes the button with the label and the click handler.
58 * Hides the button if the label is null or undefined.
59 *
60 * @param{HTMLElement} button
61 * @param{?string} label
62 * @param{Function} clickHandler
63 * @private
64 */
65MessageWindowImpl.prototype.initButton_ =
66    function(button, label, clickHandler) {
67  if (label) {
68    button.innerText = label;
69    button.addEventListener('click', clickHandler, false);
70  }
71  button.hidden = !Boolean(label);
72};
73
74/**
75 * Event-handler callback, invoked when the parent window supplies the
76 * message content.
77 *
78 * @param{Event} event
79 * @private
80 */
81MessageWindowImpl.prototype.onMessage_ = function(event) {
82  switch (event.data['command']) {
83    case 'show':
84      // Validate the message.
85      var messageId = /** @type {number} */ (event.data['id']);
86      var title = /** @type {string} */ (event.data['title']);
87      var message = /** @type {string} */ (event.data['message']);
88      var infobox = /** @type {string} */ (event.data['infobox']);
89      var buttonLabel = /** @type {string} */ (event.data['buttonLabel']);
90      /** @type {string} */
91      var cancelButtonLabel = (event.data['cancelButtonLabel']);
92      var showSpinner = /** @type {boolean} */ (event.data['showSpinner']);
93      if (typeof(messageId) != 'number' ||
94          typeof(title) != 'string' ||
95          typeof(message) != 'string' ||
96          typeof(infobox) != 'string' ||
97          typeof(buttonLabel) != 'string' ||
98          typeof(showSpinner) != 'boolean') {
99        console.log('Bad show message:', event.data);
100        break;
101      }
102
103      // Set the dialog text.
104      var button = document.getElementById('button-primary');
105      var cancelButton = document.getElementById('button-secondary');
106      var messageDiv = document.getElementById('message');
107      var infoboxDiv = document.getElementById('infobox');
108
109      document.getElementById('title').innerText = title;
110      document.querySelector('title').innerText = title;
111      messageDiv.innerHTML = message;
112
113      if (showSpinner) {
114        messageDiv.classList.add('waiting');
115        messageDiv.classList.add('prominent');
116      }
117      if (infobox != '') {
118        infoboxDiv.innerText = infobox;
119      } else {
120        infoboxDiv.hidden = true;
121      }
122
123      this.initButton_(
124          button,
125          buttonLabel,
126          this.sendReply_.bind(this, event.source, messageId, 1));
127
128      this.initButton_(
129          cancelButton,
130          cancelButtonLabel,
131          this.sendReply_.bind(this, event.source, messageId, 0));
132
133      var buttonToFocus = (cancelButtonLabel) ? cancelButton : button;
134      buttonToFocus.focus();
135
136      // Add a close handler in case the window is closed without clicking one
137      // of the buttons. This will send a 0 as the result.
138      // Note that when a button is pressed, this will result in sendReply_
139      // being called multiple times (once for the button, once for close).
140      chrome.app.window.current().onClosed.addListener(
141          this.sendReply_.bind(this, event.source, messageId, 0));
142
143      this.updateSize_();
144      chrome.app.window.current().show();
145      break;
146
147    case 'update_message':
148      var message = /** @type {string} */ (event.data['message']);
149      if (typeof(message) != 'string') {
150        console.log('Bad update_message message:', event.data);
151        break;
152      }
153
154      var messageDiv = document.getElementById('message');
155      messageDiv.innerText = message;
156
157      this.updateSize_();
158      break;
159
160    default:
161      console.error('Unexpected message:', event.data);
162  }
163};
164
165var messageWindow = new MessageWindowImpl();
166