<!DOCTYPE HTML>
<html i18n-values="dir:textdirection;">
<head>
<meta charset="utf-8">
<title i18n-content="title"></title>
<link rel="icon" href="../../app/theme/downloads_favicon.png">
<style type="text/css">
div.header {
border-bottom: 1px solid #7289E2;
padding: 8px;
margin: 0;
width: 100%;
left: 0;
top: 0;
height: 32px;
position: absolute;
box-sizing: border-box;
background-image: -webkit-linear-gradient(#D0DAF8, #A6BAF7);
border-bottom-color: #999;
border-bottom-width: 1px;
border-left-color: #999;
border-left-width: 1px;
border-right-color: #999;
border-right-width: 1px;
color: black;
}
*:-khtml-drag {
background-color: rgba(238,238,238, 0.5);
}
*[draggable] {
-khtml-user-drag: element;
cursor: move;
}
ul.downloadlist {
list-style-type: none;
margin: 0;
padding: 0;
position: relative;
}
.menuicon {
position: absolute;
right: 9px;
top: 11px;
height: 100%;
width: 5px;
margin-left: 0;
background: url('chrome://resources/images/active_downloads_menu.png');
margin-top: 5px;
background-repeat: no-repeat;
}
.menubutton {
position: absolute;
margin-top: -36px;
right: 0;
height: 35px;
width: 24px;
border-bottom: 1px solid #CCC;
cursor: pointer;
}
.rowbg {
border-bottom: 1px solid #CCC;
background: -webkit-gradient(linear, left top, left bottom,
from(#f3f3f3), to(#ebebeb), color-stop(0.8, #ededed));
}
.rowbg:hover {
background: -webkit-gradient(linear, left top, left bottom,
from(#fdfdfd), to(#f1f1f1), color-stop(0.8, #f5f5f5));
}
.rowbg:active {
background: -webkit-gradient(linear, left top, left bottom,
from(#d2e0f0), to(#dee6f0), color-stop(0.1, #e3e9f0));
}
.downloadrow {
height: 36px;
}
.rowbutton {
padding: 5px 5px 0 34px;
position: relative;
right: 24px;
border-right: 1px solid #CCC;
height: 30px;
}
.rowbutton div.icon {
float: left;
margin-top: 1px;
display: inline
position: relative;
width: 21px;
height: 17px;
background-repeat: no-repeat;
}
.rowbutton span.title {
position: relative;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
overflow: hidden;
width: 189px;
color: #325282;
}
.rowbutton span.downloading {
top: -6px;
font-size: .8em;
}
.rowbutton span.downloaded {
font-size: .8em;
}
.rowbutton span.error {
font-size: .6em;
}
.allowdownload {
margin: -10px 5px 5px 30px;
display: block;
}
.allowdownloadtext {
font-size: .6em;
color: #325282;
}
.confirm {
font-size: .6em;
text-decoration: underline;
margin-left: 5px;
color: #254f9b;
cursor: pointer;
}
.progress {
font-size: .6em;
text-align: right;
margin-left: auto;
margin-top: -5px;
}
div.columnlist {
width: 100%;
top: 0;
left: 0;
bottom: 29px; /* space for Show All Downloads */
position: absolute;
background: #e8e8e8
}
span.showalldownloadstext {
color: #254f9b;
cursor: pointer;
text-decoration: underline;
font-size: 12px;
height: 15px;
}
div.showalldownloads {
width: 100%;
bottom: 0;
height: 29px;
position: absolute;
margin-left: -8px;
text-align: center;
background: #e8e8e8
}
.menu {
top: 14px;
right: 2px;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 3px;
border-bottom-left-radius: 4px 4px;
border-bottom-right-radius: 4px 4px;
border-top-left-radius: 4px 4px;
border-top-right-radius: 0px 0px;
position: absolute;
display: none;
z-index: 999;
background: white;
border-top-left-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.6);
padding: 5px;
}
.menuitem {
width: 100%;
height: 20px;
font-size: .8em;
text-align: left;
cursor: pointer;
left: 0;
color: #0D0052;
-webkit-transition: color 1.0s ease-out ;
}
.menuitem:hover {
text-decoration: underline;
color: #20c;
background: #ebeff9;
-webkit-transition: color 0.0s ease-out ;
}
div.iconmedia {
background: url('chrome://resources/images/icon_media.png');
}
div.iconfolder {
background: url('chrome://resources/images/icon_folder.png');
}
div.iconfile {
background: url('chrome://resources/images/icon_file.png');
}
div.iconphoto {
background: url('chrome://resources/images/icon_photo.png');
}
div.iconmusic {
background: url('chrome://resources/images/icon_media.png');
}
</style>
<script src="shared/js/local_strings.js"></script>
<script src="shared/js/media_common.js"></script>
<script src="shared/js/util.js"></script>
<script>
var localStrings = null;
var downloadRowList = null;
function init() {
localStrings = new LocalStrings();
initTestHarness();
$('header').style.display = 'none';
$('showalldownloadstext').textContent =
localStrings.getString('showalldownloads');
downloadRowList = new DownloadRowList();
chrome.send('getDownloads', []);
}
/**
* Testing. Allow this page to be loaded in a browser.
* Create stubs for localStrings and chrome.send.
*/
var testHarnessEnabled = false;
function initTestHarness() {
if (testHarnessEnabled) {
localStrings = {
getString: function(name) {
if (name == 'showalldownloads')
return 'Show All Downloads';
if (name == 'allowdownload')
return 'Allow Download?';
if (name == 'confirmyes')
return 'Yes';
if (name == 'confirmcancel')
return 'Cancel';
return name;
},
getStringF: function(name, path) {
return path + ' - Unknown file type.';
},
};
chrome.send = function(name, ary) {
console.log('chrome.send ' + name + ' ' + ary);
if (name == 'getDownloads' ||
(name == 'openNewFullWindow' &&
ary[0] == 'chrome://downloads'))
sendTestResults();
}
}
}
/**
* Create a results array with test data and call downloadsList.
*/
var id = 1;
var results = [];
function sendTestResults() {
results.push({
state: (id % 2 ? 'DANGEROUS' : 'COMPLETE'),
percent: (id % 2 ? 90 : 100),
id: id,
file_name: ' Test' + id + '.pdf',
file_path: '/home/achuith/Downloads/Test' + id + '.pdf',
progress_status_text : (id % 2 ?
'download progressing nicely' : 'download complete'),
});
id++;
downloadsList(results);
}
/**
* Current Menu.
*/
var menu = {
current_: null,
/**
* Close the current menu.
*/
clear: function() {
var current = this.current_;
if (current) {
current.firstChild.style.display = 'none';
current.style.opacity = '';
this.current_ = null;
}
},
/**
* If it's a second click on an open menu, close the menu.
* Otherwise, close any other open menu and open the clicked menu.
*/
clicked: function(row) {
var menuicon = row.menuicon;
if (this.current_ === menuicon) {
this.clear();
return;
}
this.clear();
if (menuicon.firstChild.style.display != 'block') {
menuicon.firstChild.style.display = 'block';
menuicon.style.opacity = '1';
menuicon.scrollIntoView();
this.current_ = menuicon;
}
window.event.stopPropagation();
},
};
/**
* C++ api calls.
*/
function downloadsList(results) {
downloadRowList.list(results);
}
function downloadUpdated(result) {
downloadRowList.update(result);
}
function showAllDownloads() {
chrome.send('openNewFullWindow', ['chrome://downloads']);
dialogClose();
}
function dialogClose() {
chrome.send('DialogClose', ['']);
}
/**
* DownloadRow contains all the elements that go into a row of the downloads
* list. It represents a single DownloadItem.
*
* @param {DownloadRowList} list Global DownloadRowList.
* @param {Object} result JSON representation of DownloadItem.
* @constructor
*/
function DownloadRow(list, result) {
this.path = result.file_path;
this.name = result.file_name;
this.list = list;
this.id = result.id;
this.createRow_(list);
this.createRowButton_();
this.createMenu_();
}
DownloadRow.prototype = {
/**
* Create the row html element and book-keeping for the row.
* @param {DownloadRowList} list global DownloadRowList instance.
* @private
*/
createRow_: function(list) {
var row = document.createElement('li');
row.className = 'downloadrow';
row.id = this.path;
row.downloadRow = this;
list.append(row);
this.element = row;
this.list.downloadList.push(this);
},
getIconClass_: function() {
if (pathIsImageFile(this.path)) {
return 'icon iconphoto';
} else if (pathIsVideoFile(this.path)) {
return 'icon iconmedia';
} else if (pathIsAudioFile(this.path)) {
return 'icon iconmusic';
}
return 'icon iconfile';
},
setErrorText_: function(text) {
this.filename.textContent = text;
this.filename.className = 'error title';
},
supportsPdf_: function() {
return 'application/pdf' in navigator.mimeTypes;
},
openFilePath_: function() {
chrome.send('openNewFullWindow', ['file://' + this.path]);
},
/**
* Determine onclick behavior based on filename.
* @private
*/
getFunctionForItem_: function() {
var path = this.path;
var self = this;
if (pathIsAudioFile(path)) {
return function() {
chrome.send('playMediaFile', [path]);
};
}
if (pathIsVideoFile(path)) {
return function() {
chrome.send('playMediaFile', [path]);
};
}
if (pathIsImageFile(path)) {
return function() {
self.openFilePath_();
}
}
if (pathIsHtmlFile(path)) {
return function() {
self.openFilePath_();
}
}
if (pathIsPdfFile(path) && this.supportsPdf_()) {
return function() {
self.openFilePath_();
}
}
return function() {
self.setErrorText_(localStrings.getStringF('error_unknown_file_type',
self.name));
};
},
/**
* Create a child element.
*
* @param {string} type The type - div, span, etc.
* @param {string} className The class name
* @param {HTMLElement} parent Parent to append this child to.
* @param {string} textContent optional text content of child.
* @param {function(*)} onclick onclick function of child.
* @private
*/
createChild_: function(type, className, parent, textContent, onclick) {
var elem = document.createElement(type);
elem.className = className;
if (textContent !== undefined)
elem.textContent = textContent;
elem.onclick = onclick;
parent.appendChild(elem);
return elem;
},
/**
* Create the row button for the left of the row.
* This contains the icon, filename and error elements.
* @private
*/
createRowButton_: function () {
this.rowbutton = this.createChild_('div', 'rowbutton rowbg', this.element);
// Icon.
var icon = this.createChild_('div', this.getIconClass_(), this.rowbutton);
// Filename.
this.filename = this.createChild_('span', 'downloaded title',
this.rowbutton, this.name);
},
/**
* Create the menu button on the right of the row.
* This contains the menuicon. The menuicon contains the menu, which
* contains items for Open, Pause/Resume and Cancel.
* @private
*/
createMenu_: function() {
var self = this;
this.menubutton = this.createChild_('div', 'menubutton rowbg',
this.element, '',
function() {
menu.clicked(self);
});
this.menuicon = this.createChild_('div', 'menuicon', this.menubutton);
this.menuicon.align = 'right';
var menudiv = this.createChild_('div', 'menu', this.menuicon);
this.open = this.createChild_('div', 'menuitem', menudiv,
localStrings.getString('open'), this.getFunctionForItem_());
this.pause = this.createChild_('div', 'menuitem', menudiv,
localStrings.getString('pause'), function() {
self.pauseToggleDownload_();
});
this.cancel = this.createChild_('div', 'menuitem', menudiv,
localStrings.getString('cancel'), function() {
self.cancelDownload_();
});
this.pause.style.display = 'none';
this.cancel.style.display = 'none';
},
allowDownload_: function() {
chrome.send('allowDownload', ['' + this.id]);
},
cancelDownload_: function() {
chrome.send('cancelDownload', ['' + this.id]);
},
pauseToggleDownload_: function() {
this.pause.textContent =
(this.pause.textContent == localStrings.getString('pause')) ?
localStrings.getString('resume') :
localStrings.getString('pause');
// Convert id to string before send.
chrome.send('pauseToggleDownload', ['' + this.id]);
},
resetRow_: function() {
this.rowbutton.onclick = '';
this.rowbutton.style.cursor = '';
this.rowbutton.setAttribute('draggable', 'false');
},
createAllowDownload_: function() {
if (this.allowdownload)
return;
this.allowdownload = this.createChild_('div', 'allowdownload',
this.rowbutton);
this.createChild_('span', 'allowdownloadtext', this.allowdownload,
localStrings.getString('allowdownload'));
var self = this;
this.createChild_('span', 'confirm', this.allowdownload,
localStrings.getString('confirmyes'),
function() {
self.allowDownload_();
});
this.createChild_('span', 'confirm', this.allowdownload,
localStrings.getString('confirmcancel'),
function() {
self.cancelDownload_();
});
this.resetRow_();
this.menubutton.onclick = '';
},
removeAllowDownload_: function() {
if (this.allowdownload) {
this.rowbutton.removeChild(this.allowdownload);
this.allowdownload = null;
var self = this;
this.menubutton.onclick = function() {
menu.clicked(self);
};
}
},
createProgress_: function() {
if (this.progress)
return;
this.progress = this.createChild_('div', 'progress', this.rowbutton);
// Menu has Pause/Cancel. Open hidden.
this.open.style.display = 'none';
this.pause.style.display = '';
this.cancel.style.display = '';
},
removeProgress_: function() {
if (this.progress) {
this.rowbutton.removeChild(this.progress);
this.progress = null;
}
},
updatePause_: function(result) {
var pause = this.pause;
var pauseStr = localStrings.getString('pause');
var resumeStr = localStrings.getString('resume');
if (pause &&
result.state == 'PAUSED' &&
pause.textContent != resumeStr) {
pause.textContent = resumeStr;
} else if (pause &&
result.state == 'IN_PROGRESS' &&
pause.textContent != pauseStr) {
pause.textContent = pauseStr;
}
},
updateProgress_: function(result) {
this.removeAllowDownload_();
this.createProgress_();
this.progress.textContent = result.progress_status_text;
this.updatePause_(result);
},
/**
* Called when the item has finished downloading. Switch the menu
* and remove the progress bar.
* @private
*/
finishedDownloading_: function() {
this.filename.className = 'downloaded title';
// Menu has Open. Pause/Cancel hidden.
this.open.style.display = '';
this.pause.style.display = 'none';
this.cancel.style.display = 'none';
// Make rowbutton clickable.
this.rowbutton.onclick = this.getFunctionForItem_();
this.rowbutton.style.cursor = 'pointer';
// Make rowbutton draggable.
this.rowbutton.setAttribute('draggable', 'true');
var self = this;
this.rowbutton.addEventListener('dragstart', function(e) {
e.dataTransfer.effectAllowed = 'copy';
e.dataTransfer.setData('Text', self.path);
e.dataTransfer.setData('URL', 'file:///' + self.path);
}, false);
this.removeAllowDownload_();
this.removeProgress_();
},
/**
* One of the DownloadItem we are observing has updated.
* @param {Object} result JSON representation of DownloadItem.
*/
downloadUpdated: function(result) {
this.filename.textContent = result.file_name;
this.filename.className = 'downloading title';
if (result.state == 'CANCELLED' ||
result.state == 'INTERRUPTED') {
this.list.remove(this);
} else if (result.state == 'DANGEROUS') {
this.createAllowDownload_();
} else if (result.percent < 100) {
this.updateProgress_(result);
} else {
this.finishedDownloading_();
}
},
};
/**
* DownloadRowList is a container for DownloadRows.
*/
function DownloadRowList() {
var downloadpath = localStrings.getString('downloadpath');
var list = document.createElement('ul');
list.className = 'downloadlist';
list.id = downloadpath;
this.element = list;
this.rows = [];
document.title = downloadpath.split('/').pop();
$('main').appendChild(list);
}
DownloadRowList.prototype = {
/**
* ROW_HEIGHT is height of each row.
* MAX_ROWS is maximum number of rows displayed (to display a new row
* beyond MAX_ROWS, we delete the oldest row).
* MIN_ROWS is the minimum number of rows displayed.
* numRows is the current number of rows.
* downloadList is the list of DownloadRow elements.
*/
ROW_HEIGHT: 36,
MAX_ROWS: 5,
MIN_ROWS: 1,
numRows: 0,
downloadList: [],
numRowsOutsideRange_: function() {
return this.numRows > this.MIN_ROWS && this.numRows < this.MAX_ROWS;
},
/**
* Remove a row from the list, as when a download is canceled, or
* the the number of rows has exceeded the max allowed.
*
* @param {DownloadRow} row Row to be removed.
* @private
*/
remove: function(row) {
this.downloadList.splice(this.downloadList.indexOf(row), 1);
this.element.removeChild(row.element);
row.element.downloadRow = null;
this.numRows--;
if (this.numRowsOutsideRange_())
window.resizeBy(0, -this.ROW_HEIGHT);
},
/**
* Append a new row to the list, removing the last row if we exceed the
* maximum allowed.
* @param {DownloadRow} row Row to be removed.
*/
append: function(row) {
if (this.numRowsOutsideRange_())
window.resizeBy(0, this.ROW_HEIGHT);
this.numRows++;
var list = this.element;
if (this.numRows > this.MAX_ROWS)
this.remove(list.lastChild.downloadRow);
if (list.firstChild) {
list.insertBefore(row, list.firstChild);
} else {
list.appendChild(row);
}
},
/**
* Handle list callback with list of DownloadItems.
* @param {Array} results Array of JSONified DownloadItems.
*/
list: function(results) {
var removeList = [];
removeList.pushUnique = function(element) {
if (this.indexOf(element) == -1)
this.push(element);
};
for (var y = 0; y < this.downloadList.length; y++) {
var found = false;
for (var x = 0; x < results.length; x++) {
var element = $(results[x].file_path);
if (this.downloadList[y].element == element) {
found = true;
break;
}
}
if (!found)
removeList.pushUnique(this.downloadList[y]);
}
for (var i = 0; i < results.length; i++) {
this.update(results[i]);
}
for (i = 0; i < removeList.length; i++) {
this.remove(removeList[i]);
}
},
/**
* Handle update of a DownloadItem we're observing.
* @param {Object} result JSON representation of DownloadItem.
*/
update: function(result) {
var element = $(result.file_path);
var row = element && element.downloadRow;
if (!row &&
result.state != 'CANCELLED' &&
result.state != 'INTERRUPTED') {
row = new DownloadRow(this, result);
}
row && row.downloadUpdated(result);
},
};
</script>
<body onload="init();" onclick="menu.clear()" onselectstart="return false"
i18n-values=".style.fontFamily:fontfamily">
<div id="header">
<div id="currenttitle"></div>
</div><br>
<div id="main" class="columnlist"></div>
<div id="showalldownloads" class="showalldownloads">
<span id="showalldownloadstext" class="showalldownloadstext"
onclick="showAllDownloads()"></span>
</div>
</body>
</html>