Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: remoting/webapp/crd/js/ui_mode.js

Issue 1133913002: [Chromoting] Move shared webapp JS files from crd/js -> base/js (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/webapp/crd/js/typecheck_unittest.js ('k') | remoting/webapp/crd/js/wcs_adapter.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 /**
6 * @fileoverview
7 * Functions related to controlling the modal UI state of the app. UI states
8 * are expressed as HTML attributes with a dotted hierarchy. For example, the
9 * string 'host.shared' will match any elements with an associated attribute
10 * of 'host' or 'host.shared', showing those elements and hiding all others.
11 * Elements with no associated attribute are ignored.
12 */
13
14 'use strict';
15
16 /** @suppress {duplicate} */
17 var remoting = remoting || {};
18
19 /** @enum {string} */
20 // TODO(jamiewalch): Move 'in-session' to a separate web-page so that the
21 // 'home' state applies to all elements and can be removed.
22 remoting.AppMode = {
23 HOME: 'home',
24 TOKEN_REFRESH_FAILED: 'home.token-refresh-failed',
25 HOST_INSTALL: 'home.host-install',
26 HOST_INSTALL_PROMPT: 'home.host-install.prompt',
27 HOST_INSTALL_PENDING: 'home.host-install.pending',
28 HOST: 'home.host',
29 HOST_WAITING_FOR_CODE: 'home.host.waiting-for-code',
30 HOST_WAITING_FOR_CONNECTION: 'home.host.waiting-for-connection',
31 HOST_SHARED: 'home.host.shared',
32 HOST_SHARE_FAILED: 'home.host.share-failed',
33 HOST_SHARE_FINISHED: 'home.host.share-finished',
34 CLIENT: 'home.client',
35 CLIENT_UNCONNECTED: 'home.client.unconnected',
36 CLIENT_PIN_PROMPT: 'home.client.pin-prompt',
37 CLIENT_THIRD_PARTY_AUTH: 'home.client.third-party-auth',
38 CLIENT_CONNECTING: 'home.client.connecting',
39 CLIENT_CONNECT_FAILED_IT2ME: 'home.client.connect-failed.it2me',
40 CLIENT_CONNECT_FAILED_ME2ME: 'home.client.connect-failed.me2me',
41 CLIENT_SESSION_FINISHED_IT2ME: 'home.client.session-finished.it2me',
42 CLIENT_SESSION_FINISHED_ME2ME: 'home.client.session-finished.me2me',
43 CLIENT_HOST_NEEDS_UPGRADE: 'home.client.host-needs-upgrade',
44 HISTORY: 'home.history',
45 CONFIRM_HOST_DELETE: 'home.confirm-host-delete',
46 HOST_SETUP: 'home.host-setup',
47 HOST_SETUP_ASK_PIN: 'home.host-setup.ask-pin',
48 HOST_SETUP_PROCESSING: 'home.host-setup.processing',
49 HOST_SETUP_DONE: 'home.host-setup.done',
50 HOST_SETUP_ERROR: 'home.host-setup.error',
51 HOME_MANAGE_PAIRINGS: 'home.manage-pairings',
52 IN_SESSION: 'in-session'
53 };
54
55 /** @const */
56 remoting.kIT2MeVisitedStorageKey = 'it2me-visited';
57 /** @const */
58 remoting.kMe2MeVisitedStorageKey = 'me2me-visited';
59
60 /**
61 * @param {Element} element The element to check.
62 * @param {string} attrName The attribute on the element to check.
63 * @param {Array<string>} modes The modes to check for.
64 * @return {boolean} True if any mode in |modes| is found within the attribute.
65 */
66 remoting.hasModeAttribute = function(element, attrName, modes) {
67 var attr = element.getAttribute(attrName);
68 for (var i = 0; i < modes.length; ++i) {
69 if (attr.match(new RegExp('(\\s|^)' + modes[i] + '(\\s|$)')) != null) {
70 return true;
71 }
72 }
73 return false;
74 };
75
76 /**
77 * Update the DOM by showing or hiding elements based on whether or not they
78 * have an attribute matching the specified name.
79 * @param {string} mode The value against which to match the attribute.
80 * @param {string} attr The attribute name to match.
81 * @return {void} Nothing.
82 */
83 remoting.updateModalUi = function(mode, attr) {
84 var modes = mode.split('.');
85 for (var i = 1; i < modes.length; ++i)
86 modes[i] = modes[i - 1] + '.' + modes[i];
87 var elements = document.querySelectorAll('[' + attr + ']');
88 // Hide elements first so that we don't end up trying to show two modal
89 // dialogs at once (which would break keyboard-navigation confinement).
90 for (var i = 0; i < elements.length; ++i) {
91 var element = /** @type {Element} */ (elements[i]);
92 if (!remoting.hasModeAttribute(element, attr, modes)) {
93 element.hidden = true;
94 }
95 }
96 for (var i = 0; i < elements.length; ++i) {
97 var element = /** @type {Element} */ (elements[i]);
98 if (remoting.hasModeAttribute(element, attr, modes)) {
99 element.hidden = false;
100 var autofocusNode = element.querySelector('[autofocus]');
101 if (autofocusNode) {
102 autofocusNode.focus();
103 }
104 }
105 }
106 };
107
108 /**
109 * @type {remoting.AppMode} The current app mode
110 */
111 remoting.currentMode = remoting.AppMode.HOME;
112
113 /**
114 * Change the app's modal state to |mode|, determined by the data-ui-mode
115 * attribute.
116 *
117 * @param {remoting.AppMode} mode The new modal state.
118 */
119 remoting.setMode = function(mode) {
120 remoting.updateModalUi(mode, 'data-ui-mode');
121 console.log('App mode: ' + mode);
122 remoting.currentMode = mode;
123 if (mode !== remoting.AppMode.IN_SESSION) {
124 // TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772
125 // is fixed.
126 var scroller = document.getElementById('scroller');
127 if (scroller) {
128 scroller.classList.remove('no-horizontal-scroll');
129 scroller.classList.remove('no-vertical-scroll');
130 }
131 }
132
133 remoting.testEvents.raiseEvent(remoting.testEvents.Names.uiModeChanged, mode);
134 };
135
136 /**
137 * Get the major mode that the app is running in.
138 * @return {string} The app's current major mode.
139 */
140 remoting.getMajorMode = function() {
141 return remoting.currentMode.split('.')[0];
142 };
143
144 /**
145 * Helper function for showing or hiding the infographic UI based on
146 * whether or not the user has already dismissed it.
147 *
148 * @param {string} mode
149 * @param {Object<?,string>} items
150 */
151 remoting.showOrHideCallback = function(mode, items) {
152 // Get the first element of a dictionary or array, without needing to know
153 // the key.
154 var obj = /** @type {!Object} */(items);
155 /** @type {string} */
156 var key = Object.keys(obj)[0];
157 var visited = !!items[key];
158 document.getElementById(mode + '-first-run').hidden = visited;
159 document.getElementById(mode + '-content').hidden = !visited;
160 };
161
162 /**
163 * @param {Object<?,string>} items
164 */
165 remoting.showOrHideCallbackIT2Me = function(items) {
166 remoting.showOrHideCallback('it2me', items);
167 }
168
169 /**
170 * @param {Object<?,string>} items
171 */
172 remoting.showOrHideCallbackMe2Me = function(items) {
173 remoting.showOrHideCallback('me2me', items);
174 }
175
176 remoting.showOrHideIT2MeUi = function() {
177 chrome.storage.local.get(remoting.kIT2MeVisitedStorageKey,
178 remoting.showOrHideCallbackIT2Me);
179 };
180
181 remoting.showOrHideMe2MeUi = function() {
182 chrome.storage.local.get(remoting.kMe2MeVisitedStorageKey,
183 remoting.showOrHideCallbackMe2Me);
184 };
185
186 remoting.showIT2MeUiAndSave = function() {
187 var items = {};
188 items[remoting.kIT2MeVisitedStorageKey] = true;
189 chrome.storage.local.set(items);
190 remoting.showOrHideCallback('it2me', [true]);
191 };
192
193 remoting.showMe2MeUiAndSave = function() {
194 var items = {};
195 items[remoting.kMe2MeVisitedStorageKey] = true;
196 chrome.storage.local.set(items);
197 remoting.showOrHideCallback('me2me', [true]);
198 };
199
200 remoting.resetInfographics = function() {
201 chrome.storage.local.remove(remoting.kIT2MeVisitedStorageKey);
202 chrome.storage.local.remove(remoting.kMe2MeVisitedStorageKey);
203 remoting.showOrHideCallback('it2me', [false]);
204 remoting.showOrHideCallback('me2me', [false]);
205 }
206
207
208 /**
209 * Initialize all modal dialogs (class kd-modaldialog), adding event handlers
210 * to confine keyboard navigation to child controls of the dialog when it is
211 * shown and restore keyboard navigation when it is hidden.
212 */
213 remoting.initModalDialogs = function() {
214 var dialogs = document.querySelectorAll('.kd-modaldialog');
215 var observer = new MutationObserver(confineOrRestoreFocus_);
216 var options = /** @type {MutationObserverInit} */({
217 subtree: false,
218 attributes: true
219 });
220 for (var i = 0; i < dialogs.length; ++i) {
221 observer.observe(dialogs[i], options);
222 }
223 };
224
225 /**
226 * @param {Array<MutationRecord>} mutations The set of mutations affecting
227 * an observed node.
228 */
229 function confineOrRestoreFocus_(mutations) {
230 // The list of mutations can include duplicates, so reduce it to a canonical
231 // show/hide list.
232 /** @type {Array<Node>} */
233 var shown = [];
234 /** @type {Array<Node>} */
235 var hidden = [];
236 for (var i = 0; i < mutations.length; ++i) {
237 var mutation = mutations[i];
238 if (mutation.type == 'attributes' &&
239 mutation.attributeName == 'hidden') {
240 var node = mutation.target;
241 if (node.hidden && hidden.indexOf(node) == -1) {
242 hidden.push(node);
243 } else if (!node.hidden && shown.indexOf(node) == -1) {
244 shown.push(node);
245 }
246 }
247 }
248 var kSavedAttributeName = 'data-saved-tab-index';
249 // If any dialogs have been dismissed, restore all the tabIndex attributes.
250 if (hidden.length != 0) {
251 var elements = document.querySelectorAll('[' + kSavedAttributeName + ']');
252 for (var i = 0 ; i < elements.length; ++i) {
253 var element = /** @type {Element} */ (elements[i]);
254 element.tabIndex = element.getAttribute(kSavedAttributeName);
255 element.removeAttribute(kSavedAttributeName);
256 }
257 }
258 // If any dialogs have been shown, confine keyboard navigation to the first
259 // one. We don't have nested modal dialogs, so this will suffice for now.
260 if (shown.length != 0) {
261 var selector = '[tabIndex],a,area,button,input,select,textarea';
262 var disable = document.querySelectorAll(selector);
263 var except = shown[0].querySelectorAll(selector);
264 for (var i = 0; i < disable.length; ++i) {
265 var element = /** @type {Element} */ (disable[i]);
266 var removeFromKeyboardNavigation = true;
267 for (var j = 0; j < except.length; ++j) { // No indexOf on NodeList
268 if (element == except[j]) {
269 removeFromKeyboardNavigation = false;
270 break;
271 }
272 }
273 if (removeFromKeyboardNavigation) {
274 element.setAttribute(kSavedAttributeName, element.tabIndex);
275 element.tabIndex = -1;
276 }
277 }
278 }
279 }
280
281 /**
282 * @param {string} tag
283 */
284 remoting.showSetupProcessingMessage = function(tag) {
285 var messageDiv = document.getElementById('host-setup-processing-message');
286 l10n.localizeElementFromTag(messageDiv, tag);
287 remoting.setMode(remoting.AppMode.HOST_SETUP_PROCESSING);
288 }
OLDNEW
« no previous file with comments | « remoting/webapp/crd/js/typecheck_unittest.js ('k') | remoting/webapp/crd/js/wcs_adapter.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698