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

Side by Side Diff: chrome/browser/resources/downloads.html

Issue 20110: Initial checkin of the HTML downloads UI. It's not yet complete (lacking... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 10 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 <!DOCTYPE HTML>
2 <html id="t">
3 <head>
4 <meta charset="utf-8">
5 <title jscontent="title"></title>
6 <style type="text/css">
7 body {
8 font-family:arial;
9 background-color:white;
10 color:black;
11 font-size:84%;
12 margin:10px;
13 }
14 .header {
15 overflow:auto;
16 clear:both;
17 }
18 .header .logo {
19 float:left;
20 }
21 .header .form {
22 float:left;
23 margin-top:22px;
24 margin-left:12px;
25 }
26 #downloads-summary {
27 margin-top:12px;
28 border-top:1px solid #9cc2ef;
29 background-color:#ebeff9;
30 font-weight:bold;
31 padding:3px;
32 margin-bottom:6px;
33 }
34 #downloads-display {
35 max-width:740px;
36 }
37 .download {
38 position:relative;
39 padding-left:45px;
40 margin-bottom:16px;
41 }
42 .download .icon {
43 position:absolute;
44 top:2px;
45 left:2px;
46 width:32px;
47 height:32px;
48 }
49 .name {
50 display:none;
51 padding-right:16px;
52 max-width:450px;
53 text-overflow:ellipsis;
54 }
55 .download .status {
56 display:inline;
57 color:#999;
58 }
59 .download .url {
60 color:#080;
61 width:500px;
62 white-space:nowrap;
63 overflow:hidden;
64 text-overflow:ellipsis;
65 }
66 .controls a {
67 color:#777;
68 }
69 #downloads-pagination {
70 padding-top:24px;
71 margin-left:18px;
72 }
73 .page-navigation {
74 padding:8px;
75 background-color:#ebeff9;
76 margin-right:4px;
77 }
78 .footer {
79 height:24px;
80 }
81 </style>
82 <script type="text/javascript">
83 ///////////////////////////////////////////////////////////////////////////////
84 // localStrings:
85 /**
86 * We get strings into the page by using JSTemplate to populate some elements
87 * with localized content, then reading the content of those elements into
88 * this global strings object.
89 * @param {Node} node The DOM node containing all our strings.
90 */
91 function LocalStrings(node) {
92 this.strings_ = {};
93
94 var children = node.childNodes;
95 for (var i = 0, child; child = children[i]; i++) {
96 var id = child.id;
97 if (id) {
98 this.strings_[id] = child.innerHTML;
99 }
100 }
101 }
102
103 /**
104 * Gets a localized string by its id.
105 * @param {string} s The id of the string we want
106 * @return {string} The localized string
107 */
108 LocalStrings.prototype.getString = function(s) {
109 return (s in this.strings_) ? this.strings_[s] : '';
110 }
111
112 /**
113 * Returns a formatted localized string (where all %s contents are replaced
114 * by the second argument).
115 * @param {string} s The id of the string we want
116 * @param {string} d The string to include in the formatted string
117 * @return {string} The formatted string.
118 */
119 LocalStrings.prototype.formatString = function(s, d) {
120 return (s in this.strings_) ? this.strings_[s].replace(/\%s/, d) : '';
121 }
122
123 ///////////////////////////////////////////////////////////////////////////////
124 // Helper functions
125 function $(o) {return document.getElementById(o);}
126
127 function bind(fn, selfObj, var_args) {
128 var boundArgs = Array.prototype.slice.call(arguments, 2);
129 return function() {
130 var args = Array.prototype.slice.call(arguments);
131 args.unshift.apply(args, boundArgs);
132 return fn.apply(selfObj, args);
133 }
134 }
135
136 /**
137 * Sets the display style of a node.
138 */
139 function showInline(node, isShow) {
140 node.style.display = isShow ? 'inline' : 'none';
141 }
142
143 /**
144 * Creates an element of a specified type with a specified class name.
145 * @param {String} type The node type.
146 * @param {String} className The class name to use.
147 */
148 function createElementWithClassName(type, className) {
149 var elm = document.createElement(type);
150 elm.className = className;
151 return elm;
152 }
153
154 /**
155 * Creates a link with a specified onclick handler and content
156 * @param {String} onclick The onclick handler
157 * @param {String} value The link text
158 */
159 function createLink(onclick, value) {
160 var link = document.createElement("a");
161 link.onclick = onclick;
162 link.href = '#';
163 link.innerHTML = value;
164 return link;
165 }
166
167 /**
168 * Creates a button with a specified onclick handler and content
169 * @param {String} onclick The onclick handler
170 * @param {String} value The button text
171 */
172 function createButton(onclick, value) {
173 var button = document.createElement("input");
174 button.type = 'button';
175 button.value = value;
176 button.onclick = onclick;
177 return button;
178 }
179
180 ///////////////////////////////////////////////////////////////////////////////
181 // Downloads
182 /**
183 * Class to hold all the information about the visible downloads.
184 */
185 function Downloads() {
186 this.downloads_ = {};
187 this.node_ = $('downloads-display');
188 this.summary_ = $('downloads-summary');
189 this.searchText_ = "";
190 }
191
192 /**
193 * Called when a download has been updated or added.
194 * @param {Object} download A backend download object (see downloads_ui.cc)
195 */
196 Downloads.prototype.updated = function(download) {
197 var id = download.id;
198 if (!!this.downloads_[id]) {
199 this.downloads_[id].update(download);
200 } else {
201 this.downloads_[id] = new Download(download);
202 // We get downloads in display order, so we don't have to worry about
203 // maintaining correct order for now.
204 this.node_.insertBefore(this.downloads_[id].node,
205 this.node_.firstChild);
206 }
207 }
208
209 /**
210 * Set our display search text.
211 * @param {String} searchText The string we're searching for.
212 */
213 Downloads.prototype.setSearchText = function(searchText) {
214 this.searchText_ = searchText;
215 }
216
217 /**
218 * Update the summary block above the results
219 */
220 Downloads.prototype.updateSummary = function() {
221 if (this.searchText_) {
222 this.summary_.innerHTML = localStrings.formatString(
223 'searchresultsfor', this.searchText_);
224 } else {
225 this.summary_.innerHTML = localStrings.getString('downloads');
226 }
227
228 var hasDownloads = false;
229 for (var i in this.downloads_) {
230 hasDownloads = true;
231 break;
232 }
233
234 if (!hasDownloads) {
235 this.node_.innerHTML = localStrings.getString('noresults');
236 }
237 }
238
239 /**
240 * Remove a download.
241 * @param {Number} id The id of the download to remove.
242 */
243 Downloads.prototype.remove = function(id) {
244 this.node_.removeChild(this.downloads_[id].node);
245 delete this.downloads_[id];
246 }
247
248 /**
249 * Clear all downloads and reset us back to a null state.
250 */
251 Downloads.prototype.clear = function() {
252 for (var id in this.downloads_) {
253 this.downloads_[id].clear();
254 this.remove(id);
255 }
256 }
257
258 ///////////////////////////////////////////////////////////////////////////////
259 // Download
260 /**
261 * A download and the DOM representation for that download.
262 * @param {Object} download A backend download object (see downloads_ui.cc)
263 */
264 function Download(download) {
265 // Create DOM
266 this.node = createElementWithClassName('div', 'download');
267
268 // Container for all 'safe download' UI.
269 this.safe_ = document.createElement("div");
270 this.safe_.ondragstart = bind(this.drag_, this);
271 this.node.appendChild(this.safe_);
272
273 this.nodeImg_ = createElementWithClassName('img', 'icon');
274 this.safe_.appendChild(this.nodeImg_);
275
276 // FileLink is used for completed downloads, otherwise we show FileName.
277 this.nodeTitleArea_ = createElementWithClassName('div', 'title-area');
278 this.safe_.appendChild(this.nodeTitleArea_);
279
280 this.nodeFileLink_ = createLink(bind(this.openFile_, this), '');
281 this.nodeFileLink_.className = 'name';
282 this.nodeFileLink_.style.display = 'none';
283 this.nodeTitleArea_.appendChild(this.nodeFileLink_);
284
285 this.nodeFileName_ = createElementWithClassName('span', 'name');
286 this.nodeFileName_.style.display = 'none';
287 this.nodeTitleArea_.appendChild(this.nodeFileName_);
288
289 this.nodeStatus_ = createElementWithClassName('span', 'status');
290 this.nodeTitleArea_.appendChild(this.nodeStatus_);
291
292 this.nodeURL_ = createElementWithClassName('div', 'url');
293 this.safe_.appendChild(this.nodeURL_);
294
295 // Controls.
296 this.nodeControls_ = createElementWithClassName('div', 'controls');
297 this.safe_.appendChild(this.nodeControls_);
298
299 this.controlShow_ = createLink(bind(this.show_, this),
300 localStrings.getString('control_showinfolder'));
301 this.nodeControls_.appendChild(this.controlShow_);
302
303 this.controlPause_ = createLink(bind(this.pause_, this),
304 localStrings.getString('control_pause'));
305 this.nodeControls_.appendChild(this.controlPause_);
306
307 this.controlCancel_ = createLink(bind(this.cancel_, this),
308 localStrings.getString('control_cancel'));
309 this.nodeControls_.appendChild(this.controlCancel_);
310
311 // Container for 'unsafe download' UI.
312 this.danger_ = createElementWithClassName('div', 'show-dangerous');
313 this.node.appendChild(this.danger_);
314
315 this.dangerDesc_ = document.createElement("div");
316 this.danger_.appendChild(this.dangerDesc_);
317
318 this.dangerSave_ = createButton(bind(this.saveDangerous_, this),
319 localStrings.getString("danger_save"));
320 this.danger_.appendChild(this.dangerSave_);
321
322 this.dangerDiscard_ = createButton(bind(this.discardDangerous_, this),
323 localStrings.getString("danger_discard"));
324 this.danger_.appendChild(this.dangerDiscard_);
325
326 // Update member vars.
327 this.update(download);
328 }
329
330 /**
331 * The states a download can be in. These correspond to states defined in
332 * DownloadsDOMHandler::CreateDownloadItemValue
333 */
334 Download.States = {
335 IN_PROGRESS : "IN_PROGRESS",
336 CANCELLED : "CANCELLED",
337 COMPLETE : "COMPLETE",
338 PAUSED : "PAUSED",
339 DANGEROUS : "DANGEROUS",
340 }
341
342 /**
343 * Updates the download to reflect new data.
344 * @param {Object} download A backend download object (see downloads_ui.cc)
345 */
346 Download.prototype.update = function(download) {
347 this.id_ = download.id;
348 this.filePath_ = download.file_path;
349 this.fileName_ = download.file_name;
350 this.url_ = download.url;
351 this.state_ = download.state;
352 this.percent_ = download.percent;
353 this.speed_ = download.speed;
354 this.received_ = download.received;
355
356 if (this.state_ == Download.States.DANGEROUS) {
357 this.dangerDesc_.innerHTML = localStrings.formatString('danger_desc',
358 this.fileName_);
359 this.danger_.style.display = 'block';
360 this.safe_.style.display = 'none';
361 } else {
362 this.nodeImg_.src = 'chrome-ui://fileicon/' + this.filePath_;
363
364 if (this.state_ == Download.States.COMPLETE) {
365 this.nodeFileLink_.innerHTML = this.fileName_;
366 this.nodeFileLink_.href = this.filePath_;
367 } else {
368 this.nodeFileName_.innerHTML = this.fileName_;
369 }
370
371 showInline(this.nodeFileLink_, this.state_ == Download.States.COMPLETE);
372 showInline(this.nodeFileName_, this.state_ != Download.States.COMPLETE);
373
374 showInline(this.controlShow_, this.state_ == Download.States.COMPLETE);
375 showInline(this.controlPause_, this.state_ == Download.States.IN_PROGRESS);
376 showInline(this.controlCancel_, this.state_ == Download.States.IN_PROGRESS);
377
378 this.nodeURL_.innerHTML = this.url_;
379 this.nodeStatus_.innerHTML = this.getStatusText_();
380
381 this.danger_.style.display = 'none';
382 this.safe_.style.display = 'block';
383 }
384 }
385
386 /**
387 * Removes applicable bits from the DOM in preparation for deletion.
388 */
389 Download.prototype.clear = function() {
390 this.safe_.ondragstart = null;
391 this.nodeFileLink_.onclick = null;
392 this.controlShow_.onclick = null;
393 this.controlCancel_.onclick = null;
394 this.controlPause_.onclick = null;
395 this.dangerDiscard_.onclick = null;
396
397 this.node.innerHTML = '';
398 }
399
400 /**
401 * @return {String} User-visible status update text.
402 */
403 Download.prototype.getStatusText_ = function() {
404 switch (this.state_) {
405 case Download.States.IN_PROGRESS:
406 // TODO(glen): Make pretty, localize, etc.
407 return this.percent_ + '% at ' + this.speed_;
408 case Download.States.CANCELLED:
409 return localStrings.getString('status_cancelled');
410 case Download.States.PAUSED:
411 return localStrings.getString('status_paused');
412 case Download.States.DANGEROUS:
413 return localStrings.getString('danger_desc');
414 case Download.States.COMPLETE:
415 return "";
416 }
417 }
418
419 /**
420 * Tells the backend to initiate a drag, allowing users to drag
421 * files from the download page and have them appear as native file
422 * drags.
423 */
424 Download.prototype.drag_ = function() {
425 chrome.send("drag", [this.id_.toString()]);
426 return false;
427 }
428
429 /**
430 * Tells the backend to open this file.
431 */
432 Download.prototype.openFile_ = function() {
433 chrome.send("openFile", [this.id_.toString()]);
434 return false;
435 }
436
437 /**
438 * Tells the backend that the user chose to save a dangerous file.
439 */
440 Download.prototype.saveDangerous_ = function() {
441 chrome.send("saveDangerous", [this.id_.toString()]);
442 return false;
443 }
444
445 /**
446 * Tells the backend that the user chose to discard a dangerous file.
447 */
448 Download.prototype.discardDangerous_ = function() {
449 chrome.send("discardDangerous", [this.id_.toString()]);
450 downloads.remove(this.id_);
451 return false;
452 }
453
454 /**
455 * Tells the backend to show the file in explorer.
456 */
457 Download.prototype.show_ = function() {
458 chrome.send("show", [this.id_.toString()]);
459 return false;
460 }
461
462 /**
463 * Tells the backend to pause this download.
464 */
465 Download.prototype.pause_ = function() {
466 chrome.send("pause", [this.id_.toString()]);
467 return false;
468 }
469
470 /**
471 * Tells the backend to cancel this download.
472 */
473 Download.prototype.cancel_ = function() {
474 chrome.send("cancel", [this.id_.toString()]);
475 return false;
476 }
477
478 ///////////////////////////////////////////////////////////////////////////////
479 // Page:
480 var downloads, localStrings;
481
482 function load() {
483 localStrings = new LocalStrings($('l10n'));
484 downloads = new Downloads();
485 setSearch("");
486 }
487
488 function setSearch(searchText) {
489 downloads.clear();
490 downloads.setSearchText(searchText);
491 chrome.send("getDownloads", [searchText.toString()]);
492 }
493
494 ///////////////////////////////////////////////////////////////////////////////
495 // Chrome callbacks:
496 /**
497 * Our history system calls this function with results from searches or when
498 * downloads are added or removed.
499 */
500 function downloadsList(results) {
501 downloadUpdated(results);
502 downloads.updateSummary();
503 }
504
505 /**
506 * When a download is updated (progress, state change), this is called.
507 */
508 function downloadUpdated(results) {
509 for (var i = 0, thisResult; thisResult = results[i]; i++) {
510 downloads.updated(thisResult);
511 }
512 }
513 </script>
514 </head>
515 <body onload="load();">
516 <div class="header">
517 <a href="" onclick="setSearch(''); return false;">
518 <img src="../../app/theme/downloads_section.png"
519 width="67" height="67" class="logo" border="0" /></a>
520 <form method="post" action=""
521 onsubmit="setSearch(this.term.value); return false;"
522 class="form">
523 <input type="text" name="term" id="term" />
524 <input type="submit" name="submit" jsvalues="value:searchbutton" />
525 </form>
526 </div>
527 <div class="main">
528 <div id="downloads-summary"></div>
529 <div id="downloads-display"></div>
530 </div>
531 <div class="footer">
532 </div>
533 <div id="l10n" style="display:none;">
534 <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '% s'</span>
535 <span id="no_results" jscontent="no_results">No results found.</span>
536 <span id="downloads" jscontent="downloads">Downloads</span>
537
538 <span id="status_cancelled" jscontent="status_cancelled">Cancelled</span>
539 <span id="status_paused" jscontent="status_paused">Paused</span>
540
541 <span id="danger_desc" jscontent="danger_desc">This is a dangerous file</span>
542 <span id="danger_save" jscontent="danger_save">Save</span>
543 <span id="danger_discard" jscontent="danger_discard">Discard</span>
544
545 <span id="control_pause" jscontent="control_pause">Pause</span>
546 <span id="control_showinfolder" jscontent="control_showinfolder">Show in folde r</span>
547 <span id="control_cancel" jscontent="control_cancel">Cancel</span>
548 <span id="control_resume" jscontent="control_resume">Resume</span>
549 </div>
550 </body>
551 </html>
OLDNEW
« chrome/browser/dom_ui/dom_ui.cc ('K') | « chrome/browser/dom_ui/fileicon_source.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698