mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 07:48:00 +01:00
Add an experimental prototype download manager replacement. Fix some bugs.
--HG-- extra : rebase_source : aea703414d4bd601bfdea779c5878a30d5b3d782
This commit is contained in:
@@ -133,7 +133,7 @@ var Browser = Module("browser", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
commands: function () {
|
commands: function () {
|
||||||
commands.add(["downl[oads]", "dl"],
|
commands.add(["old-downl[oads]", "old-dl"],
|
||||||
"Show progress of current downloads",
|
"Show progress of current downloads",
|
||||||
function () {
|
function () {
|
||||||
dactyl.open("chrome://mozapps/content/downloads/downloads.xul",
|
dactyl.open("chrome://mozapps/content/downloads/downloads.xul",
|
||||||
|
|||||||
@@ -786,6 +786,10 @@ var Buffer = Module("buffer", {
|
|||||||
var persist = services.Persist();
|
var persist = services.Persist();
|
||||||
persist.persistFlags = persist.PERSIST_FLAGS_FROM_CACHE
|
persist.persistFlags = persist.PERSIST_FLAGS_FROM_CACHE
|
||||||
| persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
|
| persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
|
||||||
|
|
||||||
|
persist.progressListener = new window.DownloadListener(window,
|
||||||
|
services.Transfer(uri, services.io.newFileURI(file), "",
|
||||||
|
null, null, null, persist));
|
||||||
persist.saveURI(uri, null, null, null, null, file);
|
persist.saveURI(uri, null, null, null, null, file);
|
||||||
}, {
|
}, {
|
||||||
autocomplete: true,
|
autocomplete: true,
|
||||||
|
|||||||
@@ -265,10 +265,12 @@ var CommandWidgets = Class("CommandWidgets", {
|
|||||||
while (elem.contentDocument.documentURI != elem.getAttribute("src") ||
|
while (elem.contentDocument.documentURI != elem.getAttribute("src") ||
|
||||||
["viewable", "complete"].indexOf(elem.contentDocument.readyState) < 0)
|
["viewable", "complete"].indexOf(elem.contentDocument.readyState) < 0)
|
||||||
util.threadYield();
|
util.threadYield();
|
||||||
return elem;
|
res = res || (processor || util.identity).call(self, elem);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Class.replaceProperty(this, name, (processor || util.identity).call(this, this[name]))
|
let res, self = this;
|
||||||
|
return Class.replaceProperty(this, name, this[name])
|
||||||
},
|
},
|
||||||
|
|
||||||
get completionList() this._whenReady("completionList", "dactyl-completions"),
|
get completionList() this._whenReady("completionList", "dactyl-completions"),
|
||||||
@@ -748,8 +750,15 @@ var CommandLine = Module("commandline", {
|
|||||||
XML.ignoreWhitespace = false;
|
XML.ignoreWhitespace = false;
|
||||||
XML.prettyPrinting = false;
|
XML.prettyPrinting = false;
|
||||||
let style = typeof str === "string" ? "pre" : "nowrap";
|
let style = typeof str === "string" ? "pre" : "nowrap";
|
||||||
this._lastMowOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{str}</div>;
|
if (callable(str)) {
|
||||||
let output = util.xmlToDom(this._lastMowOutput, doc);
|
this._lastMowOutput = null;
|
||||||
|
var output = util.xmlToDom(<div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}/>, doc);
|
||||||
|
output.appendChild(str(doc));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._lastMowOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{str}</div>;
|
||||||
|
var output = util.xmlToDom(this._lastMowOutput, doc);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: need to make sure an open MOW is closed when commands
|
// FIXME: need to make sure an open MOW is closed when commands
|
||||||
// that don't generate output are executed
|
// that don't generate output are executed
|
||||||
@@ -824,7 +833,7 @@ var CommandLine = Module("commandline", {
|
|||||||
let single = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE);
|
let single = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE);
|
||||||
let action = this._echoLine;
|
let action = this._echoLine;
|
||||||
|
|
||||||
if ((flags & this.FORCE_MULTILINE) || (/\n/.test(str) || typeof str == "xml") && !(flags & this.FORCE_SINGLELINE))
|
if ((flags & this.FORCE_MULTILINE) || (/\n/.test(str) || !isString(str)) && !(flags & this.FORCE_SINGLELINE))
|
||||||
action = this._echoMultiline;
|
action = this._echoMultiline;
|
||||||
|
|
||||||
if (single)
|
if (single)
|
||||||
@@ -1073,63 +1082,69 @@ var CommandLine = Module("commandline", {
|
|||||||
// FIXME: if 'more' is set and the MOW is not scrollable we should still
|
// FIXME: if 'more' is set and the MOW is not scrollable we should still
|
||||||
// allow a down motion after an up rather than closing
|
// allow a down motion after an up rather than closing
|
||||||
onMultilineOutputEvent: function onMultilineOutputEvent(event) {
|
onMultilineOutputEvent: function onMultilineOutputEvent(event) {
|
||||||
const KILL = false, PASS = true;
|
try {
|
||||||
|
const KILL = false, PASS = true;
|
||||||
|
|
||||||
let win = this.widgets.multilineOutput.contentWindow;
|
let win = this.widgets.multilineOutput.contentWindow;
|
||||||
let elem = win.document.documentElement;
|
let elem = win.document.documentElement;
|
||||||
|
|
||||||
let key = events.toString(event);
|
let key = events.toString(event);
|
||||||
|
|
||||||
function openLink(where) {
|
const openLink = function openLink(where) {
|
||||||
event.preventDefault();
|
|
||||||
dactyl.open(event.target.href, where);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Wouldn't multiple handlers be cleaner? --djk
|
|
||||||
if (event.type == "click" && event.target instanceof HTMLAnchorElement) {
|
|
||||||
|
|
||||||
let command = event.originalTarget.getAttributeNS(NS.uri, "command");
|
|
||||||
if (command && dactyl.commands[command]) {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return dactyl.withSavedValues(["forceNewTab"], function () {
|
dactyl.open(event.target.href, where);
|
||||||
dactyl.forceNewTab = event.ctrlKey || event.shiftKey || event.button == 1;
|
|
||||||
return dactyl.commands[command](event);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (key) {
|
// TODO: Wouldn't multiple handlers be cleaner? --djk
|
||||||
case "<LeftMouse>":
|
if (event.type == "click" && (event.target instanceof HTMLAnchorElement ||
|
||||||
event.preventDefault();
|
event.originalTarget.hasAttributeNS(NS, "command"))) {
|
||||||
openLink(dactyl.CURRENT_TAB);
|
|
||||||
return KILL;
|
let command = event.originalTarget.getAttributeNS(NS, "command");
|
||||||
case "<MiddleMouse>":
|
if (command && dactyl.commands[command]) {
|
||||||
case "<C-LeftMouse>":
|
event.preventDefault();
|
||||||
case "<C-M-LeftMouse>":
|
return dactyl.withSavedValues(["forceNewTab"], function () {
|
||||||
openLink({ where: dactyl.NEW_TAB, background: true });
|
dactyl.forceNewTab = event.ctrlKey || event.shiftKey || event.button == 1;
|
||||||
return KILL;
|
return dactyl.commands[command](event);
|
||||||
case "<S-MiddleMouse>":
|
});
|
||||||
case "<C-S-LeftMouse>":
|
}
|
||||||
case "<C-M-S-LeftMouse>":
|
|
||||||
openLink({ where: dactyl.NEW_TAB, background: false });
|
switch (key) {
|
||||||
return KILL;
|
case "<LeftMouse>":
|
||||||
case "<S-LeftMouse>":
|
event.preventDefault();
|
||||||
openLink(dactyl.NEW_WINDOW);
|
openLink(dactyl.CURRENT_TAB);
|
||||||
return KILL;
|
return KILL;
|
||||||
|
case "<MiddleMouse>":
|
||||||
|
case "<C-LeftMouse>":
|
||||||
|
case "<C-M-LeftMouse>":
|
||||||
|
openLink({ where: dactyl.NEW_TAB, background: true });
|
||||||
|
return KILL;
|
||||||
|
case "<S-MiddleMouse>":
|
||||||
|
case "<C-S-LeftMouse>":
|
||||||
|
case "<C-M-S-LeftMouse>":
|
||||||
|
openLink({ where: dactyl.NEW_TAB, background: false });
|
||||||
|
return KILL;
|
||||||
|
case "<S-LeftMouse>":
|
||||||
|
openLink(dactyl.NEW_WINDOW);
|
||||||
|
return KILL;
|
||||||
|
}
|
||||||
|
return PASS;
|
||||||
}
|
}
|
||||||
return PASS;
|
|
||||||
|
if (event instanceof MouseEvent)
|
||||||
|
return KILL;
|
||||||
|
|
||||||
|
const atEnd = function atEnd(dir) !Buffer.isScrollable(elem, dir || 1);
|
||||||
|
|
||||||
|
if (!options["more"] || atEnd(1)) {
|
||||||
|
modes.pop();
|
||||||
|
events.feedkeys(key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
commandline.updateMorePrompt(false, true);
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
if (event instanceof MouseEvent)
|
util.reportError(e);
|
||||||
return KILL;
|
|
||||||
|
|
||||||
function atEnd(dir) !Buffer.isScrollable(elem, dir || 1);
|
|
||||||
|
|
||||||
if (!options["more"] || atEnd(1)) {
|
|
||||||
modes.pop();
|
|
||||||
events.feedkeys(key);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
commandline.updateMorePrompt(false, true);
|
|
||||||
return PASS;
|
return PASS;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -53,16 +53,18 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
},
|
},
|
||||||
|
|
||||||
observers: {
|
observers: {
|
||||||
"dactyl-cleanup": function () {
|
"dactyl-cleanup": function dactyl_cleanup() {
|
||||||
let modules = dactyl.modules;
|
let modules = dactyl.modules;
|
||||||
|
|
||||||
for (let name in values(Object.getOwnPropertyNames(modules).reverse())) {
|
for (let name in values(Object.getOwnPropertyNames(modules).reverse())) {
|
||||||
let mod = Object.getOwnPropertyDescriptor(modules, name).value;
|
let mod = Object.getOwnPropertyDescriptor(modules, name).value;
|
||||||
if (mod instanceof Class) {
|
if (mod instanceof Class) {
|
||||||
if ("cleanup" in mod)
|
if ("cleanup" in mod)
|
||||||
mod.cleanup();
|
this.trapErrors(mod.cleanup, mod);
|
||||||
if ("destroy" in mod)
|
if ("destroy" in mod)
|
||||||
mod.destroy();
|
this.trapErrors(mod.destroy, mod);
|
||||||
|
if ("INIT" in mod && "cleanup" in mod.INIT)
|
||||||
|
this.trapErrors(mod.cleanup, mod, dactyl, modules, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +362,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
},
|
},
|
||||||
|
|
||||||
userEval: function (str, context, fileName, lineNumber) {
|
userEval: function (str, context, fileName, lineNumber) {
|
||||||
|
if (jsmodules.__proto__ != window)
|
||||||
|
str = "with (window) { with (modules) { this.eval(" + str.quote() + ") } }";
|
||||||
|
|
||||||
if (fileName == null)
|
if (fileName == null)
|
||||||
if (io.sourcing && io.sourcing.file[0] !== "[")
|
if (io.sourcing && io.sourcing.file[0] !== "[")
|
||||||
({ file: fileName, line: lineNumber }) = io.sourcing;
|
({ file: fileName, line: lineNumber }) = io.sourcing;
|
||||||
@@ -389,9 +394,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
context = _userContext;
|
context = _userContext;
|
||||||
if (window.isPrototypeOf(modules))
|
return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
|
||||||
return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
|
|
||||||
return Cu.evalInSandbox("with (window) { with (modules) { this.eval(" + str.quote() + ") } }", context, "1.8", fileName, lineNumber);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2178,7 +2181,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
let init = services.environment.get(config.idName + "_INIT");
|
let init = services.environment.get(config.idName + "_INIT");
|
||||||
let rcFile = io.getRCFile("~");
|
let rcFile = io.getRCFile("~");
|
||||||
|
|
||||||
if (dactyl.userEval('typeof document') === "undefined")
|
if (dactyl.userEval("typeof document", null, "test.js") === "undefined")
|
||||||
jsmodules.__proto__ = XPCSafeJSObjectWrapper(window);
|
jsmodules.__proto__ = XPCSafeJSObjectWrapper(window);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -175,6 +175,9 @@ var Modes = Module("modes", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
cleanup: function () {
|
||||||
|
modes.reset();
|
||||||
|
},
|
||||||
|
|
||||||
_getModeMessage: function () {
|
_getModeMessage: function () {
|
||||||
// when recording a macro
|
// when recording a macro
|
||||||
|
|||||||
@@ -70,9 +70,8 @@
|
|||||||
<spec>:downl<oa>oads</oa></spec>
|
<spec>:downl<oa>oads</oa></spec>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
Show progress of current downloads. Open the standard &dactyl.host;
|
Show progress of current downloads. Here, downloads can
|
||||||
download dialog in a new tab. Here, downloads can be paused,
|
be paused, resumed, and canceled.
|
||||||
resumed, and canceled.
|
|
||||||
</p>
|
</p>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -737,9 +737,9 @@ Class.memoize = function memoize(getter)
|
|||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
init: function (key) {
|
init: function (key) {
|
||||||
this.get = function replace() (
|
this.get = function replace() let (obj = this.instance || this) (
|
||||||
Class.replaceProperty(this, key, null),
|
Class.replaceProperty(obj, key, null),
|
||||||
Class.replaceProperty(this, key, getter.call(this, key)))
|
Class.replaceProperty(obj, key, getter.call(this, key)))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1196,6 +1196,9 @@ update(iter, {
|
|||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sort: function sort(iter, fn, self)
|
||||||
|
array(this.toArray(iter).sort(fn, self)),
|
||||||
|
|
||||||
uniq: function uniq(iter) {
|
uniq: function uniq(iter) {
|
||||||
let seen = {};
|
let seen = {};
|
||||||
for (let item in iter)
|
for (let item in iter)
|
||||||
|
|||||||
@@ -426,9 +426,16 @@ var ConfigBase = Class("ConfigBase", {
|
|||||||
Keyword color: red;
|
Keyword color: red;
|
||||||
Tag color: blue;
|
Tag color: blue;
|
||||||
|
|
||||||
Usage position: relative; padding-right: 2em;
|
Link position: relative; padding-right: 2em;
|
||||||
Usage>LineInfo position: absolute; left: 100%; padding: 1ex; margin: -1ex -1em; background: rgba(255, 255, 255, .8); border-radius: 1ex;
|
Link:not(:hover)>LinkInfo opacity: 0; left: 0; width: 1px; height: 1px; overflow: hidden;
|
||||||
Usage:not(:hover)>LineInfo opacity: 0; left: 0; width: 1px; height: 1px; overflow: hidden;
|
LinkInfo {
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
padding: 1ex;
|
||||||
|
margin: -1ex -1em;
|
||||||
|
background: rgba(255, 255, 255, .8);
|
||||||
|
border-radius: 1ex;
|
||||||
|
}
|
||||||
|
|
||||||
StatusLine;;;FontFixed {
|
StatusLine;;;FontFixed {
|
||||||
-moz-appearance: none !important;
|
-moz-appearance: none !important;
|
||||||
@@ -490,6 +497,30 @@ var ConfigBase = Class("ConfigBase", {
|
|||||||
HintActive;;* background-color: #88FF00 !important; color: black !important;
|
HintActive;;* background-color: #88FF00 !important; color: black !important;
|
||||||
HintImage;;* opacity: .5 !important;
|
HintImage;;* opacity: .5 !important;
|
||||||
|
|
||||||
|
Button display: inline-block; font-weight: bold; cursor: pointer;
|
||||||
|
Button:hover text-decoration: underline;
|
||||||
|
Button[collapsed] visibility: collapse; width: 0;
|
||||||
|
Button::before content: "["; color: gray; text-decoration: none !important;
|
||||||
|
Button::after content: "]"; color: gray; text-decoration: none !important;
|
||||||
|
Button:not([collapsed]) ~ Button:not([collapsed])::before content: "/[";
|
||||||
|
|
||||||
|
Downloads display: table; margin: 0; padding: 0;
|
||||||
|
DownloadHead;;;CompTitle display: table-row;
|
||||||
|
DownloadHead>*;;;DownloadCell display: table-cell;
|
||||||
|
|
||||||
|
Download display: table-row;
|
||||||
|
|
||||||
|
DownloadCell display: table-cell; padding: 0 1ex;
|
||||||
|
DownloadButtons;;;DownloadCell
|
||||||
|
DownloadPercent;;;DownloadCell
|
||||||
|
DownloadProgress;;;DownloadCell
|
||||||
|
DownloadProgressHave
|
||||||
|
DownloadProgressTotal
|
||||||
|
DownloadSource;;;DownloadCell,URL
|
||||||
|
DownloadState;;;DownloadCell
|
||||||
|
DownloadTime;;;DownloadCell
|
||||||
|
DownloadTitle;;;DownloadCell,URL
|
||||||
|
|
||||||
// </css>
|
// </css>
|
||||||
]]></>, /
/g, "\n")),
|
]]></>, /
/g, "\n")),
|
||||||
|
|
||||||
|
|||||||
268
common/modules/downloads.jsm
Normal file
268
common/modules/downloads.jsm
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
// Copyright (c) 2011 by 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", "services", "template", "util"]
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
|
||||||
|
|
||||||
|
let prefix = "DOWNLOAD_";
|
||||||
|
var states = iter([v, k.slice(prefix.length).toLowerCase()]
|
||||||
|
for ([k, v] in Iterator(Ci.nsIDownloadManager))
|
||||||
|
if (k.indexOf(prefix) == 0))
|
||||||
|
.toObject();
|
||||||
|
|
||||||
|
var Download = Class("Download", {
|
||||||
|
init: function init(id, document) {
|
||||||
|
let self = XPCSafeJSObjectWrapper(services.downloadManager.getDownload(id));
|
||||||
|
self.__proto__ = this;
|
||||||
|
this.instance = this;
|
||||||
|
|
||||||
|
this.nodes = {};
|
||||||
|
util.xmlToDom(
|
||||||
|
<li highlight="Download" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
|
||||||
|
<span highlight="DownloadTitle">
|
||||||
|
<span highlight="Link">
|
||||||
|
<a key="title" href={self.target.spec}>{self.displayName}</a>
|
||||||
|
<span highlight="LinkInfo">{self.targetFile.path}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span highlight="DownloadState" key="state"/>
|
||||||
|
<span highlight="DownloadButtons">
|
||||||
|
<span highlight="Button" key="pause">Pause</span>
|
||||||
|
<span highlight="Button" key="remove">Remove</span>
|
||||||
|
<span highlight="Button" key="resume">Resume</span>
|
||||||
|
<span highlight="Button" key="retry">Retry</span>
|
||||||
|
<span highlight="Button" key="cancel">Cancel</span>
|
||||||
|
<span highlight="Button" key="delete">Delete</span>
|
||||||
|
</span>
|
||||||
|
<span highlight="DownloadProgress" key="progress">
|
||||||
|
<span highlight="DownloadProgressHave" key="progressHave"
|
||||||
|
/>/<span highlight="DownloadProgressTotal" key="progressTotal"/>
|
||||||
|
</span>
|
||||||
|
<span highlight="DownloadPercent" key="percent"/>
|
||||||
|
<span highlight="DownloadTime" key="time"/>
|
||||||
|
<a highlight="DownloadSource" key="source" href={self.source.spec}>{self.source.spec}</a>
|
||||||
|
</li>,
|
||||||
|
document, this.nodes);
|
||||||
|
|
||||||
|
for (let [key, node] in Iterator(this.nodes)) {
|
||||||
|
node.dactylDownload = self;
|
||||||
|
if (node.getAttributeNS(NS, "highlight") == "Button") {
|
||||||
|
node.setAttributeNS(NS, "command", "download.command");
|
||||||
|
update(node, {
|
||||||
|
set collapsed(collapsed) {
|
||||||
|
if (collapsed)
|
||||||
|
this.setAttribute("collapsed", "true");
|
||||||
|
else
|
||||||
|
this.removeAttribute("collapsed");
|
||||||
|
},
|
||||||
|
get collapsed() !!this.getAttribute("collapsed")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateStatus();
|
||||||
|
return self;
|
||||||
|
},
|
||||||
|
|
||||||
|
get status() states[this.state],
|
||||||
|
|
||||||
|
inState: function inState(states) states.indexOf(this.status) >= 0,
|
||||||
|
|
||||||
|
get alive() this.inState(["downloading", "notstarted", "paused", "queued", "scanning"]),
|
||||||
|
|
||||||
|
allowed: Class.memoize(function () let (self = this) ({
|
||||||
|
get cancel() self.cancelable && self.inState(["downloading", "paused", "starting"]),
|
||||||
|
get delete() !this.cancel && self.targetFile.exists(),
|
||||||
|
get pause() self.inState(["downloading"]),
|
||||||
|
get remove() self.inState(["blocked_parental", "blocked_policy",
|
||||||
|
"canceled", "dirty", "failed", "finished"]),
|
||||||
|
get resume() self.resumable && self.inState(["paused"]),
|
||||||
|
get retry() self.inState(["canceled", "failed"])
|
||||||
|
})),
|
||||||
|
|
||||||
|
command: function command(name) {
|
||||||
|
util.assert(set.has(this.allowed, name), "Unknown command");
|
||||||
|
util.assert(this.allowed[name], "Command not allowed");
|
||||||
|
|
||||||
|
if (set.has(this.commands, name))
|
||||||
|
this.commands[name].call(this);
|
||||||
|
else
|
||||||
|
services.downloadManager[name + "Download"](this.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
commands: {
|
||||||
|
delete: function delete() {
|
||||||
|
this.targetFile.remove(false);
|
||||||
|
this.updateStatus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
compare: function compare(other) String.localeCompare(this.displayName, other.displayName),
|
||||||
|
|
||||||
|
timeRemaining: Infinity,
|
||||||
|
|
||||||
|
updateProgress: function updateProgress() {
|
||||||
|
let self = this.__proto__;
|
||||||
|
|
||||||
|
if (this.amountTransferred === this.size)
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : "Unknown";
|
||||||
|
let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$");
|
||||||
|
this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, "");
|
||||||
|
|
||||||
|
this.nodes.percent.textContent = this.size ? Math.round(this.amountTransferred * 100 / this.size) + "%" : "";
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStatus: function updateStatus() {
|
||||||
|
|
||||||
|
this.nodes.state.textContent = util.capitalize(this.status);
|
||||||
|
for (let [command, enabled] in Iterator(this.allowed))
|
||||||
|
this.nodes[command].collapsed = !enabled;
|
||||||
|
this.updateProgress();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var DownloadList = Class("DownloadList",
|
||||||
|
XPCOM([Ci.nsIDownloadProgressListener,
|
||||||
|
Ci.nsIObserver,
|
||||||
|
Ci.nsISupportsWeakReference]), {
|
||||||
|
init: function init(document, modules) {
|
||||||
|
this.modules = modules;
|
||||||
|
this.document = document;
|
||||||
|
this.nodes = {};
|
||||||
|
util.xmlToDom(<ul highlight="Downloads" key="list" xmlns={XHTML}>
|
||||||
|
<li highlight="DownloadHead">
|
||||||
|
<span>Title</span>
|
||||||
|
<span>Status</span>
|
||||||
|
<span></span>
|
||||||
|
<span>Progress</span>
|
||||||
|
<span></span>
|
||||||
|
<span>Time remaining</span>
|
||||||
|
<span>Source</span>
|
||||||
|
</li>
|
||||||
|
</ul>, this.document, this.nodes);
|
||||||
|
|
||||||
|
this.downloads = {};
|
||||||
|
for (let row in iter(services.downloadManager.DBConnection
|
||||||
|
.createStatement("SELECT id FROM moz_downloads")))
|
||||||
|
this.addDownload(row.id);
|
||||||
|
|
||||||
|
util.addObserver(this);
|
||||||
|
services.downloadManager.addListener(this);
|
||||||
|
},
|
||||||
|
cleanup: function cleanup() {
|
||||||
|
this.observe.unregister();
|
||||||
|
services.downloadManager.removeListener(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
addDownload: function addDownload(id) {
|
||||||
|
if (!(id in this.downloads)) {
|
||||||
|
this.downloads[id] = Download(id, this.document);
|
||||||
|
|
||||||
|
let index = values(this.downloads).sort(function (a, b) a.compare(b))
|
||||||
|
.indexOf(this.downloads[id]);
|
||||||
|
|
||||||
|
this.nodes.list.insertBefore(this.downloads[id].nodes.row,
|
||||||
|
this.nodes.list.childNodes[index + 1]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeDownload: function removeDownload(id) {
|
||||||
|
if (id in this.downloads) {
|
||||||
|
this.nodes.list.removeChild(this.downloads[id].nodes.row);
|
||||||
|
delete this.downloads[id];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
leave: function leave(stack) {
|
||||||
|
if (stack.pop)
|
||||||
|
this.cleanup();
|
||||||
|
},
|
||||||
|
|
||||||
|
observers: {
|
||||||
|
"download-manager-remove-download": function (id) {
|
||||||
|
if (id == null)
|
||||||
|
id = [k for ([k, dl] in iter(this.downloads)) if (dl.allowed.remove)];
|
||||||
|
else
|
||||||
|
id = [id.QueryInterface(Ci.nsISupportsPRUint32).data];
|
||||||
|
|
||||||
|
Array.concat(id).map(this.closure.removeDownload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDownloadStateChange: function (state, download) {
|
||||||
|
try {
|
||||||
|
if (download.id in this.downloads)
|
||||||
|
this.downloads[download.id].updateStatus();
|
||||||
|
else {
|
||||||
|
this.addDownload(download.id);
|
||||||
|
|
||||||
|
this.modules.commandline.updateOutputHeight(true);
|
||||||
|
this.nodes.list.scrollIntoView(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
util.reportError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onProgressChange: function (webProgress, request,
|
||||||
|
curProgress, maxProgress,
|
||||||
|
curTotalProgress, maxTotalProgress,
|
||||||
|
download) {
|
||||||
|
try {
|
||||||
|
if (download.id in this.downloads)
|
||||||
|
this.downloads[download.id].updateProgress();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
util.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Downloads = Module("downloads", {
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
commands: function (dactyl, modules, window) {
|
||||||
|
const { commands } = modules;
|
||||||
|
|
||||||
|
commands.add(["downl[oads]", "dl"],
|
||||||
|
"Display the downloads list",
|
||||||
|
function (args) {
|
||||||
|
modules.commandline.echo(function (doc) {
|
||||||
|
let downloads = DownloadList(doc, modules);
|
||||||
|
// Temporary and dangerous hack:
|
||||||
|
modules.modes.getStack(0).params = downloads;
|
||||||
|
return downloads.nodes.list;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dactyl: function (dactyl, modules, window) {
|
||||||
|
dactyl.commands["download.command"] = function (event) {
|
||||||
|
let elem = event.originalTarget;
|
||||||
|
elem.dactylDownload.command(elem.getAttribute("key"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
endModule();
|
||||||
|
|
||||||
|
// catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
|
||||||
@@ -92,7 +92,8 @@ var Highlights = Module("Highlight", {
|
|||||||
|
|
||||||
keys: function keys() Object.keys(this.highlight).sort(),
|
keys: function keys() Object.keys(this.highlight).sort(),
|
||||||
|
|
||||||
__iterator__: function () values(this.highlight),
|
__iterator__: function () values(this.highlight).sort(function (a, b) String.localeCompare(a.class, b.class))
|
||||||
|
.iterValues(),
|
||||||
|
|
||||||
_create: function (agent, args) {
|
_create: function (agent, args) {
|
||||||
let obj = Highlight.apply(Highlight, args);
|
let obj = Highlight.apply(Highlight, args);
|
||||||
@@ -318,7 +319,7 @@ var Highlights = Module("Highlight", {
|
|||||||
if (!modify)
|
if (!modify)
|
||||||
modules.commandline.commandOutput(
|
modules.commandline.commandOutput(
|
||||||
template.tabular(["Key", "Sample", "Link", "CSS"],
|
template.tabular(["Key", "Sample", "Link", "CSS"],
|
||||||
["padding: 0 1em 0 0; vertical-align: top",
|
["padding: 0 1em 0 0; vertical-align: top; max-width: 16em; overflow: hidden;",
|
||||||
"text-align: center"],
|
"text-align: center"],
|
||||||
([h.class,
|
([h.class,
|
||||||
<span style={"text-align: center; line-height: 1em;" + h.value + style}>XXX</span>,
|
<span style={"text-align: center; line-height: 1em;" + h.value + style}>XXX</span>,
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ var Overlay = Module("Overlay", {
|
|||||||
["base",
|
["base",
|
||||||
"completion",
|
"completion",
|
||||||
"config",
|
"config",
|
||||||
|
"downloads",
|
||||||
"javascript",
|
"javascript",
|
||||||
"overlay",
|
"overlay",
|
||||||
"prefs",
|
"prefs",
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ var Services = Module("Services", {
|
|||||||
[Ci.nsIChannel, Ci.nsIInputStreamChannel, Ci.nsIRequest], "setURI");
|
[Ci.nsIChannel, Ci.nsIInputStreamChannel, Ci.nsIRequest], "setURI");
|
||||||
this.addClass("String", "@mozilla.org/supports-string;1", Ci.nsISupportsString, "data");
|
this.addClass("String", "@mozilla.org/supports-string;1", Ci.nsISupportsString, "data");
|
||||||
this.addClass("StringStream", "@mozilla.org/io/string-input-stream;1", Ci.nsIStringInputStream, "data");
|
this.addClass("StringStream", "@mozilla.org/io/string-input-stream;1", Ci.nsIStringInputStream, "data");
|
||||||
|
this.addClass("Transfer", "@mozilla.org/transfer;1", Ci.nsITransfer, "init");
|
||||||
this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback");
|
this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback");
|
||||||
this.addClass("StreamCopier", "@mozilla.org/network/async-stream-copier;1",Ci.nsIAsyncStreamCopier, "init");
|
this.addClass("StreamCopier", "@mozilla.org/network/async-stream-copier;1",Ci.nsIAsyncStreamCopier, "init");
|
||||||
this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest);
|
this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest);
|
||||||
|
|||||||
@@ -259,9 +259,15 @@ var Storage = Module("Storage", {
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
}, {
|
}, {
|
||||||
init: function (dactyl, modules) {
|
init: function init(dactyl, modules) {
|
||||||
|
init.superapply(this, arguments);
|
||||||
storage.infoPath = File(modules.IO.runtimePath.replace(/,.*/, ""))
|
storage.infoPath = File(modules.IO.runtimePath.replace(/,.*/, ""))
|
||||||
.child("info").child(dactyl.profileName);
|
.child("info").child(dactyl.profileName);
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanup: function (dactyl, modules, window) {
|
||||||
|
delete window.dactylStorageRefs;
|
||||||
|
this.removeDeadObservers();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -331,11 +331,11 @@ var Template = Module("Template", {
|
|||||||
this.map(iter, function (item)
|
this.map(iter, function (item)
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding-right: 2em;">
|
<td style="padding-right: 2em;">
|
||||||
<span highlight="Usage">{
|
<span highlight="Usage Link">{
|
||||||
let (name = item.name || item.names[0], frame = item.definedAt)
|
let (name = item.name || item.names[0], frame = item.definedAt)
|
||||||
!frame ? name :
|
!frame ? name :
|
||||||
template.helpLink(help(item), name, "Title") +
|
template.helpLink(help(item), name, "Title") +
|
||||||
<span highlight="LineInfo" xmlns:dactyl={NS}>Defined at {sourceLink(frame)}</span>
|
<span highlight="LinkInfo" xmlns:dactyl={NS}>Defined at {sourceLink(frame)}</span>
|
||||||
}</span>
|
}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{desc(item)}</td>
|
<td>{desc(item)}</td>
|
||||||
|
|||||||
@@ -135,6 +135,13 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|||||||
throw FailedAssertion(message, 1);
|
throw FailedAssertion(message, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capitalizes the first character of the given string.
|
||||||
|
* @param {string} str The string to capitalize
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
capitalize: function capitalize(str) str && str[0].toUpperCase() + str.slice(1),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a RegExp object that matches characters specified in the range
|
* Returns a RegExp object that matches characters specified in the range
|
||||||
* expression *list*, or signals an appropriate error if *list* is invalid.
|
* expression *list*, or signals an appropriate error if *list* is invalid.
|
||||||
@@ -499,9 +506,10 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|||||||
|
|
||||||
stackLines: function (stack) {
|
stackLines: function (stack) {
|
||||||
let lines = [];
|
let lines = [];
|
||||||
let match, re = /([^]*?)(@[^@\n]*)(?:\n|$)/g;
|
let match, re = /([^]*?)@([^@\n]*)(?:\n|$)/g;
|
||||||
while (match = re.exec(stack))
|
while (match = re.exec(stack))
|
||||||
lines.push(match[1].replace(/\n/g, "\\n").substr(0, 80) + match[2]);
|
lines.push(match[1].replace(/\n/g, "\\n").substr(0, 80) + "@" +
|
||||||
|
match[2].replace(/.* -> /, ""));
|
||||||
return lines;
|
return lines;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -653,6 +661,28 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|||||||
return strNum[0] + " " + unitVal[unitIndex];
|
return strNum[0] + " " + unitVal[unitIndex];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts *seconds* into a human readable time string.
|
||||||
|
*
|
||||||
|
* @param {number} seconds
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
formatSeconds: function formatSeconds(seconds) {
|
||||||
|
function div(num, denom) [Math.round(num / denom), Math.round(num % denom)];
|
||||||
|
let days, hours, minutes;
|
||||||
|
|
||||||
|
[minutes, seconds] = div(seconds, 60);
|
||||||
|
[hours, minutes] = div(minutes, 60);
|
||||||
|
[days, hours] = div(hours, 24);
|
||||||
|
if (days)
|
||||||
|
return days + " days " + hours + " hours"
|
||||||
|
if (hours)
|
||||||
|
return hours + "h " + minutes + "m";
|
||||||
|
if (minutes)
|
||||||
|
return minutes + ":" + seconds;
|
||||||
|
return seconds + "s";
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file which backs a given URL, if available.
|
* Returns the file which backs a given URL, if available.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -64,6 +64,8 @@
|
|||||||
consistent interactive help facility (improvements include
|
consistent interactive help facility (improvements include
|
||||||
listing keys for modes other than Normal, filtering the output
|
listing keys for modes other than Normal, filtering the output
|
||||||
and linking to source locations).
|
and linking to source locations).
|
||||||
|
- :downloads now opens a download list in the multi-line output
|
||||||
|
buffer.
|
||||||
- Added :cookies command.
|
- Added :cookies command.
|
||||||
* :extadd now supports remote URLs as well as local files on Firefox 4.
|
* :extadd now supports remote URLs as well as local files on Firefox 4.
|
||||||
* Added :if/:elseif/:else/:endif conditionals.
|
* Added :if/:elseif/:else/:endif conditionals.
|
||||||
|
|||||||
Reference in New Issue
Block a user