diff options
author | benjhayden@chromium.org <benjhayden@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-30 15:20:46 +0000 |
---|---|---|
committer | benjhayden@chromium.org <benjhayden@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-30 15:20:46 +0000 |
commit | 6d0aba64b4c5e150e9775bf8ef134a6f1690a90e (patch) | |
tree | 4400af5fac8fbc5c837ce00f40c892938b255fc0 /chrome/common/extensions/docs/examples/api/downloads | |
parent | aaf76d62e23ba313fa96f3e1c044e7570dea478a (diff) | |
download | chromium_src-6d0aba64b4c5e150e9775bf8ef134a6f1690a90e.zip chromium_src-6d0aba64b4c5e150e9775bf8ef134a6f1690a90e.tar.gz chromium_src-6d0aba64b4c5e150e9775bf8ef134a6f1690a90e.tar.bz2 |
Modern Download Manager browser action extension
Some of the features depend on https://codereview.chromium.org/16924017/
After that CL and this are submitted, I'll upload this to the webstore and start getting the By Google tag.
If you have a canary/dev/beta channel chrome installed or have a binary built from ToT and you're on the google network, go to
http://www.corp.google.com/~benjhayden/
right-click "download_manager.crx", Save Link As, open chrome://extensions and drag download_manager.crx from the shelf and drop it on chrome://extensions to install it.
If you install it on the stable channel, it will invite you to install the beta channel.
R=asanka@chromium.org, asargent@chromium.org, mkwst@chromium.org
Review URL: https://codereview.chromium.org/17286017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@214356 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/extensions/docs/examples/api/downloads')
16 files changed, 1691 insertions, 0 deletions
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/_locales/en/messages.json b/chrome/common/extensions/docs/examples/api/downloads/download_manager/_locales/en/messages.json new file mode 100644 index 0000000..cc1b636 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/_locales/en/messages.json @@ -0,0 +1,284 @@ +{"extName": { + "message": "Modern Download Manager", + "description": "Extension name"}, + "extDesc": { + "message": "Modern Download Manager User Interface for Google Chrome", + "description": "Extension description"}, + "badChromeVersion": { + "message": "The downloads API is only available on the canary, dev, and beta channels.", + "description": ""}, + "tabTitle": { + "message": "Downloads", + "description": "tab title"}, + "searchPlaceholder": { + "message": "Search Downloads", + "description": ""}, + "clearAllTitle": { + "message": "Erase All Visible Downloads", + "description": ""}, + "openDownloadsFolderTitle": { + "message": "Open Downloads Folder", + "description": ""}, + "zeroItems": { + "message": "There are zero download items.", + "description": ""}, + "searching": { + "message": "Teleporting lots of goats...", + "description": ""}, + "zeroSearchResults": { + "message": "Zero matches", + "description": ""}, + "showOlderDownloads": { + "message": "Show Older Downloads", + "description": ""}, + "loadingOlderDownloads": { + "message": "Loading Older Downloads...", + "description": ""}, + "openTitle": { + "message": "Open", + "description": ""}, + "pauseTitle": { + "message": "Pause", + "description": ""}, + "resumeTitle": { + "message": "Resume", + "description": ""}, + "cancelTitle": { + "message": "Cancel", + "description": ""}, + "removeFileTitle": { + "message": "Remove file", + "description": ""}, + "eraseTitle": { + "message": "Erase", + "description": ""}, + "retryTitle": { + "message": "Retry", + "description": ""}, + "referrerTitle": { + "message": "Referrer", + "description": ""}, + "month0abbr": {"message": "Jan","description": ""}, + "month1abbr": {"message": "Feb","description": ""}, + "month2abbr": {"message": "Mar","description": ""}, + "month3abbr": {"message": "Apr","description": ""}, + "month4abbr": {"message": "May","description": ""}, + "month5abbr": {"message": "Jun","description": ""}, + "month6abbr": {"message": "Jul","description": ""}, + "month7abbr": {"message": "Aug","description": ""}, + "month8abbr": {"message": "Sep","description": ""}, + "month9abbr": {"message": "Oct","description": ""}, + "month10abbr": {"message": "Nov","description": ""}, + "month11abbr": {"message": "Dec","description": ""}, + "openWhenCompleteFinishing": { + "message": "Opening in just a moment", + "description": ""}, + "timeLeftFinishing": { + "message": "finishing...", + "description": ""}, + "openWhenCompleteDays": { + "message": "Opening in $days$d $hours$h", + "description": "", + "placeholders": { + "days": { + "content": "$1", + "example": "2"}, + "hours": { + "content": "$2", + "example": "23"}}}, + "timeLeftDays": { + "message": "$days$d $hours$h left", + "description": "", + "placeholders": { + "days": { + "content": "$1", + "example": "2"}, + "hours": { + "content": "$2", + "example": "23"}}}, + "openWhenCompleteHours": { + "message": "Opening in $hours$h $mins$m", + "description": "", + "placeholders": { + "hours": { + "content": "$1", + "example": "23"}, + "mins": { + "content": "$2", + "example": "59"}}}, + "timeLeftHours": { + "message": "$hours$h $mins$m left", + "description": "", + "placeholders": { + "hours": { + "content": "$1", + "example": "23"}, + "mins": { + "content": "$2", + "example": "59"}}}, + "openWhenCompleteMinutes": { + "message": "Opening in $mins$m $sec$s", + "description": "", + "placeholders": { + "mins": { + "content": "$1", + "example": "59"}, + "sec": { + "content": "$2", + "example": "59"}}}, + "timeLeftMinutes": { + "message": "$mins$m $sec$s left", + "description": "", + "placeholders": { + "mins": { + "content": "$1", + "example": "59"}, + "sec": { + "content": "$2", + "example": "59"}}}, + "openWhenCompleteSeconds": { + "message": "Opening in $sec$s", + "description": "", + "placeholders": { + "sec": { + "content": "$1", + "example": "59"}}}, + "timeLeftSeconds": { + "message": "$sec$s left", + "description": "", + "placeholders": { + "sec": { + "content": "$1", + "example": "59"}}}, + "error_FILE_FAILED": { + "message": "File Failed", + "description": ""}, + "error_FILE_ACCESS_DENIED": { + "message": "File-System Access Denied", + "description": ""}, + "error_FILE_NO_SPACE": { + "message": "No Space On Disk", + "description": ""}, + "error_FILE_NAME_TOO_LONG": { + "message": "Filename Too Long", + "description": ""}, + "error_FILE_TOO_LARGE": { + "message": "File Too Large", + "description": ""}, + "error_FILE_VIRUS_INFECTED": { + "message": "Virus Infected", + "description": ""}, + "error_FILE_TRANSIENT_ERROR": { + "message": "Transient File-System Error", + "description": ""}, + "error_FILE_BLOCKED": { + "message": "File Blocked", + "description": ""}, + "error_FILE_SECURITY_CHECK_FAILED": { + "message": "Security Check Failed", + "description": ""}, + "error_FILE_TOO_SHORT": { + "message": "File Too Short", + "description": ""}, + "error_NETWORK_FAILED": { + "message": "Network Failure", + "description": ""}, + "error_NETWORK_TIMEOUT": { + "message": "Network Timeout", + "description": ""}, + "error_NETWORK_DISCONNECTED": { + "message": "Network Disconnected", + "description": ""}, + "error_NETWORK_SERVER_DOWN": { + "message": "Server Down", + "description": ""}, + "error_SERVER_FAILED": { + "message": "Server Failure", + "description": ""}, + "error_SERVER_NO_RANGE": { + "message": "Server No Range", + "description": ""}, + "error_SERVER_PRECONDITION": { + "message": "Server Precondition Failure", + "description": ""}, + "error_SERVER_BAD_CONTENT": { + "message": "Bad Content", + "description": ""}, + "error_USER_CANCELED": { + "message": "Cancelled", + "description": ""}, + "error_USER_SHUTDOWN": { + "message": "Cancelled", + "description": ""}, + "error_CRASH": { + "message": "Crash", + "description": ""}, + "error_1": { + "message": "File Failed", + "description": ""}, + "error_2": { + "message": "File-System Access Denied", + "description": ""}, + "error_3": { + "message": "No Space On Disk", + "description": ""}, + "error_5": { + "message": "Filename Too Long", + "description": ""}, + "error_6": { + "message": "File Too Large", + "description": ""}, + "error_7": { + "message": "Virus Infected", + "description": ""}, + "error_10": { + "message": "Transient File-System Error", + "description": ""}, + "error_11": { + "message": "File Blocked", + "description": ""}, + "error_12": { + "message": "Security Check Failed", + "description": ""}, + "error_13": { + "message": "File Too Short", + "description": ""}, + "error_20": { + "message": "Network Failure", + "description": ""}, + "error_21": { + "message": "Network Timeout", + "description": ""}, + "error_22": { + "message": "Network Disconnected", + "description": ""}, + "error_23": { + "message": "Server Down", + "description": ""}, + "error_30": { + "message": "Server Failure", + "description": ""}, + "error_31": { + "message": "Server No Range", + "description": ""}, + "error_32": { + "message": "Server Precondition Failure", + "description": ""}, + "error_33": { + "message": "Bad Content", + "description": ""}, + "error_40": { + "message": "Cancelled", + "description": ""}, + "error_41": { + "message": "Cancelled", + "description": ""}, + "error_50": { + "message": "Crash", + "description": ""}, + "errorRemoved": { + "message": "Removed", + "description": ""}, + "showInFolderTitle": { + "message": "Show in Folder", + "description": "Alt text for show in folder icon"}} diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js new file mode 100644 index 0000000..ffc2a4d --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js @@ -0,0 +1,260 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var colors = { + progressColor: '#0d0', + arrow: '#555', + danger: 'red', + complete: 'green', + paused: 'grey', + background: 'white', +}; + +function drawLine(ctx, x1, y1, x2, y2) { + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); +} + +function drawProgressSpinner(ctx, stage) { + var center = ctx.canvas.width/2; + var radius = center*0.9; + const segments = 16; + var segArc = 2 * Math.PI / segments; + ctx.lineWidth = Math.round(ctx.canvas.width*0.1); + for (var seg = 0; seg < segments; ++seg) { + var color = ((stage == 'm') ? ((seg % 2) == 0) : (seg <= stage)); + ctx.fillStyle = ctx.strokeStyle = ( + color ? colors.progressColor : colors.background); + ctx.moveTo(center, center); + ctx.beginPath(); + ctx.arc(center, center, radius, (seg-4)*segArc, (seg-3)*segArc, false); + ctx.lineTo(center, center); + ctx.fill(); + ctx.stroke(); + } + ctx.strokeStyle = colors.background; + radius += ctx.lineWidth-1; + drawLine(ctx, center-radius, center, center+radius, center); + drawLine(ctx, center, center-radius, center, center+radius); + radius *= Math.sin(Math.PI/4); + drawLine(ctx, center-radius, center-radius, center+radius, center+radius); + drawLine(ctx, center-radius, center+radius, center+radius, center-radius); +} + +function drawArrow(ctx) { + ctx.beginPath(); + ctx.lineWidth = Math.round(ctx.canvas.width*0.1); + ctx.lineJoin = 'round'; + ctx.strokeStyle = ctx.fillStyle = colors.arrow; + var center = ctx.canvas.width/2; + var minw2 = center*0.2; + var maxw2 = center*0.60; + var height2 = maxw2; + ctx.moveTo(center-minw2, center-height2); + ctx.lineTo(center+minw2, center-height2); + ctx.lineTo(center+minw2, center); + ctx.lineTo(center+maxw2, center); + ctx.lineTo(center, center+height2); + ctx.lineTo(center-maxw2, center); + ctx.lineTo(center-minw2, center); + ctx.lineTo(center-minw2, center-height2); + ctx.lineTo(center+minw2, center-height2); + ctx.stroke(); + ctx.fill(); +} + +function drawDangerBadge(ctx) { + var s = ctx.canvas.width/100; + ctx.fillStyle = colors.danger; + ctx.strokeStyle = colors.background; + ctx.lineWidth = Math.round(s*5); + var edge = ctx.canvas.width-ctx.lineWidth; + ctx.beginPath(); + ctx.moveTo(s*75, s*55); + ctx.lineTo(edge, edge); + ctx.lineTo(s*55, edge); + ctx.lineTo(s*75, s*55); + ctx.lineTo(edge, edge); + ctx.fill(); + ctx.stroke(); +} + +function drawPausedBadge(ctx) { + var s = ctx.canvas.width/100; + ctx.beginPath(); + ctx.strokeStyle = colors.background; + ctx.lineWidth = Math.round(s*5); + ctx.rect(s*55, s*55, s*15, s*35); + ctx.fillStyle = colors.paused; + ctx.fill(); + ctx.stroke(); + ctx.rect(s*75, s*55, s*15, s*35); + ctx.fill(); + ctx.stroke(); +} + +function drawCompleteBadge(ctx) { + var s = ctx.canvas.width/100; + ctx.beginPath(); + ctx.arc(s*75, s*75, s*15, 0, 2*Math.PI, false); + ctx.fillStyle = colors.complete; + ctx.fill(); + ctx.strokeStyle = colors.background; + ctx.lineWidth = Math.round(s*5); + ctx.stroke(); +} + +function drawIcon(side, stage, badge) { + var canvas = document.createElement('canvas'); + canvas.width = canvas.height = side; + document.body.appendChild(canvas); + var ctx = canvas.getContext('2d'); + if (stage != 'n') { + drawProgressSpinner(ctx, stage); + } + drawArrow(ctx); + if (badge == 'd') { + drawDangerBadge(ctx); + } else if (badge == 'p') { + drawPausedBadge(ctx); + } else if (badge == 'c') { + drawCompleteBadge(ctx); + } + return canvas; +} + +function maybeOpen(id) { + var openWhenComplete = []; + try { + openWhenComplete = JSON.parse(localStorage.openWhenComplete); + } catch (e) { + localStorage.openWhenComplete = JSON.stringify(openWhenComplete); + } + var openNowIndex = openWhenComplete.indexOf(id); + if (openNowIndex >= 0) { + chrome.downloads.open(id); + openWhenComplete.splice(openNowIndex, 1); + localStorage.openWhenComplete = JSON.stringify(openWhenComplete); + } +} + +function setBrowserActionIcon(stage, badge) { + var canvas1 = drawIcon(19, stage, badge); + var canvas2 = drawIcon(38, stage, badge); + var imageData = {}; + imageData['' + canvas1.width] = canvas1.getContext('2d').getImageData( + 0, 0, canvas1.width, canvas1.height); + imageData['' + canvas2.width] = canvas2.getContext('2d').getImageData( + 0, 0, canvas2.width, canvas2.height); + chrome.browserAction.setIcon({imageData:imageData}); + canvas1.parentNode.removeChild(canvas1); + canvas2.parentNode.removeChild(canvas2); +} + +function pollProgress() { + pollProgress.tid = -1; + chrome.downloads.search({}, function(items) { + var popupLastOpened = parseInt(localStorage.popupLastOpened); + var totalTotalBytes = 0; + var totalBytesReceived = 0; + var anyMissingTotalBytes = false; + var anyDangerous = false; + var anyPaused = false; + var anyRecentlyCompleted = false; + var anyInProgress = false; + items.forEach(function(item) { + if (item.state == 'in_progress') { + anyInProgress = true; + if (item.totalBytes) { + totalTotalBytes += item.totalBytes; + totalBytesReceived += item.bytesReceived; + } else { + anyMissingTotalBytes = true; + } + var dangerous = ((item.danger != 'safe') && + (item.danger != 'accepted')); + anyDangerous = anyDangerous || dangerous; + anyPaused = anyPaused || item.paused; + } else if ((item.state == 'complete') && item.endTime && !item.error) { + var ended = (new Date(item.endTime)).getTime(); + var recentlyCompleted = (ended >= popupLastOpened); + anyRecentlyCompleted = anyRecentlyCompleted || recentlyCompleted; + maybeOpen(item.id); + } + }); + var stage = !anyInProgress ? 'n' : (anyMissingTotalBytes ? 'm' : + parseInt((totalBytesReceived / totalTotalBytes) * 15)); + var badge = anyDangerous ? 'd' : (anyPaused ? 'p' : + (anyRecentlyCompleted ? 'c' : '')); + + var targetIcon = stage + ' ' + badge; + if (sessionStorage.currentIcon != targetIcon) { + setBrowserActionIcon(stage, badge); + sessionStorage.currentIcon = targetIcon; + } + + if (anyInProgress && + (pollProgress.tid < 0)) { + pollProgress.start(); + } + }); +} +pollProgress.tid = -1; +pollProgress.MS = 200; + +pollProgress.start = function() { + if (pollProgress.tid < 0) { + pollProgress.tid = setTimeout(pollProgress, pollProgress.MS); + } +}; + +function isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +if (!isNumber(localStorage.popupLastOpened)) { + localStorage.popupLastOpened = '' + (new Date()).getTime(); +} + +chrome.downloads.onCreated.addListener(function(item) { + pollProgress(); +}); + +pollProgress(); + +function openWhenComplete(downloadId) { + var ids = []; + try { + ids = JSON.parse(localStorage.openWhenComplete); + } catch (e) { + localStorage.openWhenComplete = JSON.stringify(ids); + } + pollProgress.start(); + if (ids.indexOf(downloadId) >= 0) { + return; + } + ids.push(downloadId); + localStorage.openWhenComplete = JSON.stringify(ids); +} + +chrome.runtime.onMessage.addListener(function(request) { + if (request == 'poll') { + pollProgress.start(); + } + if (request == 'icons') { + [16, 19, 38, 128].forEach(function(s) { + var canvas = drawIcon(s, 'n', ''); + chrome.downloads.download({ + url: canvas.toDataURL('image/png', 1.0), + filename: 'icon' + s + '.png', + }); + canvas.parentNode.removeChild(canvas); + }); + } + if (isNumber(request.openWhenComplete)) { + openWhenComplete(request.openWhenComplete); + } +}); diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon128.png b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon128.png Binary files differnew file mode 100644 index 0000000..ff20437 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon128.png diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon19.png b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon19.png Binary files differnew file mode 100644 index 0000000..ad88a21 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon19.png diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon38.png b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon38.png Binary files differnew file mode 100644 index 0000000..cd33c09 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icon38.png diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/icons.html b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icons.html new file mode 100644 index 0000000..fca4bf2 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icons.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> +<title>Icon Generator</title> +<script src='icons.js'></script> +</head> +<body> +<button id=download>Generate Manifest Icons</button> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/icons.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icons.js new file mode 100644 index 0000000..d1bc098 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/icons.js @@ -0,0 +1,12 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +window.onload = function() { + var download = document.getElementById('download'); + download.onclick = function() { + chrome.runtime.sendMessage('icons'); + download.disabled = true; + return false; + }; +}; diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json b/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json new file mode 100644 index 0000000..85f2d0e --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json @@ -0,0 +1,14 @@ +{"name": "__MSG_extName__", + "version": "0.1", + "manifest_version": 2, + "description": "__MSG_extDesc__", + "icons": {"128": "icon128.png"}, + "browser_action": { + "default_icon": { + "19": "icon19.png", + "38": "icon38.png"}, + "default_title": "__MSG_extName__", + "default_popup": "popup.html"}, + "background": {"persistent": false, "scripts": ["background.js"]}, + "default_locale": "en", + "permissions": ["management", "downloads", "downloads.open"]} diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css new file mode 100644 index 0000000..3624a86 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css @@ -0,0 +1,229 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +body { + text-align: center; +} + +#outer, +#empty, +#open-folder, +.file-url, +.time-left, +.more-left { + display: inline-block; +} + +#head { + position: fixed; + width: 100%; + left: 0; + top: 0; + background: white; + border-bottom: 1px solid grey; +} + +#search-zero, +#items { + margin-top: 2em; +} + +#head, +#empty, +#searching, +#search-zero, +.item, +.start-time, +.complete-size, +.error, +.file-url-head, +.removed, +.open-filename, +.progress, +.time-left, +.url, +#text-width-probe { + white-space: nowrap; +} + +#head, +.item { + text-align: left; +} + +#empty { + vertical-align: bottom; + height: 2em; +} + +#q { + margin-top: 0.5em; + margin-left: 3%; +} + +#clear-all, +#open-folder { + float: right; +} + +svg { + width: 2em; + height: 2em; +} + +svg rect.border { + fill-opacity: 0; + stroke-width: 6; +} + +#open-folder svg, +.show-folder svg { + stroke: brown; + fill: white; + stroke-width: 3; +} + +#clear-all svg { + fill: white; + stroke-width: 3; +} + +#clear-all svg, +.remove-file svg, +.erase svg { + stroke: black; +} + +.clearfix { + clear: both; + float: right; +} + +#older, +#loading-older, +.item { + margin-top: 1em; +} + +.more { + position: absolute; + border: 1px solid grey; + background: white; + z-index: 1; +} + +.complete-size, +.error, +.removed, +.open-filename, +.progress, +.time-left { + margin-right: 0.4em; +} + +.error { + color: darkred; +} + +.referrer svg { + stroke: blue; + fill-opacity: 0; + stroke-width: 7; +} + +.removed, +.open-filename { + max-width: 400px; + overflow: hidden; + display: inline-block; +} + +.remove-file svg { + fill-opacity: 0; + stroke-width: 5; +} + +.remove-file svg line { + stroke-width: 7; + stroke: red; +} + +.erase svg ellipse, +.erase svg line { + fill-opacity: 0; + stroke-width: 5; +} + +.pause svg { + stroke: #333; +} + +.resume svg { + stroke: rgb(68, 187, 68); + fill: rgb(68, 187, 68); +} + +.cancel svg line { + stroke-width: 7; +} + +.cancel svg { + stroke: rgb(204, 68, 68); +} + +.meter { + height: 0.5em; + position: relative; + background: grey; +} + +.meter > span { + display: block; + height: 100%; + background-color: rgb(43, 194, 83); + background-image: -webkit-gradient( + linear, left bottom, left top, + color-stop(0, rgb(43,194,83)), + color-stop(1, rgb(84,240,84))); + position: relative; + overflow: hidden; +} + +.meter > span:after { + content: ''; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + background-image: -webkit-gradient( + linear, 0 0, 100% 100%, + color-stop(.25, rgba(255, 255, 255, .2)), + color-stop(.25, transparent), + color-stop(.5, transparent), + color-stop(.5, rgba(255, 255, 255, .2)), + color-stop(.75, rgba(255, 255, 255, .2)), + color-stop(.75, transparent), + to(transparent)); + z-index: 1; + -webkit-background-size: 50px 50px; + background-size: 50px 50px; + -webkit-animation: move 2s linear infinite; + overflow: hidden; +} + +@-webkit-keyframes move { + 0% { background-position: 0 0; } + 100% { background-position: 50px 50px; } +} + +.url { + color: grey; + max-width: 700px; + overflow: hidden; + display: block; +} + +#text-width-probe { + position: absolute; + top: -100px; + left: 0; +} diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html new file mode 100644 index 0000000..08bda79 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html @@ -0,0 +1,104 @@ +<!DOCTYPE HTML> +<html> +<head> + <title></title> + <script src="popup.js"></script> + <link rel="stylesheet" type="text/css" href="popup.css"> + <link rel="icon" href="icon19.png"> +</head> +<body> +<div id="outer"> + <div id="head"> + <a id="bad-chrome-version" hidden + href="https://www.google.com/intl/en/chrome/browser/beta.html"></a> + <span id="empty" hidden></span> + <input type="search" id="q" incremental> + <a href="#" id="clear-all"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <ellipse rx="30" ry="8" cx="50" cy="55" /> + <ellipse rx="20" ry="5" cx="50" cy="80" /> + <line x1="20" y1="55" x2="30" y2="83" /> + <line x1="40" y1="60" x2="43" y2="83" /> + <line x1="60" y1="60" x2="56" y2="83" /> + <line x1="80" y1="55" x2="69" y2="83" /> + <polygon points="25,20 35,20 35,40 40,40 30,55 20,40 25,40" /> + <polygon points="45,20 55,20 55,40 60,40 50,55 40,40 45,40" /> + <polygon points="65,20 75,20 75,40 80,40 70,55 60,40 65,40" /> + </g></svg></a> + <a href="#" id="open-folder"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <polygon points="20,20 20,80 60,80 60,30 35,30 35,20" /> + <polygon points="20,80 60,80 80,45 40,45" /> + </g></svg></a> + <div class="clearfix"> </div> + </div> + <div id="searching" hidden></div> + <div id="search-zero" hidden></div> + <div id="items"></div> + <a href="#" id="older" hidden></a> + <span id="loading-older" hidden></span> +</div> + +<div class="item" hidden> + <img class="icon" src="icon38.png"> + <div class="more" hidden> + <span class="more-left"> + <div class="start-time"></div> + <div class="complete-size"></div> + </span> + <a href="#" class="referrer"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <ellipse cx="35" cy="50" rx="20" ry="20" /> + <ellipse cx="60" cy="50" rx="20" ry="20" /> + </g></svg></a> + <a href="#" class="show-folder"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <polygon points="20,20 20,80 60,80 60,30 35,30 35,20" /> + <polygon points="20,80 60,80 80,45 40,45" /> + </g></svg></a> + <a href="#" class="remove-file"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <polygon points="20,45 45,20 75,20 75,75 20,75, 20,45 45,45 45,20" /> + <line x1="45" y1="45" x2="80" y2="80" /> + <line x1="80" y1="45" x2="45" y2="80" /> + </g></svg></a> + <a href="#" class="erase"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <ellipse rx="30" ry="10" cx="50" cy="40" /> + <ellipse rx="20" ry="10" cx="50" cy="70" /> + <line x1="20" y1="35" x2="30" y2="75" /> + <line x1="80" y1="35" x2="65" y2="75" /> + <line x1="50" y1="50" x2="50" y2="80" /> + </g></svg></a> + </div> + <a href="#" class="pause"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <rect x="25" y="25" rx="20" ry="20" width="15" height="50" /> + <rect x="55" y="25" rx="20" ry="20" width="15" height="50" /> + </g></svg></a> + <a href="#" class="resume"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <polygon points="25,25 75,50 25,75"> + </g></svg></a> + <a href="#" class="cancel"><svg viewBox="0 0 100 100"><g> + <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" /> + <line x1="25" y1="25" x2="75" y2="75" /> + <line x1="75" y1="25" x2="25" y2="75" /> + </g></svg></a> + <span class="file-url"> + <div class="file-url-head"> + <span class="removed"></span> + <a href="#" class="open-filename"></a> + <span class="error"></span> + <span class="in-progress"> + <span class="progress"></span> + <span class="time-left"></span> + </span> + </div> + <div class="meter"><span /></div> + <a href="#" class="url" download=""></a> + </span> +</div> +<span id="text-width-probe"></span> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js new file mode 100644 index 0000000..68212c9 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js @@ -0,0 +1,713 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function pointInElement(p, elem) { + return ((p.x >= elem.offsetLeft) && + (p.x <= (elem.offsetLeft + elem.offsetWidth)) && + (p.y >= elem.offsetTop) && + (p.y <= (elem.offsetTop + elem.offsetHeight))); +}; + +function setLastOpened() { + localStorage.popupLastOpened = (new Date()).getTime(); + chrome.runtime.sendMessage('poll'); +}; + +function loadI18nMessages() { + function setProperty(selector, prop, msg) { + document.querySelector(selector)[prop] = chrome.i18n.getMessage(msg); + } + + setProperty('title', 'innerText', 'tabTitle'); + setProperty('#q', 'placeholder', 'searchPlaceholder'); + setProperty('#clear-all', 'title', 'clearAllTitle'); + setProperty('#open-folder', 'title', 'openDownloadsFolderTitle'); + setProperty('#empty', 'innerText', 'zeroItems'); + setProperty('#searching', 'innerText', 'searching'); + setProperty('#search-zero', 'innerText', 'zeroSearchResults'); + setProperty('#older', 'innerText', 'showOlderDownloads'); + setProperty('#loading-older', 'innerText', 'loadingOlderDownloads'); + setProperty('.pause', 'title', 'pauseTitle'); + setProperty('.resume', 'title', 'resumeTitle'); + setProperty('.cancel', 'title', 'cancelTitle'); + setProperty('.show-folder', 'title', 'showInFolderTitle'); + setProperty('.erase', 'title', 'eraseTitle'); + setProperty('.url', 'title', 'retryTitle'); + setProperty('.referrer', 'title', 'referrerTitle'); + setProperty('.open-filename', 'title', 'openTitle'); + setProperty('#bad-chrome-version', 'innerText', 'badChromeVersion'); + setProperty('.remove-file', 'title', 'removeFileTitle'); + + document.querySelector('.progress').style.minWidth = + getTextWidth(formatBytes(1024 * 1024 * 1023.9) + '/' + + formatBytes(1024 * 1024 * 1023.9)) + 'px'; + + // This only covers {timeLeft,openWhenComplete}{Finishing,Days}. If + // ...Hours/Minutes/Seconds could be longer for any locale, then this should + // test them. + var max_time_left_width = 0; + for (var i = 0; i < 4; ++i) { + max_time_left_width = Math.max(max_time_left_width, getTextWidth( + formatTimeLeft(0 == (i % 2), + (i < 2) ? 0 : ((100 * 24) + 23) * 60 * 60 * 1000))); + } + document.querySelector('body div.item span.time-left').style.minWidth = + max_time_left_width + 'px'; +}; + +function getTextWidth(s) { + var probe = document.getElementById('text-width-probe'); + probe.innerText = s; + return probe.offsetWidth; +}; + +function formatDateTime(date) { + var now = new Date(); + var zpad_mins = ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); + if (date.getYear() != now.getYear()) { + return '' + (1900 + date.getYear()); + } else if ((date.getMonth() != now.getMonth()) || + (date.getDate() != now.getDate())) { + return date.getDate() + ' ' + chrome.i18n.getMessage( + 'month' + date.getMonth() + 'abbr'); + } else if (date.getHours() == 12) { + return '12' + zpad_mins + 'pm'; + } else if (date.getHours() > 12) { + return (date.getHours() - 12) + zpad_mins + 'pm'; + } + return date.getHours() + zpad_mins + 'am'; +} + +function formatBytes(n) { + if (n < 1024) { + return n + 'B'; + } + var prefixes = 'KMGTPEZY'; + var mul = 1024; + for (var i = 0; i < prefixes.length; ++i) { + if (n < (1024 * mul)) { + return (parseInt(n / mul) + '.' + parseInt(10 * ((n / mul) % 1)) + + prefixes[i] + 'B'); + } + mul *= 1024; + } + return '!!!'; +} + +function formatTimeLeft(openWhenComplete, ms) { + var prefix = openWhenComplete ? 'openWhenComplete' : 'timeLeft'; + if (ms < 1000) { + return chrome.i18n.getMessage(prefix + 'Finishing'); + } + var days = parseInt(ms / (24 * 60 * 60 * 1000)); + var hours = parseInt(ms / (60 * 60 * 1000)) % 24; + if (days) { + return chrome.i18n.getMessage(prefix + 'Days', [days, hours]); + } + var minutes = parseInt(ms / (60 * 1000)) % 60; + if (hours) { + return chrome.i18n.getMessage(prefix + 'Hours', [hours, minutes]); + } + var seconds = parseInt(ms / 1000) % 60; + if (minutes) { + return chrome.i18n.getMessage(prefix + 'Minutes', [minutes, seconds]); + } + return chrome.i18n.getMessage(prefix + 'Seconds', [seconds]); +} + +function binarySearch(array, target, cmp) { + var low = 0, high = array.length - 1, i, comparison; + while (low <= high) { + i = (low + high) >> 1; + comparison = cmp(target, array[i]); + if (comparison < 0) { + low = i + 1; + } else if (comparison > 0) { + high = i - 1; + } else { + return i; + } + } + return i; +}; + +function arrayFrom(seq) { + return Array.prototype.slice.apply(seq); +}; + +function DownloadItem(data) { + var item = this; + for (var prop in data) { + item[prop] = data[prop]; + } + item.startTime = new Date(item.startTime); + if (item.canResume == undefined) { + DownloadItem.canResumeHack = true; + } + + item.div = document.querySelector('body>div.item').cloneNode(true); + item.div.id = 'item' + item.id; + item.div.item = item; + + var items_div = document.getElementById('items'); + if ((items_div.childNodes.length == 0) || + (item.startTime.getTime() < items_div.childNodes[ + items_div.childNodes.length - 1].item.startTime.getTime())) { + items_div.appendChild(item.div); + } else if (item.startTime.getTime() > + items_div.childNodes[0].item.startTime.getTime()) { + items_div.insertBefore(item.div, items_div.childNodes[0]); + } else { + var adjacent_div = items_div.childNodes[ + binarySearch(arrayFrom(items_div.childNodes), + item.startTime.getTime(), + function(target, other) { + return target - other.item.startTime.getTime(); + })]; + var adjacent_item = adjacent_div.item; + if (adjacent_item.startTime.getTime() < item.startTime.getTime()) { + items_div.insertBefore(item.div, adjacent_div); + } else { + items_div.insertBefore(item.div, adjacent_div.nextSibling); + } + } + + item.getElement('referrer').onclick = function() { + chrome.tabs.create({url: item.referrer}); + return false; + }; + item.getElement('open-filename').onclick = function() { + item.open(); + return false; + }; + item.getElement('open-filename').ondragstart = function() { + item.drag(); + return false; + }; + item.getElement('pause').onclick = function() { + item.pause(); + return false; + }; + item.getElement('cancel').onclick = function() { + item.cancel(); + return false; + }; + item.getElement('resume').onclick = function() { + item.resume(); + return false; + }; + item.getElement('show-folder').onclick = function() { + item.show(); + return false; + }; + item.getElement('remove-file').onclick = function() { + item.removeFile(); + return false; + }; + item.getElement('erase').onclick = function() { + item.erase(); + return false; + }; + + item.more_mousemove = function(evt) { + if (item.getElement('more') && + (pointInElement(evt, item.div) || + pointInElement(evt, item.getElement('more')))) { + return; + } + if (item.getElement('more')) { + item.getElement('more').hidden = true; + } + window.removeEventListener('mousemove', item.more_mousemove); + }; + [item.getElement('icon'), item.getElement('more')].concat( + item.getElement('more').children).forEach(function(elem) { + elem.onmouseover = function() { + arrayFrom(items_div.children).forEach(function(other) { + if (other.item != item) { + other.item.getElement('more').hidden = true; + } + }); + item.getElement('more').hidden = false; + item.getElement('more').style.top = + (item.div.offsetTop + item.div.offsetHeight) + 'px'; + item.getElement('more').style.left = item.div.offsetLeft + 'px'; + if (window.innerHeight < (parseInt(item.getElement('more').style.top) + + item.getElement('more').offsetHeight)) { + item.getElement('more').style.top = ( + item.div.offsetTop - item.getElement('more').offsetHeight) + 'px'; + } + window.addEventListener('mousemove', item.more_mousemove); + }; + }); + + if (item.referrer) { + item.getElement('referrer').href = item.referrer; + } else { + item.getElement('referrer').hidden = true; + } + item.getElement('url').href = item.url; + item.getElement('url').innerText = item.url; + item.render(); +} +DownloadItem.canResumeHack = false; + +DownloadItem.prototype.getElement = function(name) { + return document.querySelector('#item' + this.id + ' .' + name); +}; + +DownloadItem.prototype.render = function() { + var item = this; + var now = new Date(); + var in_progress = (item.state == 'in_progress') + var openable = (item.state != 'interrupted') && item.exists && !item.deleted; + + item.startTime = new Date(item.startTime); + if (DownloadItem.canResumeHack) { + item.canResume = in_progress && item.paused; + } + if (item.filename) { + item.basename = item.filename.substring(Math.max( + item.filename.lastIndexOf('\\'), + item.filename.lastIndexOf('/')) + 1); + } + if (item.estimatedEndTime) { + item.estimatedEndTime = new Date(item.estimatedEndTime); + } + if (item.endTime) { + item.endTime = new Date(item.endTime); + } + + if (item.filename && !item.icon_url) { + chrome.downloads.getFileIcon( + item.id, + {'size': 32}, + function(icon_url) { + item.getElement('icon').hidden = !icon_url; + if (icon_url) { + item.icon_url = icon_url; + item.getElement('icon').src = icon_url; + } + }); + } + + item.getElement('removed').style.display = openable ? 'none' : 'inline-block'; + item.getElement('open-filename').style.display = ( + openable ? 'inline-block' : 'none'); + item.getElement('in-progress').hidden = !in_progress; + item.getElement('pause').style.display = ( + !in_progress || item.paused) ? 'none' : 'inline-block'; + item.getElement('resume').style.display = ( + !in_progress || !item.canResume) ? 'none' : 'inline-block'; + item.getElement('cancel').style.display = ( + !in_progress ? 'none' : 'inline-block'); + item.getElement('remove-file').hidden = ( + (item.state != 'complete') || + !item.exists || + item.deleted || + !chrome.downloads.removeFile); + item.getElement('erase').hidden = in_progress; + + var could_progress = in_progress || item.canResume; + item.getElement('progress').style.display = ( + could_progress ? 'inline-block' : 'none'); + item.getElement('meter').hidden = !could_progress || !item.totalBytes; + + item.getElement('removed').innerText = item.basename; + item.getElement('open-filename').innerText = item.basename; + + if (!item.getElement('error').hidden) { + if (item.error) { + // TODO(benjhayden) When https://codereview.chromium.org/16924017/ is + // released, set minimum_chrome_version and remove the error_N messages. + item.getElement('error').innerText = chrome.i18n.getMessage( + 'error_' + item.error); + if (!item.getElement('error').innerText) { + item.getElement('error').innerText = item.error; + } + } else if (!openable) { + item.getElement('error').innerText = chrome.i18n.getMessage( + 'errorRemoved'); + } + } + + item.getElement('complete-size').innerText = formatBytes( + item.bytesReceived); + if (item.totalBytes && (item.state != 'complete')) { + item.getElement('progress').innerText = ( + item.getElement('complete-size').innerText + '/' + + formatBytes(item.totalBytes)); + item.getElement('meter').children[0].style.width = parseInt( + 100 * item.bytesReceived / item.totalBytes) + '%'; + } + + if (in_progress) { + if (item.estimatedEndTime && !item.paused) { + var openWhenComplete = false; + try { + openWhenComplete = JSON.parse(localStorage.openWhenComplete).indexOf( + item.id) >= 0; + } catch (e) { + } + item.getElement('time-left').innerText = formatTimeLeft( + openWhenComplete, item.estimatedEndTime.getTime() - now.getTime()); + } else { + item.getElement('time-left').innerText = String.fromCharCode(160); + } + } + + if (item.startTime) { + item.getElement('start-time').innerText = formatDateTime( + item.startTime); + } + + this.maybeAccept(); +}; + +DownloadItem.prototype.onChanged = function(delta) { + for (var key in delta) { + if (key != 'id') { + this[key] = delta[key].current; + } + } + this.render(); + if (delta.state) { + setLastOpened(); + } + if ((this.state == 'in_progress') && !this.paused) { + DownloadManager.startPollingProgress(); + } +}; + +DownloadItem.prototype.onErased = function() { + window.removeEventListener('mousemove', this.more_mousemove); + document.getElementById('items').removeChild(this.div); +}; + +DownloadItem.prototype.drag = function() { + chrome.downloads.drag(this.id); +}; + +DownloadItem.prototype.show = function() { + chrome.downloads.show(this.id); +}; + +DownloadItem.prototype.open = function() { + if (this.state == 'complete') { + chrome.downloads.open(this.id); + return; + } + chrome.runtime.sendMessage({openWhenComplete:this.id}); +}; + +DownloadItem.prototype.removeFile = function() { + chrome.downloads.removeFile(this.id); + this.deleted = true; + this.render(); +}; + +DownloadItem.prototype.erase = function() { + chrome.downloads.erase({id: this.id}); +}; + +DownloadItem.prototype.pause = function() { + chrome.downloads.pause(this.id); +}; + +DownloadItem.prototype.resume = function() { + chrome.downloads.resume(this.id); +}; + +DownloadItem.prototype.cancel = function() { + chrome.downloads.cancel(this.id); +}; + +DownloadItem.prototype.maybeAccept = function() { + // This function is safe to call at any time for any item, and it will always + // do the right thing, which is to display the danger prompt only if the item + // is in_progress and dangerous, and if the prompt is not already displayed. + if ((this.state != 'in_progress') || + (this.danger == 'safe') || + (this.danger == 'accepted') || + DownloadItem.prototype.maybeAccept.accepting_danger) { + return; + } + DownloadItem.prototype.maybeAccept.accepting_danger = true; + chrome.downloads.acceptDanger(this.id, function() { + DownloadItem.prototype.maybeAccept.accepting_danger = false; + arrayFrom(document.getElementById('items').childNodes).forEach( + function(item_div) { item_div.item.maybeAccept(); }); + }); +}; +DownloadItem.prototype.maybeAccept.accepting_danger = false; + +var DownloadManager = {}; + +DownloadManager.showingOlder = false; + +DownloadManager.getItem = function(id) { + var item_div = document.getElementById('item' + id); + return item_div ? item_div.item : null; +}; + +DownloadManager.getOrCreate = function(data) { + var item = DownloadManager.getItem(data.id); + return item ? item : new DownloadItem(data); +}; + +DownloadManager.forEachItem = function(cb) { + // Calls cb(item, index) in the order that they are displayed, i.e. in order + // of decreasing startTime. + arrayFrom(document.getElementById('items').childNodes).forEach( + function(item_div, index) { cb(item_div.item, index); }); +}; + +DownloadManager.startPollingProgress = function() { + if (DownloadManager.startPollingProgress.tid < 0) { + DownloadManager.startPollingProgress.tid = setTimeout( + DownloadManager.startPollingProgress.pollProgress, + DownloadManager.startPollingProgress.MS); + } +} +DownloadManager.startPollingProgress.MS = 200; +DownloadManager.startPollingProgress.tid = -1; +DownloadManager.startPollingProgress.pollProgress = function() { + DownloadManager.startPollingProgress.tid = -1; + chrome.downloads.search({state: 'in_progress', paused: false}, + function(results) { + if (!results.length) + return; + results.forEach(function(result) { + var item = DownloadManager.getOrCreate(result); + for (var prop in result) { + item[prop] = result[prop]; + } + item.render(); + if ((item.state == 'in_progress') && !item.paused) { + DownloadManager.startPollingProgress(); + } + }); + }); +}; + +DownloadManager.showNew = function() { + var any_items = (document.getElementById('items').childNodes.length > 0); + document.getElementById('empty').style.display = + any_items ? 'none' : 'inline-block'; + document.getElementById('clear-all').hidden = !any_items; + document.getElementById('open-folder').style.float = + any_items ? 'right' : 'none'; + + var query_search = document.getElementById('q'); + query_search.hidden = !any_items; + + if (!any_items) { + return; + } + var old_ms = (new Date()).getTime() - kOldMs; + var any_hidden = false; + var any_showing = false; + // First show up to kShowNewMax items newer than kOldMs. If there aren't any + // items newer than kOldMs, then show up to kShowNewMax items of any age. If + // there are any hidden items, show the Show Older button. + DownloadManager.forEachItem(function(item, index) { + item.div.hidden = !DownloadManager.showingOlder && ( + (item.startTime.getTime() < old_ms) || (index >= kShowNewMax)); + any_hidden = any_hidden || item.div.hidden; + any_showing = any_showing || !item.div.hidden; + }); + if (!any_showing) { + any_hidden = false; + DownloadManager.forEachItem(function(item, index) { + item.div.hidden = !DownloadManager.showingOlder && (index >= kShowNewMax); + any_hidden = any_hidden || item.div.hidden; + any_showing = any_showing || !item.div.hidden; + }); + } + document.getElementById('older').hidden = !any_hidden; + + query_search.focus(); +}; + +DownloadManager.showOlder = function() { + DownloadManager.showingOlder = true; + var loading_older_span = document.getElementById('loading-older'); + document.getElementById('older').hidden = true; + loading_older_span.hidden = false; + chrome.downloads.search({}, function(results) { + results.forEach(function(result) { + var item = DownloadManager.getOrCreate(result); + item.div.hidden = false; + }); + loading_older_span.hidden = true; + }); +}; + +DownloadManager.onSearch = function() { + // split string by space, but ignore space in quotes + // http://stackoverflow.com/questions/16261635 + var query = document.getElementById('q').value.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!query) { + DownloadManager.showNew(); + document.getElementById('search-zero').hidden = true; + } else { + query = query.map(function(term) { + // strip quotes + return (term.match(/\s/) && + term[0].match(/["']/) && + term[term.length - 1] == term[0]) ? + term.substr(1, term.length - 2) : term; + }); + var searching = document.getElementById('searching'); + searching.hidden = false; + chrome.downloads.search({query: query}, function(results) { + document.getElementById('older').hidden = true; + DownloadManager.forEachItem(function(item) { + item.div.hidden = true; + }); + results.forEach(function(result) { + DownloadManager.getOrCreate(result).div.hidden = false; + }); + searching.hidden = true; + document.getElementById('search-zero').hidden = (results.length != 0); + }); + } +}; + +DownloadManager.clearAll = function() { + DownloadManager.forEachItem(function(item) { + if (!item.div.hidden) { + item.erase(); + // The onErased handler should circle back around to loadItems. + } + }); +}; + +var kShowNewMax = 50; +var kOldMs = 1000 * 60 * 60 * 24 * 7; + +// These settings can be tuned by modifying localStorage in dev-tools. +if ('kShowNewMax' in localStorage) { + kShowNewMax = parseInt(localStorage.kShowNewMax); +} +if ('kOldMs' in localStorage) { + kOldMs = parseInt(localStorage.kOldMs); +} + +DownloadManager.loadItems = function() { + // Request up to kShowNewMax + 1, but only display kShowNewMax; the +1 is a + // probe to see if there are any older downloads. + // TODO(benjhayden) When https://codereview.chromium.org/16924017/ is + // released, set minimum_chrome_version and remove this try/catch. + try { + chrome.downloads.search({ + orderBy: ['-startTime'], + limit: kShowNewMax + 1}, + function(results) { + DownloadManager.loadItems.items = results; + DownloadManager.loadItems.onLoaded(); + }); + } catch (exc) { + chrome.downloads.search({ + orderBy: '-startTime', + limit: kShowNewMax + 1}, + function(results) { + DownloadManager.loadItems.items = results; + DownloadManager.loadItems.onLoaded(); + }); + } +}; +DownloadManager.loadItems.items = []; +DownloadManager.loadItems.window_loaded = false; + +DownloadManager.loadItems.onLoaded = function() { + if (!DownloadManager.loadItems.window_loaded) { + return; + } + DownloadManager.loadItems.items.forEach(function(item) { + DownloadManager.getOrCreate(item); + }); + DownloadManager.loadItems.items = []; + DownloadManager.showNew(); +}; + +DownloadManager.loadItems.onWindowLoaded = function() { + DownloadManager.loadItems.window_loaded = true; + DownloadManager.loadItems.onLoaded(); +}; + +// If this extension is installed on a stable-channel chrome, where the +// downloads API is not available, do not use the downloads API, and link to the +// beta channel. +if (chrome.downloads) { + // Start searching ASAP, don't wait for onload. + DownloadManager.loadItems(); + + chrome.downloads.onCreated.addListener(function(item) { + DownloadManager.getOrCreate(item); + DownloadManager.showNew(); + DownloadManager.startPollingProgress(); + }); + + chrome.downloads.onChanged.addListener(function(delta) { + var item = DownloadManager.getItem(delta.id); + if (item) { + item.onChanged(delta); + } + }); + + chrome.downloads.onErased.addListener(function(id) { + var item = DownloadManager.getItem(id); + if (!item) { + return; + } + item.onErased(); + DownloadManager.loadItems(); + }); + + window.onload = function() { + document.body.style.minWidth = ( + document.getElementById('q').offsetWidth + + document.getElementById('clear-all').offsetWidth + + document.getElementById('open-folder').offsetWidth) + 'px'; + setLastOpened(); + loadI18nMessages(); + DownloadManager.loadItems.onWindowLoaded(); + document.getElementById('older').onclick = function() { + DownloadManager.showOlder(); + return false; + }; + document.getElementById('q').onsearch = function() { + DownloadManager.onSearch(); + }; + document.getElementById('clear-all').onclick = function() { + DownloadManager.clearAll(); + return false; + }; + if (chrome.downloads.showDefaultFolder) { + document.getElementById('open-folder').onclick = function() { + chrome.downloads.showDefaultFolder(); + return false; + }; + } else { + document.getElementById('open-folder').hidden = true; + } + }; +} else { + // The downloads API is not available. + // TODO(benjhayden) Remove this when minimum_chrome_version is set. + window.onload = function() { + loadI18nMessages(); + var bad_version = document.getElementById('bad-chrome-version'); + bad_version.hidden = false; + bad_version.onclick = function() { + chrome.tabs.create({url: bad_version.href}); + return false; + }; + document.getElementById('empty').style.display = 'none'; + document.getElementById('q').style.display = 'none'; + document.getElementById('open-folder').style.display = 'none'; + document.getElementById('clear-all').style.display = 'none'; + }; +} diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_open/_locales/en/messages.json b/chrome/common/extensions/docs/examples/api/downloads/download_open/_locales/en/messages.json new file mode 100644 index 0000000..5a6df11 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_open/_locales/en/messages.json @@ -0,0 +1,9 @@ +{"extName": { + "message": "Download and Open Context Menu Button", + "description": "Extension name"}, + "extDesc": { + "message": "Download and Open Context Menu Button", + "description": "Extension description"}, + "openContextMenuTitle": { + "message": "Download and Open", + "description": "context menu button text"}} diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_open/background.js b/chrome/common/extensions/docs/examples/api/downloads/download_open/background.js new file mode 100644 index 0000000..bfc40ec --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_open/background.js @@ -0,0 +1,48 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function getOpeningIds() { + var ids = []; + try { + ids = JSON.parse(localStorage.openWhenComplete); + } catch (e) { + localStorage.openWhenComplete = JSON.stringify(ids); + } + return ids; +} + +function setOpeningIds(ids) { + localStorage.openWhenComplete = JSON.stringify(ids); +} + +chrome.downloads.onChanged.addListener(function(delta) { + if (!delta.state || + (delta.state.current != 'complete')) { + return; + } + var ids = getOpeningIds(); + if (ids.indexOf(delta.id) < 0) { + return; + } + chrome.downloads.open(delta.id); + ids.splice(ids.indexOf(delta.id), 1); + setOpeningIds(ids); +}); + +chrome.contextMenus.onClicked.addListener(function(info, tab) { + chrome.downloads.download({url: info.linkUrl}, function(downloadId) { + var ids = getOpeningIds(); + if (ids.indexOf(downloadId) >= 0) { + return; + } + ids.push(downloadId); + setOpeningIds(ids); + }); +}); + +chrome.contextMenus.create({ + id: 'open', + title: chrome.i18n.getMessage('openContextMenuTitle'), + contexts: ['link'], +}); diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_open/icon128.png b/chrome/common/extensions/docs/examples/api/downloads/download_open/icon128.png Binary files differnew file mode 100644 index 0000000..ff20437 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_open/icon128.png diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_open/icon16.png b/chrome/common/extensions/docs/examples/api/downloads/download_open/icon16.png Binary files differnew file mode 100644 index 0000000..ad88a21 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_open/icon16.png diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_open/manifest.json b/chrome/common/extensions/docs/examples/api/downloads/download_open/manifest.json new file mode 100644 index 0000000..98a49ab --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/downloads/download_open/manifest.json @@ -0,0 +1,8 @@ +{"name": "__MSG_extName__", + "version": "0.1", + "manifest_version": 2, + "description": "__MSG_extDesc__", + "icons": {"16": "icon16.png", "128": "icon128.png"}, + "background": {"persistent": false, "scripts": ["background.js"]}, + "default_locale": "en", + "permissions": ["contextMenus", "downloads", "downloads.open"]} |