Remove the old signature of NotificationManager::closePersistent().
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / login / network_dropdown.js
blob846183d13ff20055c9068ec1f349fd2e2fe1f94c
1 // Copyright (c) 2012 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 /**
6 * @fileoverview Network drop-down implementation.
7 */
9 cr.define('cr.ui', function() {
10 /**
11 * Whether keyboard flow is in use. When setting to true, up/down arrow key
12 * will be used to move focus instead of opening the drop down.
14 var useKeyboardFlow = false;
16 /**
17 * Creates a new container for the drop down menu items.
18 * @constructor
19 * @extends {HTMLDivElement}
21 var DropDownContainer = cr.ui.define('div');
23 DropDownContainer.prototype = {
24 __proto__: HTMLDivElement.prototype,
26 /** @override */
27 decorate: function() {
28 this.classList.add('dropdown-container');
29 // Selected item in the menu list.
30 this.selectedItem = null;
31 // First item which could be selected.
32 this.firstItem = null;
33 this.setAttribute('role', 'menu');
34 // Whether scroll has just happened.
35 this.scrollJustHappened = false;
38 /**
39 * Gets scroll action to be done for the item.
40 * @param {!Object} item Menu item.
41 * @return {integer} -1 for scroll up; 0 for no action; 1 for scroll down.
43 scrollAction: function(item) {
44 var thisTop = this.scrollTop;
45 var thisBottom = thisTop + this.offsetHeight;
46 var itemTop = item.offsetTop;
47 var itemBottom = itemTop + item.offsetHeight;
48 if (itemTop <= thisTop) return -1;
49 if (itemBottom >= thisBottom) return 1;
50 return 0;
53 /**
54 * Selects new item.
55 * @param {!Object} selectedItem Item to be selected.
56 * @param {boolean} mouseOver Is mouseover event triggered?
58 selectItem: function(selectedItem, mouseOver) {
59 if (mouseOver && this.scrollJustHappened) {
60 this.scrollJustHappened = false;
61 return;
63 if (this.selectedItem)
64 this.selectedItem.classList.remove('hover');
65 selectedItem.classList.add('hover');
66 this.selectedItem = selectedItem;
67 if (!this.hidden) {
68 this.previousSibling.setAttribute(
69 'aria-activedescendant', selectedItem.id);
71 var action = this.scrollAction(selectedItem);
72 if (action != 0) {
73 selectedItem.scrollIntoView(action < 0);
74 this.scrollJustHappened = true;
79 /**
80 * Creates a new DropDown div.
81 * @constructor
82 * @extends {HTMLDivElement}
84 var DropDown = cr.ui.define('div');
86 DropDown.ITEM_DIVIDER_ID = -2;
88 DropDown.KEYCODE_DOWN = 40;
89 DropDown.KEYCODE_ENTER = 13;
90 DropDown.KEYCODE_ESC = 27;
91 DropDown.KEYCODE_SPACE = 32;
92 DropDown.KEYCODE_TAB = 9;
93 DropDown.KEYCODE_UP = 38;
95 DropDown.prototype = {
96 __proto__: HTMLDivElement.prototype,
98 /** @override */
99 decorate: function() {
100 this.appendChild(this.createOverlay_());
101 this.appendChild(this.title_ = this.createTitle_());
102 var container = new DropDownContainer();
103 container.id = this.id + '-dropdown-container';
104 this.appendChild(container);
106 this.addEventListener('keydown', this.keyDownHandler_);
108 this.title_.id = this.id + '-dropdown';
109 this.title_.setAttribute('role', 'button');
110 this.title_.setAttribute('aria-haspopup', 'true');
111 this.title_.setAttribute('aria-owns', container.id);
115 * Returns true if dropdown menu is shown.
116 * @type {bool} Whether menu element is shown.
118 get isShown() {
119 return !this.container.hidden;
123 * Sets dropdown menu visibility.
124 * @param {bool} show New visibility state for dropdown menu.
126 set isShown(show) {
127 this.firstElementChild.hidden = !show;
128 this.container.hidden = !show;
129 if (show) {
130 this.container.selectItem(this.container.firstItem, false);
131 } else {
132 this.title_.removeAttribute('aria-activedescendant');
135 // Flag for keyboard flow util to forward the up/down keys.
136 this.title_.classList.toggle('needs-up-down-keys', show);
140 * Returns container of the menu items.
142 get container() {
143 return this.lastElementChild;
147 * Sets title and icon.
148 * @param {string} title Text on dropdown.
149 * @param {string} icon Icon in dataURL format.
151 setTitle: function(title, icon) {
152 this.title_.firstElementChild.src = icon;
153 this.title_.lastElementChild.textContent = title;
157 * Sets dropdown items.
158 * @param {Array} items Dropdown items array.
160 setItems: function(items) {
161 this.container.innerHTML = '';
162 this.container.firstItem = null;
163 this.container.selectedItem = null;
164 for (var i = 0; i < items.length; ++i) {
165 var item = items[i];
166 if ('sub' in item) {
167 // Workaround for submenus, add items on top level.
168 // TODO(altimofeev): support submenus.
169 for (var j = 0; j < item.sub.length; ++j)
170 this.createItem_(this.container, item.sub[j]);
171 continue;
173 this.createItem_(this.container, item);
175 this.container.selectItem(this.container.firstItem, false);
177 var maxHeight = cr.ui.LoginUITools.getMaxHeightBeforeShelfOverlapping(
178 this.container);
179 if (maxHeight < this.container.offsetHeight)
180 this.container.style.maxHeight = maxHeight + 'px';
184 * Id of the active drop-down element.
185 * @private
187 activeElementId_: '',
190 * Creates dropdown item element and adds into container.
191 * @param {HTMLElement} container Container where item is added.
192 * @param {!Object} item Item to be added.
193 * @private
195 createItem_: function(container, item) {
196 var itemContentElement;
197 var className = 'dropdown-item';
198 if (item.id == DropDown.ITEM_DIVIDER_ID) {
199 className = 'dropdown-divider';
200 itemContentElement = this.ownerDocument.createElement('hr');
201 } else {
202 var span = this.ownerDocument.createElement('span');
203 itemContentElement = span;
204 span.textContent = item.label;
205 if ('bold' in item && item.bold)
206 span.classList.add('bold');
207 var image = this.ownerDocument.createElement('img');
208 image.alt = '';
209 image.classList.add('dropdown-image');
210 if (item.icon)
211 image.src = item.icon;
214 var itemElement = this.ownerDocument.createElement('div');
215 itemElement.classList.add(className);
216 itemElement.appendChild(itemContentElement);
217 itemElement.iid = item.id;
218 itemElement.controller = this;
219 var enabled = 'enabled' in item && item.enabled;
220 if (!enabled)
221 itemElement.classList.add('disabled-item');
223 if (item.id > 0) {
224 var wrapperDiv = this.ownerDocument.createElement('div');
225 wrapperDiv.setAttribute('role', 'menuitem');
226 wrapperDiv.id = this.id + item.id;
227 if (!enabled)
228 wrapperDiv.setAttribute('aria-disabled', 'true');
229 wrapperDiv.classList.add('dropdown-item-container');
230 var imageDiv = this.ownerDocument.createElement('div');
231 imageDiv.appendChild(image);
232 wrapperDiv.appendChild(imageDiv);
233 wrapperDiv.appendChild(itemElement);
234 wrapperDiv.addEventListener('click', function f(e) {
235 var item = this.lastElementChild;
236 if (item.iid < -1 || item.classList.contains('disabled-item'))
237 return;
238 item.controller.isShown = false;
239 if (item.iid >= 0)
240 chrome.send('networkItemChosen', [item.iid]);
241 this.parentNode.parentNode.title_.focus();
243 wrapperDiv.addEventListener('mouseover', function f(e) {
244 this.parentNode.selectItem(this, true);
246 itemElement = wrapperDiv;
248 container.appendChild(itemElement);
249 if (!container.firstItem && item.id >= 0) {
250 container.firstItem = itemElement;
255 * Creates dropdown overlay element, which catches outside clicks.
256 * @type {HTMLElement}
257 * @private
259 createOverlay_: function() {
260 var overlay = this.ownerDocument.createElement('div');
261 overlay.classList.add('dropdown-overlay');
262 overlay.addEventListener('click', function() {
263 this.parentNode.title_.focus();
264 this.parentNode.isShown = false;
266 return overlay;
270 * Creates dropdown title element.
271 * @type {HTMLElement}
272 * @private
274 createTitle_: function() {
275 var image = this.ownerDocument.createElement('img');
276 image.alt = '';
277 image.classList.add('dropdown-image');
278 var text = this.ownerDocument.createElement('div');
280 var el = this.ownerDocument.createElement('div');
281 el.appendChild(image);
282 el.appendChild(text);
284 el.tabIndex = 0;
285 el.classList.add('dropdown-title');
286 el.iid = -1;
287 el.controller = this;
288 el.inFocus = false;
289 el.opening = false;
291 el.addEventListener('click', function f(e) {
292 this.controller.isShown = !this.controller.isShown;
295 el.addEventListener('focus', function(e) {
296 this.inFocus = true;
299 el.addEventListener('blur', function(e) {
300 this.inFocus = false;
303 el.addEventListener('keydown', function f(e) {
304 if (this.inFocus && !this.controller.isShown &&
305 (e.keyCode == DropDown.KEYCODE_ENTER ||
306 e.keyCode == DropDown.KEYCODE_SPACE ||
307 (!useKeyboardFlow && (e.keyCode == DropDown.KEYCODE_UP ||
308 e.keyCode == DropDown.KEYCODE_DOWN)))) {
309 this.opening = true;
310 this.controller.isShown = true;
311 e.stopPropagation();
312 e.preventDefault();
315 return el;
319 * Handles keydown event from the keyboard.
320 * @private
321 * @param {!Event} e Keydown event.
323 keyDownHandler_: function(e) {
324 if (!this.isShown)
325 return;
326 var selected = this.container.selectedItem;
327 var handled = false;
328 switch (e.keyCode) {
329 case DropDown.KEYCODE_UP: {
330 do {
331 selected = selected.previousSibling;
332 if (!selected)
333 selected = this.container.lastElementChild;
334 } while (selected.iid < 0);
335 this.container.selectItem(selected, false);
336 handled = true;
337 break;
339 case DropDown.KEYCODE_DOWN: {
340 do {
341 selected = selected.nextSibling;
342 if (!selected)
343 selected = this.container.firstItem;
344 } while (selected.iid < 0);
345 this.container.selectItem(selected, false);
346 handled = true;
347 break;
349 case DropDown.KEYCODE_ESC: {
350 this.isShown = false;
351 handled = true;
352 break;
354 case DropDown.KEYCODE_TAB: {
355 this.isShown = false;
356 handled = true;
357 break;
359 case DropDown.KEYCODE_ENTER: {
360 if (!this.title_.opening) {
361 this.title_.focus();
362 this.isShown = false;
363 var item =
364 this.title_.controller.container.selectedItem.lastElementChild;
365 if (item.iid >= 0 && !item.classList.contains('disabled-item'))
366 chrome.send('networkItemChosen', [item.iid]);
368 handled = true;
369 break;
372 if (handled) {
373 e.stopPropagation();
374 e.preventDefault();
376 this.title_.opening = false;
381 * Updates networks list with the new data.
382 * @param {!Object} data Networks list.
384 DropDown.updateNetworks = function(data) {
385 if (DropDown.activeElementId_)
386 $(DropDown.activeElementId_).setItems(data);
390 * Updates network title, which is shown by the drop-down.
391 * @param {string} title Title to be displayed.
392 * @param {!Object} icon Icon to be displayed.
394 DropDown.updateNetworkTitle = function(title, icon) {
395 if (DropDown.activeElementId_)
396 $(DropDown.activeElementId_).setTitle(title, icon);
400 * Activates network drop-down. Only one network drop-down
401 * can be active at the same time. So activating new drop-down deactivates
402 * the previous one.
403 * @param {string} elementId Id of network drop-down element.
404 * @param {boolean} isOobe Whether drop-down is used by an Oobe screen.
406 DropDown.show = function(elementId, isOobe) {
407 $(elementId).isShown = false;
408 if (DropDown.activeElementId_ != elementId) {
409 DropDown.activeElementId_ = elementId;
410 chrome.send('networkDropdownShow', [elementId, isOobe]);
415 * Deactivates network drop-down. Deactivating inactive drop-down does
416 * nothing.
417 * @param {string} elementId Id of network drop-down element.
419 DropDown.hide = function(elementId) {
420 if (DropDown.activeElementId_ == elementId) {
421 DropDown.activeElementId_ = '';
422 chrome.send('networkDropdownHide');
427 * Refreshes network drop-down. Should be called on language change.
429 DropDown.refresh = function() {
430 chrome.send('networkDropdownRefresh');
434 * Sets the keyboard flow flag.
436 DropDown.enableKeyboardFlow = function() {
437 useKeyboardFlow = true;
440 return {
441 DropDown: DropDown