X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fmodules%2Fdownloads.jsm;h=faee6e22a7a1ce5a28bcda6019c2e99dc9c05a68;hb=70740024f9c028c1fd63e1a1850ab062ff956054;hp=403c1e418a4642ec6867d1a6b47babb0b8557549;hpb=eeed0be1a8abf7e3c97f43b63c1d595e940fef21;p=dactyl.git diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm index 403c1e4..faee6e2 100644 --- a/common/modules/downloads.jsm +++ b/common/modules/downloads.jsm @@ -7,7 +7,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("downloads", { exports: ["Download", "Downloads", "downloads"], - use: ["io", "prefs", "services", "util"] + use: ["io", "messages", "prefs", "services", "util"] }, this); Cu.import("resource://gre/modules/DownloadUtils.jsm", this); @@ -32,30 +32,38 @@ var Download = Class("Download", { - {self.displayName} {self.targetFile.path} - Pause - Remove - Resume - Retry - Cancel - Delete + {_("download.action.Pause")} + {_("download.action.Remove")} + {_("download.action.Resume")} + {_("download.action.Retry")} + {_("download.action.Cancel")} + {_("download.action.Delete")} / + {self.source.spec} , this.list.document, this.nodes); + this.nodes.launch.addEventListener("click", function (event) { + if (event.button == 0) { + event.preventDefault(); + self.command("launch"); + } + }, false); + self.updateStatus(); return self; }, @@ -78,10 +86,13 @@ var Download = Class("Download", { })), command: function command(name) { - util.assert(set.has(this.allowedCommands, name), "Unknown command"); - util.assert(this.allowedCommands[name], "Command not allowed"); + util.assert(Set.has(this.allowedCommands, name), _("download.unknownCommand")); + util.assert(this.allowedCommands[name], _("download.commandNotAllowed")); - services.downloadManager[name + "Download"](this.id); + if (Set.has(this.commands, name)) + this.commands[name].call(this); + else + services.downloadManager[name + "Download"](this.id); }, commands: { @@ -95,7 +106,7 @@ var Download = Class("Download", { function action() { try { if (this.MIMEInfo && this.MIMEInfo.preferredAction == this.MIMEInfo.useHelperApp) - this.MIMEInfo.launchWithFile(file) + this.MIMEInfo.launchWithFile(file); else file.launch(); } @@ -106,7 +117,7 @@ var Download = Class("Download", { let file = io.File(this.targetFile); if (file.isExecutable() && prefs.get("browser.download.manager.alertOnEXEOpen", true)) - this.list.modules.commandline.input("This will launch an executable download. Continue? (yes/[no]/always) ", + this.list.modules.commandline.input(_("download.prompt.launchExecutable") + " ", function (resp) { if (/^a(lways)$/i.test(resp)) { prefs.set("browser.download.manager.alertOnEXEOpen", false); @@ -120,26 +131,48 @@ var Download = Class("Download", { } }, - compare: function compare(other) String.localeCompare(this.displayName, other.displayName), + _compare: { + active: function (a, b) a.alive - b.alive, + complete: function (a, b) a.percentComplete - b.percentComplete, + date: function (a, b) a.startTime - b.startTime, + filename: function (a, b) String.localeCompare(a.targetFile.leafName, b.targetFile.leafName), + size: function (a, b) a.size - b.size, + speed: function (a, b) a.speed - b.speed, + time: function (a, b) a.timeRemaining - b.timeRemaining, + url: function (a, b) String.localeCompare(a.source.spec, b.source.spec) + }, + + compare: function compare(other) values(this.list.sortOrder).map(function (order) { + let val = this._compare[order.substr(1)](this, other); + + return (order[0] == "-") ? -val : val; + }, this).nth(util.identity, 0) || 0, timeRemaining: Infinity, updateProgress: function updateProgress() { let self = this.__proto__; - if (this.amountTransferred === this.size) + if (this.amountTransferred === this.size) { + this.nodes.speed.textContent = ""; this.nodes.time.textContent = ""; - else if (this.speed == 0 || this.size == 0) - this.nodes.time.textContent = "Unknown"; + } else { - let seconds = (this.size - this.amountTransferred) / this.speed; - [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining); - if (this.timeRemaining) - this.nodes.time.textContent = util.formatSeconds(this.timeRemaining); - else - this.nodes.time.textContent = "~1 second"; + this.nodes.speed.textContent = util.formatBytes(this.speed, 1, true) + "/s"; + + if (this.speed == 0 || this.size == 0) + this.nodes.time.textContent = _("download.unknown"); + else { + let seconds = (this.size - this.amountTransferred) / this.speed; + [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining); + if (this.timeRemaining) + this.nodes.time.textContent = util.formatSeconds(this.timeRemaining); + else + this.nodes.time.textContent = _("download.almostDone"); + } } - let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : "Unknown"; + + let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : _("download.unknown"); let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$"); this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, ""); @@ -165,7 +198,8 @@ var DownloadList = Class("DownloadList", XPCOM([Ci.nsIDownloadProgressListener, Ci.nsIObserver, Ci.nsISupportsWeakReference]), { - init: function init(modules, filter) { + init: function init(modules, filter, sort) { + this.sortOrder = sort; this.modules = modules; this.filter = filter && filter.toLowerCase(); this.nodes = { @@ -173,6 +207,7 @@ var DownloadList = Class("DownloadList", }; this.downloads = {}; }, + cleanup: function cleanup() { this.observe.unregister(); services.downloadManager.removeListener(this); @@ -182,26 +217,28 @@ var DownloadList = Class("DownloadList", util.xmlToDom( - Title - Status + {_("title.Title")} + {_("title.Status")} - Progress + {_("title.Progress")} - Time remaining - Source + {_("title.Speed")} + {_("title.Time remaining")} + {_("title.Source")}
- + @@ -253,6 +290,17 @@ var DownloadList = Class("DownloadList", } }, + sort: function sort() { + let list = values(this.downloads).sort(function (a, b) a.compare(b)); + + for (let [i, download] in iter(list)) + if (this.nodes.list.childNodes[i + 1] != download.nodes.row) + this.nodes.list.insertBefore(download.nodes.row, + this.nodes.list.childNodes[i + 1]); + }, + + shouldSort: function shouldSort() Array.some(arguments, function (val) this.sortOrder.some(function (v) v.substr(1) == val), this), + update: function update() { for (let node in values(this.nodes)) if (node.update && node.update != update) @@ -277,9 +325,12 @@ var DownloadList = Class("DownloadList", let active = downloads.filter(function (dl) dl.alive).length; if (active) - this.nodes.total.textContent = active + " active"; - else for (let key in values(["total", "percent", "time"])) + this.nodes.total.textContent = _("download.nActive", active); + else for (let key in values(["total", "percent", "speed", "time"])) this.nodes[key].textContent = ""; + + if (this.shouldSort("complete", "size", "speed", "time")) + this.sort(); }, observers: { @@ -305,11 +356,15 @@ var DownloadList = Class("DownloadList", this.nodes.list.scrollIntoView(false); } this.update(); + + if (this.shouldSort("active")) + this.sort(); } catch (e) { util.reportError(e); } }, + onProgressChange: function (webProgress, request, curProgress, maxProgress, curTotalProgress, maxTotalProgress, @@ -328,17 +383,85 @@ var DownloadList = Class("DownloadList", var Downloads = Module("downloads", { }, { }, { - commands: function (dactyl, modules, window) { - const { commands } = modules; + commands: function initCommands(dactyl, modules, window) { + const { commands, CommandOption } = modules; commands.add(["downl[oads]", "dl"], "Display the downloads list", function (args) { - let downloads = DownloadList(modules, args[0]); + let downloads = DownloadList(modules, args[0], args["-sort"]); modules.commandline.echo(downloads); }, { - argCount: "?" + argCount: "?", + options: [ + { + names: ["-sort", "-s"], + description: "Sort order (see 'downloadsort')", + type: CommandOption.LIST, + get default() modules.options["downloadsort"], + completer: function (context, args) modules.options.get("downloadsort").completer(context, { values: args["-sort"] }), + validator: function (value) modules.options.get("downloadsort").validator(value) + } + ] + }); + + commands.add(["dlc[lear]"], + "Clear completed downloads", + function (args) { services.downloadManager.cleanUp(); }); + }, + options: function initOptions(dactyl, modules, window) { + const { options } = modules; + + if (false) + options.add(["downloadcolumns", "dlc"], + "The columns to show in the download manager", + "stringlist", "filename,state,buttons,progress,percent,time,url", + { + values: { + buttons: "Control buttons", + filename: "Target filename", + percent: "Percent complete", + size: "File size", + speed: "Download speed", + state: "The download's state", + time: "Time remaining", + url: "Source URL" + } + }); + + options.add(["downloadsort", "dlsort", "dls"], + ":downloads sort order", + "stringlist", "-active,+filename", + { + values: { + active: "Whether download is active", + complete: "Percent complete", + date: "Date and time the download began", + filename: "Target filename", + size: "File size", + speed: "Download speed", + time: "Time remaining", + url: "Source URL" + }, + + completer: function (context, extra) { + let seen = Set.has(Set(extra.values.map(function (val) val.substr(1)))); + + context.completions = iter(this.values).filter(function ([k, v]) !seen(k)) + .map(function ([k, v]) [["+" + k, [v, " (", _("sort.ascending"), ")"].join("")], + ["-" + k, [v, " (", _("sort.descending"), ")"].join("")]]) + .flatten().array; + }, + + has: function () Array.some(arguments, function (val) this.value.some(function (v) v.substr(1) == val)), + + validator: function (value) { + let seen = {}; + return value.every(function (val) /^[+-]/.test(val) && Set.has(this.values, val.substr(1)) + && !Set.add(seen, val.substr(1)), + this) && value.length; + } }); } });
Totals: {_("title.Totals")}:  - Clear + {_("download.action.Clear")} / +