-// Copyright (c) 2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2011-2012 Kris Maglione <maglione.k@gmail.com>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
"use strict";
-Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("downloads", {
exports: ["Download", "Downloads", "downloads"],
- use: ["io", "messages", "prefs", "services", "util"]
-}, this);
+ require: ["util"]
+});
+
+lazyRequire("overlay", ["overlay"]);
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
+var MAX_LOAD_TIME = 10 * 1000;
+
let prefix = "DOWNLOAD_";
var states = iter([v, k.slice(prefix.length).toLowerCase()]
for ([k, v] in Iterator(Ci.nsIDownloadManager))
var Download = Class("Download", {
init: function init(id, list) {
- let self = XPCSafeJSObjectWrapper(services.downloadManager.getDownload(id));
- self.__proto__ = this;
- this.instance = this;
+ let self = this;
+ this.download = services.downloadManager.getDownload(id);
this.list = list;
this.nodes = {
commandTarget: self
};
- util.xmlToDom(
- <tr highlight="Download" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
- <td highlight="DownloadTitle">
- <span highlight="Link">
- <a key="launch"
- href={self.target.spec} path={self.targetFile.path}>{self.displayName}</a>
- <span highlight="LinkInfo">{self.targetFile.path}</span>
- </span>
- </td>
- <td highlight="DownloadState" key="state"/>
- <td highlight="DownloadButtons Buttons">
- <a highlight="Button" href="javascript:0" key="pause">{_("download.action.Pause")}</a>
- <a highlight="Button" href="javascript:0" key="remove">{_("download.action.Remove")}</a>
- <a highlight="Button" href="javascript:0" key="resume">{_("download.action.Resume")}</a>
- <a highlight="Button" href="javascript:0" key="retry">{_("download.action.Retry")}</a>
- <a highlight="Button" href="javascript:0" key="cancel">{_("download.action.Cancel")}</a>
- <a highlight="Button" href="javascript:0" key="delete">{_("download.action.Delete")}</a>
- </td>
- <td highlight="DownloadProgress" key="progress">
- <span highlight="DownloadProgressHave" key="progressHave"
- />/<span highlight="DownloadProgressTotal" key="progressTotal"/>
- </td>
- <td highlight="DownloadPercent" key="percent"/>
- <td highlight="DownloadSpeed" key="speed"/>
- <td highlight="DownloadTime" key="time"/>
- <td><a highlight="DownloadSource" key="source" href={self.source.spec}>{self.source.spec}</a></td>
- </tr>,
+ DOM.fromJSON(
+ ["tr", { highlight: "Download", key: "row" },
+ ["td", { highlight: "DownloadTitle" },
+ ["span", { highlight: "Link" },
+ ["a", { key: "launch", href: self.target.spec, path: self.targetFile.path },
+ self.displayName],
+ ["span", { highlight: "LinkInfo" },
+ self.targetFile.path]]],
+ ["td", { highlight: "DownloadState", key: "state" }],
+ ["td", { highlight: "DownloadButtons Buttons" },
+ ["a", { highlight: "Button", href: "javascript:0", key: "pause" }, _("download.action.Pause")],
+ ["a", { highlight: "Button", href: "javascript:0", key: "remove" }, _("download.action.Remove")],
+ ["a", { highlight: "Button", href: "javascript:0", key: "resume" }, _("download.action.Resume")],
+ ["a", { highlight: "Button", href: "javascript:0", key: "retry" }, _("download.action.Retry")],
+ ["a", { highlight: "Button", href: "javascript:0", key: "cancel" }, _("download.action.Cancel")],
+ ["a", { highlight: "Button", href: "javascript:0", key: "delete" }, _("download.action.Delete")]],
+ ["td", { highlight: "DownloadProgress", key: "progress" },
+ ["span", { highlight: "DownloadProgressHave", key: "progressHave" }],
+ "/",
+ ["span", { highlight: "DownloadProgressTotal", key: "progressTotal" }]],,
+ ["td", { highlight: "DownloadPercent", key: "percent" }],
+ ["td", { highlight: "DownloadSpeed", key: "speed" }],
+ ["td", { highlight: "DownloadTime", key: "time" }],
+ ["td", {},
+ ["a", { highlight: "DownloadSource", key: "source", href: self.source.spec },
+ self.source.spec]]],
this.list.document, this.nodes);
this.nodes.launch.addEventListener("click", function (event) {
get alive() this.inState(["downloading", "notstarted", "paused", "queued", "scanning"]),
- allowedCommands: Class.memoize(function () let (self = this) ({
+ allowedCommands: Class.Memoize(function () let (self = this) ({
get cancel() self.cancelable && self.inState(["downloading", "paused", "starting"]),
get delete() !this.cancel && self.targetFile.exists(),
get launch() self.targetFile.exists() && self.inState(["finished"]),
function action() {
try {
if (this.MIMEInfo && this.MIMEInfo.preferredAction == this.MIMEInfo.useHelperApp)
- this.MIMEInfo.launchWithFile(file);
+ this.MIMEInfo.launchWithFile(file.file);
else
file.launch();
}
}
}
- let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : _("download.unknown");
+ let total = this.nodes.progressTotal.textContent = this.size || !this.nActive ? 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, "");
this.updateProgress();
}
});
+Object.keys(XPCOMShim([Ci.nsIDownload])).forEach(function (key) {
+ if (!(key in Download.prototype))
+ Object.defineProperty(Download.prototype, key, {
+ get: function get() this.download[key],
+ set: function set(val) this.download[key] = val,
+ configurable: true
+ });
+});
var DownloadList = Class("DownloadList",
XPCOM([Ci.nsIDownloadProgressListener,
services.downloadManager.removeListener(this);
},
- message: Class.memoize(function () {
-
- util.xmlToDom(<table highlight="Downloads" key="list" xmlns={XHTML}>
- <tr highlight="DownloadHead">
- <span>{_("title.Title")}</span>
- <span>{_("title.Status")}</span>
- <span/>
- <span>{_("title.Progress")}</span>
- <span/>
- <span>{_("title.Speed")}</span>
- <span>{_("title.Time remaining")}</span>
- <span>{_("title.Source")}</span>
- </tr>
- <tr highlight="Download"><span><div style="min-height: 1ex; /* FIXME */"/></span></tr>
- <tr highlight="Download" key="totals" active="true">
- <td><span highlight="Title">{_("title.Totals")}:</span> <span key="total"/></td>
- <td/>
- <td highlight="DownloadButtons">
- <a highlight="Button" href="javascript:0" key="clear">{_("download.action.Clear")}</a>
- </td>
- <td highlight="DownloadProgress" key="progress">
- <span highlight="DownloadProgressHave" key="progressHave"
- />/<span highlight="DownloadProgressTotal" key="progressTotal"/>
- </td>
- <td highlight="DownloadPercent" key="percent"/>
- <td highlight="DownloadSpeed" key="speed"/>
- <td highlight="DownloadTime" key="time"/>
- <td/>
- </tr>
- </table>, this.document, this.nodes);
-
+ message: Class.Memoize(function () {
+
+ DOM.fromJSON(["table", { highlight: "Downloads", key: "list" },
+ ["tr", { highlight: "DownloadHead", key: "head" },
+ ["span", {}, _("title.Title")],
+ ["span", {}, _("title.Status")],
+ ["span"],
+ ["span", {}, _("title.Progress")],
+ ["span"],
+ ["span", {}, _("title.Speed")],
+ ["span", {}, _("title.Time remaining")],
+ ["span", {}, _("title.Source")]],
+ ["tr", { highlight: "Download" },
+ ["span", {},
+ ["div", { style: "min-height: 1ex; /* FIXME */" }]]],
+ ["tr", { highlight: "Download", key: "totals", active: "true" },
+ ["td", {},
+ ["span", { highlight: "Title" },
+ _("title.Totals") + ":"],
+ " ",
+ ["span", { key: "total" }]],
+ ["td"],
+ ["td", { highlight: "DownloadButtons" },
+ ["a", { highlight: "Button", href: "javascript:0", key: "clear" }, _("download.action.Clear")]],
+ ["td", { highlight: "DownloadProgress", key: "progress" },
+ ["span", { highlight: "DownloadProgressHave", key: "progressHave" }],
+ "/",
+ ["span", { highlight: "DownloadProgressTotal", key: "progressTotal" }]],
+ ["td", { highlight: "DownloadPercent", key: "percent" }],
+ ["td", { highlight: "DownloadSpeed", key: "speed" }],
+ ["td", { highlight: "DownloadTime", key: "time" }],
+ ["td"]]],
+ this.document, this.nodes);
+
+ this.index = Array.indexOf(this.nodes.list.childNodes,
+ this.nodes.head);
+
+ let start = Date.now();
for (let row in iter(services.downloadManager.DBConnection
- .createStatement("SELECT id FROM moz_downloads")))
+ .createStatement("SELECT id FROM moz_downloads"))) {
+ if (Date.now() - start > MAX_LOAD_TIME) {
+ util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000));
+ break;
+ }
this.addDownload(row.id);
+ }
this.update();
util.addObserver(this);
.indexOf(download);
this.nodes.list.insertBefore(download.nodes.row,
- this.nodes.list.childNodes[index + 1]);
+ this.nodes.list.childNodes[index + this.index + 1]);
}
},
removeDownload: function removeDownload(id) {
this.cleanup();
},
- allowedCommands: Class.memoize(function () let (self = this) ({
+ allowedCommands: Class.Memoize(function () let (self = this) ({
get clear() values(self.downloads).some(function (dl) dl.allowedCommands.remove)
})),
updateProgress: function updateProgress() {
let downloads = values(this.downloads).toArray();
+ let active = downloads.filter(function (d) d.alive);
let self = Object.create(this);
for (let prop in values(["amountTransferred", "size", "speed", "timeRemaining"]))
- this[prop] = downloads.reduce(function (acc, dl) dl[prop] + acc, 0);
+ this[prop] = active.reduce(function (acc, dl) dl[prop] + acc, 0);
Download.prototype.updateProgress.call(self);
- let active = downloads.filter(function (dl) dl.alive).length;
- if (active)
- this.nodes.total.textContent = _("download.nActive", active);
+ this.nActive = active.length;
+ if (active.length)
+ this.nodes.total.textContent = _("download.nActive", active.length);
else for (let key in values(["total", "percent", "speed", "time"]))
this.nodes[key].textContent = "";
}
});
-var Downloads = Module("downloads", {
+var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
+ init: function () {
+ services.downloadManager.addListener(this);
+ },
+
+ cleanup: function destroy() {
+ services.downloadManager.removeListener(this);
+ },
+
+ onDownloadStateChange: function (state, download) {
+ if (download.state == services.downloadManager.DOWNLOAD_FINISHED) {
+ let url = download.source.spec;
+ let title = download.displayName;
+ let file = download.targetFile.path;
+ let size = download.size;
+
+
+ overlay.modules.forEach(function (modules) {
+ modules.dactyl.echomsg({ domains: [util.getHost(url)], message: _("io.downloadFinished", title, file) },
+ 1, modules.commandline.ACTIVE_WINDOW);
+ modules.autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size });
+ });
+ }
+ }
}, {
}, {
commands: function initCommands(dactyl, modules, window) {