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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/downloads.html
===================================================================
--- chrome/browser/resources/downloads.html (revision 0)
+++ chrome/browser/resources/downloads.html (revision 0)
@@ -0,0 +1,551 @@
+<!DOCTYPE HTML>
+<html id="t">
+<head>
+<meta charset="utf-8">
+<title jscontent="title"></title>
+<style type="text/css">
+body {
+ font-family:arial;
+ background-color:white;
+ color:black;
+ font-size:84%;
+ margin:10px;
+}
+.header {
+ overflow:auto;
+ clear:both;
+}
+.header .logo {
+ float:left;
+}
+.header .form {
+ float:left;
+ margin-top:22px;
+ margin-left:12px;
+}
+#downloads-summary {
+ margin-top:12px;
+ border-top:1px solid #9cc2ef;
+ background-color:#ebeff9;
+ font-weight:bold;
+ padding:3px;
+ margin-bottom:6px;
+}
+#downloads-display {
+ max-width:740px;
+}
+.download {
+ position:relative;
+ padding-left:45px;
+ margin-bottom:16px;
+}
+.download .icon {
+ position:absolute;
+ top:2px;
+ left:2px;
+ width:32px;
+ height:32px;
+}
+.name {
+ display:none;
+ padding-right:16px;
+ max-width:450px;
+ text-overflow:ellipsis;
+}
+.download .status {
+ display:inline;
+ color:#999;
+}
+.download .url {
+ color:#080;
+ width:500px;
+ white-space:nowrap;
+ overflow:hidden;
+ text-overflow:ellipsis;
+}
+.controls a {
+ color:#777;
+}
+#downloads-pagination {
+ padding-top:24px;
+ margin-left:18px;
+}
+.page-navigation {
+ padding:8px;
+ background-color:#ebeff9;
+ margin-right:4px;
+}
+.footer {
+ height:24px;
+}
+</style>
+<script type="text/javascript">
+///////////////////////////////////////////////////////////////////////////////
+// localStrings:
+/**
+ * We get strings into the page by using JSTemplate to populate some elements
+ * with localized content, then reading the content of those elements into
+ * this global strings object.
+ * @param {Node} node The DOM node containing all our strings.
+ */
+function LocalStrings(node) {
+ this.strings_ = {};
+
+ var children = node.childNodes;
+ for (var i = 0, child; child = children[i]; i++) {
+ var id = child.id;
+ if (id) {
+ this.strings_[id] = child.innerHTML;
+ }
+ }
+}
+
+/**
+ * Gets a localized string by its id.
+ * @param {string} s The id of the string we want
+ * @return {string} The localized string
+ */
+LocalStrings.prototype.getString = function(s) {
+ return (s in this.strings_) ? this.strings_[s] : '';
+}
+
+/**
+ * Returns a formatted localized string (where all %s contents are replaced
+ * by the second argument).
+ * @param {string} s The id of the string we want
+ * @param {string} d The string to include in the formatted string
+ * @return {string} The formatted string.
+ */
+LocalStrings.prototype.formatString = function(s, d) {
+ return (s in this.strings_) ? this.strings_[s].replace(/\%s/, d) : '';
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper functions
+function $(o) {return document.getElementById(o);}
+
+function bind(fn, selfObj, var_args) {
+ var boundArgs = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift.apply(args, boundArgs);
+ return fn.apply(selfObj, args);
+ }
+}
+
+/**
+ * Sets the display style of a node.
+ */
+function showInline(node, isShow) {
+ node.style.display = isShow ? 'inline' : 'none';
+}
+
+/**
+ * Creates an element of a specified type with a specified class name.
+ * @param {String} type The node type.
+ * @param {String} className The class name to use.
+ */
+function createElementWithClassName(type, className) {
+ var elm = document.createElement(type);
+ elm.className = className;
+ return elm;
+}
+
+/**
+ * Creates a link with a specified onclick handler and content
+ * @param {String} onclick The onclick handler
+ * @param {String} value The link text
+ */
+function createLink(onclick, value) {
+ var link = document.createElement("a");
+ link.onclick = onclick;
+ link.href = '#';
+ link.innerHTML = value;
+ return link;
+}
+
+/**
+ * Creates a button with a specified onclick handler and content
+ * @param {String} onclick The onclick handler
+ * @param {String} value The button text
+ */
+function createButton(onclick, value) {
+ var button = document.createElement("input");
+ button.type = 'button';
+ button.value = value;
+ button.onclick = onclick;
+ return button;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Downloads
+/**
+ * Class to hold all the information about the visible downloads.
+ */
+function Downloads() {
+ this.downloads_ = {};
+ this.node_ = $('downloads-display');
+ this.summary_ = $('downloads-summary');
+ this.searchText_ = "";
+}
+
+/**
+ * Called when a download has been updated or added.
+ * @param {Object} download A backend download object (see downloads_ui.cc)
+ */
+Downloads.prototype.updated = function(download) {
+ var id = download.id;
+ if (!!this.downloads_[id]) {
+ this.downloads_[id].update(download);
+ } else {
+ this.downloads_[id] = new Download(download);
+ // We get downloads in display order, so we don't have to worry about
+ // maintaining correct order for now.
+ this.node_.insertBefore(this.downloads_[id].node,
+ this.node_.firstChild);
+ }
+}
+
+/**
+ * Set our display search text.
+ * @param {String} searchText The string we're searching for.
+ */
+Downloads.prototype.setSearchText = function(searchText) {
+ this.searchText_ = searchText;
+}
+
+/**
+ * Update the summary block above the results
+ */
+Downloads.prototype.updateSummary = function() {
+ if (this.searchText_) {
+ this.summary_.innerHTML = localStrings.formatString(
+ 'searchresultsfor', this.searchText_);
+ } else {
+ this.summary_.innerHTML = localStrings.getString('downloads');
+ }
+
+ var hasDownloads = false;
+ for (var i in this.downloads_) {
+ hasDownloads = true;
+ break;
+ }
+
+ if (!hasDownloads) {
+ this.node_.innerHTML = localStrings.getString('noresults');
+ }
+}
+
+/**
+ * Remove a download.
+ * @param {Number} id The id of the download to remove.
+ */
+Downloads.prototype.remove = function(id) {
+ this.node_.removeChild(this.downloads_[id].node);
+ delete this.downloads_[id];
+}
+
+/**
+ * Clear all downloads and reset us back to a null state.
+ */
+Downloads.prototype.clear = function() {
+ for (var id in this.downloads_) {
+ this.downloads_[id].clear();
+ this.remove(id);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Download
+/**
+ * A download and the DOM representation for that download.
+ * @param {Object} download A backend download object (see downloads_ui.cc)
+ */
+function Download(download) {
+ // Create DOM
+ this.node = createElementWithClassName('div', 'download');
+
+ // Container for all 'safe download' UI.
+ this.safe_ = document.createElement("div");
+ this.safe_.ondragstart = bind(this.drag_, this);
+ this.node.appendChild(this.safe_);
+
+ this.nodeImg_ = createElementWithClassName('img', 'icon');
+ this.safe_.appendChild(this.nodeImg_);
+
+ // FileLink is used for completed downloads, otherwise we show FileName.
+ this.nodeTitleArea_ = createElementWithClassName('div', 'title-area');
+ this.safe_.appendChild(this.nodeTitleArea_);
+
+ this.nodeFileLink_ = createLink(bind(this.openFile_, this), '');
+ this.nodeFileLink_.className = 'name';
+ this.nodeFileLink_.style.display = 'none';
+ this.nodeTitleArea_.appendChild(this.nodeFileLink_);
+
+ this.nodeFileName_ = createElementWithClassName('span', 'name');
+ this.nodeFileName_.style.display = 'none';
+ this.nodeTitleArea_.appendChild(this.nodeFileName_);
+
+ this.nodeStatus_ = createElementWithClassName('span', 'status');
+ this.nodeTitleArea_.appendChild(this.nodeStatus_);
+
+ this.nodeURL_ = createElementWithClassName('div', 'url');
+ this.safe_.appendChild(this.nodeURL_);
+
+ // Controls.
+ this.nodeControls_ = createElementWithClassName('div', 'controls');
+ this.safe_.appendChild(this.nodeControls_);
+
+ this.controlShow_ = createLink(bind(this.show_, this),
+ localStrings.getString('control_showinfolder'));
+ this.nodeControls_.appendChild(this.controlShow_);
+
+ this.controlPause_ = createLink(bind(this.pause_, this),
+ localStrings.getString('control_pause'));
+ this.nodeControls_.appendChild(this.controlPause_);
+
+ this.controlCancel_ = createLink(bind(this.cancel_, this),
+ localStrings.getString('control_cancel'));
+ this.nodeControls_.appendChild(this.controlCancel_);
+
+ // Container for 'unsafe download' UI.
+ this.danger_ = createElementWithClassName('div', 'show-dangerous');
+ this.node.appendChild(this.danger_);
+
+ this.dangerDesc_ = document.createElement("div");
+ this.danger_.appendChild(this.dangerDesc_);
+
+ this.dangerSave_ = createButton(bind(this.saveDangerous_, this),
+ localStrings.getString("danger_save"));
+ this.danger_.appendChild(this.dangerSave_);
+
+ this.dangerDiscard_ = createButton(bind(this.discardDangerous_, this),
+ localStrings.getString("danger_discard"));
+ this.danger_.appendChild(this.dangerDiscard_);
+
+ // Update member vars.
+ this.update(download);
+}
+
+/**
+ * The states a download can be in. These correspond to states defined in
+ * DownloadsDOMHandler::CreateDownloadItemValue
+ */
+Download.States = {
+ IN_PROGRESS : "IN_PROGRESS",
+ CANCELLED : "CANCELLED",
+ COMPLETE : "COMPLETE",
+ PAUSED : "PAUSED",
+ DANGEROUS : "DANGEROUS",
+}
+
+/**
+ * Updates the download to reflect new data.
+ * @param {Object} download A backend download object (see downloads_ui.cc)
+ */
+Download.prototype.update = function(download) {
+ this.id_ = download.id;
+ this.filePath_ = download.file_path;
+ this.fileName_ = download.file_name;
+ this.url_ = download.url;
+ this.state_ = download.state;
+ this.percent_ = download.percent;
+ this.speed_ = download.speed;
+ this.received_ = download.received;
+
+ if (this.state_ == Download.States.DANGEROUS) {
+ this.dangerDesc_.innerHTML = localStrings.formatString('danger_desc',
+ this.fileName_);
+ this.danger_.style.display = 'block';
+ this.safe_.style.display = 'none';
+ } else {
+ this.nodeImg_.src = 'chrome-ui://fileicon/' + this.filePath_;
+
+ if (this.state_ == Download.States.COMPLETE) {
+ this.nodeFileLink_.innerHTML = this.fileName_;
+ this.nodeFileLink_.href = this.filePath_;
+ } else {
+ this.nodeFileName_.innerHTML = this.fileName_;
+ }
+
+ showInline(this.nodeFileLink_, this.state_ == Download.States.COMPLETE);
+ showInline(this.nodeFileName_, this.state_ != Download.States.COMPLETE);
+
+ showInline(this.controlShow_, this.state_ == Download.States.COMPLETE);
+ showInline(this.controlPause_, this.state_ == Download.States.IN_PROGRESS);
+ showInline(this.controlCancel_, this.state_ == Download.States.IN_PROGRESS);
+
+ this.nodeURL_.innerHTML = this.url_;
+ this.nodeStatus_.innerHTML = this.getStatusText_();
+
+ this.danger_.style.display = 'none';
+ this.safe_.style.display = 'block';
+ }
+}
+
+/**
+ * Removes applicable bits from the DOM in preparation for deletion.
+ */
+Download.prototype.clear = function() {
+ this.safe_.ondragstart = null;
+ this.nodeFileLink_.onclick = null;
+ this.controlShow_.onclick = null;
+ this.controlCancel_.onclick = null;
+ this.controlPause_.onclick = null;
+ this.dangerDiscard_.onclick = null;
+
+ this.node.innerHTML = '';
+}
+
+/**
+ * @return {String} User-visible status update text.
+ */
+Download.prototype.getStatusText_ = function() {
+ switch (this.state_) {
+ case Download.States.IN_PROGRESS:
+ // TODO(glen): Make pretty, localize, etc.
+ return this.percent_ + '% at ' + this.speed_;
+ case Download.States.CANCELLED:
+ return localStrings.getString('status_cancelled');
+ case Download.States.PAUSED:
+ return localStrings.getString('status_paused');
+ case Download.States.DANGEROUS:
+ return localStrings.getString('danger_desc');
+ case Download.States.COMPLETE:
+ return "";
+ }
+}
+
+/**
+ * Tells the backend to initiate a drag, allowing users to drag
+ * files from the download page and have them appear as native file
+ * drags.
+ */
+Download.prototype.drag_ = function() {
+ chrome.send("drag", [this.id_.toString()]);
+ return false;
+}
+
+/**
+ * Tells the backend to open this file.
+ */
+Download.prototype.openFile_ = function() {
+ chrome.send("openFile", [this.id_.toString()]);
+ return false;
+}
+
+/**
+ * Tells the backend that the user chose to save a dangerous file.
+ */
+Download.prototype.saveDangerous_ = function() {
+ chrome.send("saveDangerous", [this.id_.toString()]);
+ return false;
+}
+
+/**
+ * Tells the backend that the user chose to discard a dangerous file.
+ */
+Download.prototype.discardDangerous_ = function() {
+ chrome.send("discardDangerous", [this.id_.toString()]);
+ downloads.remove(this.id_);
+ return false;
+}
+
+/**
+ * Tells the backend to show the file in explorer.
+ */
+Download.prototype.show_ = function() {
+ chrome.send("show", [this.id_.toString()]);
+ return false;
+}
+
+/**
+ * Tells the backend to pause this download.
+ */
+Download.prototype.pause_ = function() {
+ chrome.send("pause", [this.id_.toString()]);
+ return false;
+}
+
+/**
+ * Tells the backend to cancel this download.
+ */
+Download.prototype.cancel_ = function() {
+ chrome.send("cancel", [this.id_.toString()]);
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Page:
+var downloads, localStrings;
+
+function load() {
+ localStrings = new LocalStrings($('l10n'));
+ downloads = new Downloads();
+ setSearch("");
+}
+
+function setSearch(searchText) {
+ downloads.clear();
+ downloads.setSearchText(searchText);
+ chrome.send("getDownloads", [searchText.toString()]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Chrome callbacks:
+/**
+ * Our history system calls this function with results from searches or when
+ * downloads are added or removed.
+ */
+function downloadsList(results) {
+ downloadUpdated(results);
+ downloads.updateSummary();
+}
+
+/**
+ * When a download is updated (progress, state change), this is called.
+ */
+function downloadUpdated(results) {
+ for (var i = 0, thisResult; thisResult = results[i]; i++) {
+ downloads.updated(thisResult);
+ }
+}
+</script>
+</head>
+<body onload="load();">
+<div class="header">
+ <a href="" onclick="setSearch(''); return false;">
+ <img src="../../app/theme/downloads_section.png"
+ width="67" height="67" class="logo" border="0" /></a>
+ <form method="post" action=""
+ onsubmit="setSearch(this.term.value); return false;"
+ class="form">
+ <input type="text" name="term" id="term" />
+ <input type="submit" name="submit" jsvalues="value:searchbutton" />
+ </form>
+</div>
+<div class="main">
+ <div id="downloads-summary"></div>
+ <div id="downloads-display"></div>
+</div>
+<div class="footer">
+</div>
+<div id="l10n" style="display:none;">
+ <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '%s'</span>
+ <span id="no_results" jscontent="no_results">No results found.</span>
+ <span id="downloads" jscontent="downloads">Downloads</span>
+
+ <span id="status_cancelled" jscontent="status_cancelled">Cancelled</span>
+ <span id="status_paused" jscontent="status_paused">Paused</span>
+
+ <span id="danger_desc" jscontent="danger_desc">This is a dangerous file</span>
+ <span id="danger_save" jscontent="danger_save">Save</span>
+ <span id="danger_discard" jscontent="danger_discard">Discard</span>
+
+ <span id="control_pause" jscontent="control_pause">Pause</span>
+ <span id="control_showinfolder" jscontent="control_showinfolder">Show in folder</span>
+ <span id="control_cancel" jscontent="control_cancel">Cancel</span>
+ <span id="control_resume" jscontent="control_resume">Resume</span>
+</div>
+</body>
+</html>
Property changes on: chrome\browser\resources\downloads.html
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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