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

Side by Side Diff: chrome/common/extensions/docs/examples/apps/cycler/cycler.js

Issue 10832191: Major revision of page cycler UI. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Major revision to cycler UI Created 8 years, 4 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
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 function $(criterion) {
6 return document.querySelector(criterion);
7 }
8
9 var cyclerUI = new (function () {
Jeffrey Yasskin 2012/08/09 19:27:04 Do you ever use 'cyclerUI'? I'm not a JS expert, b
clintstaley 2012/08/10 00:33:13 I'm fairly new to it myself, and am just imitating
10
11 /**
12 * Note to reviewers: This is a (perhaps excessively?) fancy
Jeffrey Yasskin 2012/08/09 19:27:04 Grammar-o: "This is a fancy these functions into .
clintstaley 2012/08/10 00:33:13 Done.
13 * these functions into the cyclerUI object wouldn't
14 * group them so nicely, but would also avoid a lot of 'bind' calls. I'm
15 * still sorting out JS design, and would value your opinion on this.
Jeffrey Yasskin 2012/08/09 19:27:04 I'm not at all an expert on JS design, but it seem
clintstaley 2012/08/10 00:33:13 Yeah, this is what I was worried about, too. If y
16 *
17 * Enum for different UI states, showing variously a capture page,
18 * a page for choosing a playback, one for showing an already-chosen
19 * playback, and one for indicating that no playback choices yet exist.
20 * @enum {{set: !function()}}
21 * @private
Jeffrey Yasskin 2012/08/09 19:27:04 I think being a local variable in the object closu
clintstaley 2012/08/10 00:33:13 It surely does. I was thinking of the @private as
22 */
23 var EnableState_ = {
24 capture: {
25 set:
26 function() {
27 if (this.enableState != EnableState_.capture) {
28 this.enableState = EnableState_.capture;
29 this.enableTab_(this.captureTabLabel, this.captureTab);
30 this.captureTab.hidden = false;
31 this.playbackTab.hidden = true;
32 }
33 }
34 },
35 choosePlayback: {
36 set:
37 function() {
38 if (this.enableState != EnableState_.choosePlayback) {
39 this.enableState = EnableState_.choosePlayback;
40 this.enableTab_(this.playbackTabLabel, this.playbackTab);
41 this.captureTab.hidden = true;
42 this.playbackTab.hidden = false;
43 this.noCaptures.hidden = true;
44 this.yesCaptures.hidden = false;
45 this.playbackDetails.hidden = true;
46 }
47 }
48 },
49 showPlayback: {
50 set:
51 function() {
52 if (this.enableState != EnableState_.showPlayback) {
53 this.enableState = EnableState_.showPlayback;
54 this.enableTab_(this.playbackTabLabel, this.playbackTab);
55 this.captureTab.hidden = true;
56 this.playbackTab.hidden = false;
57 this.noCaptures.hidden = true;
58 this.yesCaptures.hidden = false;
59 this.playbackDetails.hidden = false;
60 }
61 }
62 },
63 showNoCaptures: {
64 set:
65 function() {
66 if (this.enableState != EnableState_.showNoCaptures) {
67 this.enableState = EnableState_.showNoCaptures;
68 this.enableTab_(this.playbackTabLabel, this.playbackTab);
69 this.captureTab.hidden = true;
70 this.playbackTab.hidden = false;
71 this.noCaptures.hidden = false;
72 this.yesCaptures.hidden = true;
73 }
74 }
75 }
76 };
77
78 // Members for all UI elements subject to programmatic adjustment.
79 this.captureTab = $('#capture-tab');
80 this.captureTabLabel = $('#capture-tab-label');
81 this.captureName = $('#capture-name');
82 this.captureURLs = $('#capture-urls');
83 this.doCaptureButton = $('#do-capture');
84
85 this.playbackTab = $('#playback-tab');
86 this.noCaptures = $('#no-captures');
87 this.yesCaptures = $('#yes-captures');
88
89 this.playbackTabLabel = $('#playback-tab-label');
90 this.playbackName = $('#playback-name');
91 this.playbackDetails = $('#playback-details');
92 this.playbackURLs = $('#playback-urls');
93 this.playbackRepeats = $('#playback-repeats');
94 this.playbackExtension = $('#playback-extension');
95 this.doPlaybackButton = $('#do-playback');
96 this.doDeleteButton = $('#do-delete');
97
98 this.popupDialog = $('#popup');
99 this.popupContent = $('#popup-content');
100 this.doPopupDismiss = $('#do-popup-dismiss');
101
102 // currentCaptureName_ is most recently done capture, or most recently
103 // selected stored capture.
104 this.currentCaptureName_ = null;
105
106 /**
107 * Enable the tab indciated by |tabLabel| and |tab|, the HTML elements
Jeffrey Yasskin 2012/08/09 19:27:04 sp: indciated
clintstaley 2012/08/10 00:33:13 Done.
108 * showing the selection-label for the tab, and the tab itself, respectively.
109 * @param {!HTMLDivElement} tabLabel The <div> for the label of the
110 * tab to enable.
111 * @param {!HTMLDivElement} tab The <div> for the tab itself.
112 * @private
113 */
114 this.enableTab_ = function(tabLabel, tab) {
115 var tabList = document.querySelectorAll('.tab');
116 var tabLabelList = document.querySelectorAll('.tab-label');
117
118 for (var i = 0; i < tabList.length; i++)
119 tabList[i].hidden = tabList[i] != tab;
120
121 for (var i = 0; i < tabLabelList.length; i++)
122 if (tabLabelList[i] == tabLabel) {
123 tabLabelList[i].classList.add('select');
124 } else {
125 tabLabelList[i].classList.remove('select');
126 }
127 }
128
129 /**
130 * Show an overlay with a message, a dismiss button with configurable
131 * label, and an action to call upon dismissal.
132 * @param {!string} content The message to display.
133 * @param {!string} dismissLabel The label on the dismiss button.
134 * @param {function()} action Additional action to take, if any, upon
135 * dismissal.
136 * @private
137 */
138 this.showMessage_ = function(content, dismissLabel, action) {
139 this.popupContent.innerText = content;
140 this.doPopupDismiss.innerText = dismissLabel;
141 this.popupDialog.hidden = false;
142 if (action != null)
143 doPopupDismiss.addEventListener('click', action);
144 }
145
146 /**
147 * Default action for popup dismissal button, performed in addition to
148 * any other actions that may be specified in showMessage_ call.
149 * @private
150 */
151 this.clearMessage_ = function() {
152 this.popupDialog.hidden = true;
153 }
154
155 /* Mockup of capture saving system. Sets up a simple array of known
156 * captures, and adjusts that array. This code will all be replaced
157 * in the next CL.
158 */
159 this.currentCaptures = ['alpha', 'beta', 'gamma'];
160
161 /**
162 * Save a capture from a specified cache directory on the browser side,
163 * storing it in HTML side FileSystem storage. Update the capture choices
164 * HTML select element.
165 * @param {!string} name The name of the capture.
166 * @param {!Array.<!string>} urlList List of URLs to capture.
167 * @param {!string} directory The cache directory name.
168 */
169 this.saveCapture = function(name, urlList, directory) {
170 console.log('Saving capture ' + name + ' on ' + urlList +
171 ' from browserside directory ' + directory);
172 this.currentCaptures.push(name);
173 }
174
175 /**
176 * Return a list of currently stored captures in the local FileSystem.
177 * @return {Array.<!string>} Names of all the current captures.
178 */
179 this.getCaptures = function() {
180 return this.currentCaptures;
181 }
182
183 /**
184 * Delete capture |name| from the local FileSystem, and update the
Jeffrey Yasskin 2012/08/09 19:27:04 Does this actually delete from the filesystem?
clintstaley 2012/08/10 00:33:13 No, like the comment says above, it's a mockup for
185 * capture choices HTML select element.
186 * @param {!string} name The name of the capture to delete.
187 */
188 this.deleteCapture = function(name) {
189 this.currentCaptures.splice(this.currentCaptures.indexOf(name), 1);
190 this.updatePlaybackChoices_();
191 }
192
193 /* End capture saving mockup */
194
195 /**
196 * Do a capture using current data from the capture tab. Post an error
197 * dialog if said data is incorrect or incomplete. Otherwise pass
198 * control to the browser side.
199 * @private
200 */
201 this.doCapture_ = function() {
202 var errors = [];
203 var captureName = this.captureName.value.trim();
204 var urlList;
205
206 urlList = this.captureURLs.value.split('\n');
207 if (captureName.length == 0)
208 errors.push('Must give a capture name');
209 if (urlList.length == 0)
210 errors.push('Must give at least one URL');
211
212 if (errors.length > 0) {
213 this.showMessage_(errors.join('\n'), 'Ok');
214 } else {
215 this.doCaptureButton.disabled = true;
216 chrome.experimental.record.captureURLs(captureName, urlList,
Jeffrey Yasskin 2012/08/09 19:27:04 Can captureURLs throw exceptions that need to be e
clintstaley 2012/08/10 00:33:13 Not across an extension API boundary that I know o
217 this.onCaptureDone.bind(this));
218 }
219 }
220
221 /**
222 * Delete the capture with name |currentCaptureName_|. For this method
223 * to be callable, there must be a selected currentCaptureName_. Change
224 * the displayed HTML to show no captures if none exist now, or to show
225 * none selected (since we just deleted the selected one).
226 * @private
227 */
228 this.doDelete_ = function() {
229 this.deleteCapture(this.currentCaptureName_);
230 this.currentCaptureName_ = null;
231 this.updatePlaybackChoices_();
232 if (this.getCaptures().length == 0) {
233 EnableState_.showNoCaptures.set.bind(this)();
234 } else {
235 EnableState_.choosePlayback.set.bind(this)();
236 }
237 }
238
239 /**
240 * Callback for completed (or possibly failed) capture. Post a message
241 * box, either with errors or "Success!" message. Save the capture cache
242 * directory in the event of success.
243 * @param {!Array.<string>} errors List of errors that occured
244 * during capture, if any.
245 * @param {!string} cacheDirectory Cache directory name for successful
246 * capture.
247 * @private
248 */
249 this.onCaptureDone = function(errors, cacheDirectory) {
250 this.doCaptureButton.disabled = false;
251
252 if (errors.length > 0) {
253 this.showMessage_(errors.join('\n'), 'Ok');
254 } else {
255 this.showMessage_('Success!', 'Ok');
256 this.currentCaptureName_ = this.captureName.value.trim();
257 this.saveCapture(this.currentCaptureName_, cacheDirectory);
258 this.updatePlaybackChoices_();
259 }
260 }
261
262 /**
263 * Display the playback tab, showing either its "no captures", "choose
264 * a capture" or "display chosen capture" form, depending on the state
265 * of existing captures and |currentCaptureName_|.
266 * @private
267 */
268 this.displayPlayback = function() {
269 if (this.getCaptures().length == 0) {
270 EnableState_.showNoCaptures.set.bind(this)();
271 } else if (this.currentCaptureName_ == null) {
272 EnableState_.choosePlayback.set.bind(this)();
273 } else {
274 EnableState_.showPlayback.set.bind(this)();
275 }
276 }
277
278 /**
279 * Utility function to refresh the selection list of captures that may
280 * be chosen for playback. Show all current captures, and also a
281 * "Choose a capture" default text if no capture is currently selected.
282 * @private
283 */
284 this.updatePlaybackChoices_ = function() {
285 var captureNames = this.getCaptures();
286 var options = this.playbackName.options;
287 var nextIndex = 0;
288
289 options.length = 0;
290
291 if (this.currentCaptureName_ == null) {
292 options.add(new Option('Choose a capture', null));
293 options.selectedIndex = nextIndex++;
294 }
295 for (var i = 0; i < captureNames.length; i++) {
296 options.add(new Option(captureNames[i], captureNames[i]));
297 if (captureNames[i] == this.currentCaptureName_) {
298 options.selectedIndex = nextIndex;
299 }
300 nextIndex++;
301 }
302 }
303
304 /**
305 * Event callback for selection of a capture to play back. Save the
306 * choice in |currentCaptureName_|. Update the selection list because the
307 * default reminder message is no longer needed when a capture is chosen.
308 * Change playback tab to show-chosen-capture mode.
309 */
310 this.selectPlaybackName = function() {
311 this.currentCaptureName_ = this.playbackName.value;
312 this.updatePlaybackChoices_();
313 EnableState_.showPlayback.set.bind(this)();
314 }
315
316 /**
317 * Event callback for pressing the playback button, which button is only
318 * enabled if a capture has been chosen for playback. Check for errors
319 * on playback page, and either display a message with such errors, or
320 * call the extenion api to initiate a playback.
321 */
322 this.doPlayback_ = function() {
323 var extensionPath = this.playbackExtension.value;
324 var repeatCount = parseInt(this.playbackRepeats.value);
325 var errors = [];
326
327 // Check local errors
328 if (isNaN(repeatCount)) {
329 errors.push('Enter a number for repeat count');
330 } else if (repeatCount < 1 || repeatCount > 100) {
331 errors.push('Repeat count must be between 1 and 100');
332 }
333
334 if (errors.length > 0) {
335 this.showMessage_(errors.join('\n'), 'Ok');
336 } else {
337 this.doPlaybackButton.disabled = true;
338 chrome.experimental.record.replayURLs(
339 this.currentCaptureName_,
340 repeatCount,
341 {'extensionPath': extensionPath},
342 this.onPlaybackDone.bind(this));
343 }
344 }
345
346 /**
347 * Extension API calls this back when a playback is done.
348 * @param {!number} runtime Number of ms it took to run the playback.
349 * @param {!string} stats Statistics from the playback.
350 * @param {!Array.<!string>} errors List of error messages, if any,
351 * resuting from the playback.
352 */
353 this.onPlaybackDone = function(runTime, stats, errors) {
354 this.doPlaybackButton.disabled = false;
355
356 if (errors.length > 0) {
357 this.showMessage_(errors.join('\n'), 'Ok');
358 } else {
359 this.showMessage_('Test took ' + runTime + 'mS :\n' + stats, 'Ok');
Jeffrey Yasskin 2012/08/09 19:27:04 I understand this is just the initial version, but
clintstaley 2012/08/10 00:33:13 The design of the C++ side of this is a whole noth
360 }
361 }
362
363 // Set up listeners on all buttons.
364 this.doCaptureButton.addEventListener('click', this.doCapture_.bind(this));
365 this.doPlaybackButton.addEventListener('click', this.doPlayback_.bind(this));
366 this.doDeleteButton.addEventListener('click', this.doDelete_.bind(this));
367 this.doPopupDismiss.addEventListener('click', this.clearMessage_.bind(this));
368
369 // Set up listeners on tab labels.
370 this.captureTabLabel.addEventListener('click',
371 EnableState_.capture.set.bind(this));
372 this.playbackTabLabel.addEventListener('click',
373 this.displayPlayback.bind(this));
374
375 // Set up initial selection list for existing captures, and selection
376 // event listener.
377 this.updatePlaybackChoices_();
378 this.playbackName.addEventListener('change',
379 this.selectPlaybackName.bind(this));
380
381 // Start with capture tab displayed.
382 EnableState_.capture.set.bind(this)();
383 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698