repo: 373f1649c80dea9be7b5bc9c57e8395f94f93ab1
-node: 91d4f03a135efe65e2e7de46c00ef09c78fdaae3
-branch: default
-tag: pentadactyl-1.0rc1
+node: 227d1399fe7c9d8de98fc6ab3222cf9e1d8ad38f
+branch: pentadactyl-1.0-branch
+tag: pentadactyl-1.0
GOOGLE = https://$(GOOGLE_PROJ).googlecode.com/files
VERSION ?= $(shell $(SED) -n 's/.*em:version(>|=")(.*)["<].*/\2/p' $(TOP)/install.rdf | sed 1q)
UUID := $(shell $(SED) -n 's/.*em:id(>|=")(.*)["<].*/\2/p' $(TOP)/install.rdf | sed 1q)
-MANGLE := $(shell date '+%s' | awk '{ printf "%x", $$1 }')
+MANGLE := chrome
MOZMILL = mozmill
HOSTAPP_PATH = $(shell which $(HOSTAPP))
TEST_DIR = $(BASE)/tests/functional
XPI = ../downloads/$(XPI_NAME).xpi
XPI_PATH = $(TOP)/$(XPI:%.xpi=%)
-RDF = ../downloads/update.rdf
-RDF_IN = $(RDF).in
-
BUILD_DIR = build.$(VERSION).$(OS)
.SILENT:
@echo " make install - installs this source tree directly to your $(HOSTAPP) profile"
@echo ' set $$PROFILE to select a profile by name and $$PROFILEPATHS'
@echo ' to change the directory where profiles are searched'
- @echo " make release - updates update.rdf (this is not for you)"
@echo " make dist - uploads to Google Code (this is not for you)"
@echo " make clean - clean up"
@echo " make distclean - clean up more"
jar: $(JAR)
-release: $(XPI) $(RDF)
-
# This is not for you!
dist: $(XPI)
@echo DIST $(XPI) $(GOOGLE)
installxpi: xpi
$(HOSTAPP) $(XPI)
-$(RDF): $(RDF_IN) Makefile
- @echo "Preparing release..."
- $(SED) -e "s,@VERSION@,$(VERSION),g" \
- -e "s,@DATE@,$(BUILD_DATE),g" \
- < $< > $@
- @echo "SUCCESS: $@"
-
clean:
@echo "General $(NAME) cleanup..."
rm -f $(JAR) $(XPI)
break;
case "resource":
- var hardSuffix = /^[^\/]*/.exec(fields[2])[0];
-
resources.push(fields[1], fields[1] + suffix);
resourceProto.setSubstitution(fields[1], getURI(fields[2]));
resourceProto.setSubstitution(fields[1] + suffix, getURI(fields[2]));
// Flush the cache if necessary, just to be paranoid
let pref = "extensions.dactyl.cacheFlushCheck";
- let val = addon.version + "-" + hardSuffix;
+ let val = addon.version;
if (!Services.prefs.prefHasUserValue(pref) || Services.prefs.getCharPref(pref) != val) {
var cacheFlush = true;
Services.obs.notifyObservers(null, "startupcache-invalidate", "");
util.dactyl.execute(remote);
}
}
- catch(e) {
- util.reportError(e)
- };
+ catch (e) {
+ util.reportError(e);
+ }
try {
this.optionValue = commandLine.handleFlagWithParam(config.name, false);
storage.addObserver("bookmark-cache", function (key, event, arg) {
if (["add", "change", "remove"].indexOf(event) >= 0)
- autocommands.trigger("Bookmark" + event[0].toUpperCase() + event.substr(1),
+ autocommands.trigger("Bookmark" + util.capitalize(event),
iter({
bookmark: {
toString: function () "bookmarkcache.bookmarks[" + arg.id + "]",
valueOf: function () arg
}
- }, arg));
+ }, arg).toObject());
bookmarks.timer.tell();
}, window);
},
catch (e) {}
if (charset)
- var encodedParam = escape(window.convertFromUnicode(charset, param));
+ var encodedParam = escape(window.convertFromUnicode(charset, param)).replace(/\+/g, encodeURIComponent);
else
- encodedParam = bookmarkcache.keywords[keyword].encodeURIComponent(param);
+ encodedParam = bookmarkcache.keywords[keyword.toLowerCase()].encodeURIComponent(param);
- url = url.replace(/%s/g, encodedParam).replace(/%S/g, param);
+ url = url.replace(/%s/g, function () encodedParam)
+ .replace(/%S/g, function () param);
if (/%s/i.test(data))
postData = window.getPostDataStream(data, param, encodedParam, "application/x-www-form-urlencoded");
}
let updated = bookmarks.add(opts);
let action = updated ? "updated" : "added";
- let extra = (opts.title == opts.url) ? "" : " (" + opts.title + ")";
+ let extra = (opts.title && opts.title != opts.url) ? " (" + opts.title + ")" : "";
dactyl.echomsg({ domains: [util.getHost(opts.url)], message: _("bookmark." + action, opts.url + extra) },
1, commandline.FORCE_SINGLELINE);
ctxt.compare = CompletionContext.Sort.unsorted;
ctxt.filterFunc = null;
+ if (ctxt.waitingForTab)
+ return;
+
let words = ctxt.filter.toLowerCase().split(/\s+/g);
ctxt.completions = ctxt.completions.filter(function (i) words.every(function (w) i.toLowerCase().indexOf(w) >= 0));
setOverLink: util.wrapCallback(function setOverLink(link, b) {
setOverLink.superapply(this, arguments);
dactyl.triggerObserver("browser.overLink", link);
- }),
+ })
}
}, {
}, {
function () {
window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
.redraw();
+ statusline.overLink = null;
statusline.updateStatus();
commandline.clear();
},
contextMenu: Class.Memoize(function () {
["copy", "copylink", "selectall"].forEach(function (tail) {
// some host apps use "hostPrefixContext-copy" ids
- let xpath = "//xul:menuitem[contains(@id, '" + "ontext-" + tail + "') and not(starts-with(@id, 'dactyl-'))]";
- document.getElementById("dactyl-context-" + tail).style.listStyleImage =
- DOM(DOM.XPath(xpath, document).snapshotItem(0)).style.listStyleImage;
+ let css = "menuitem[id$='ontext-" + tail + "']:not([id^=dactyl-])";
+ let style = DOM(css, document).style;
+ DOM("#dactyl-context-" + tail, document).css({
+ listStyleImage: style.listStyleImage,
+ MozImageRegion: style.MozImageRegion
+ });
});
return document.getElementById("dactyl-contextmenu");
}),
data = message.message;
}
- if ((flags & this.ACTIVE_WINDOW) &&
- window != services.windowWatcher.activeWindow &&
- services.windowWatcher.activeWindow.dactyl)
+ if ((flags & this.ACTIVE_WINDOW) && window != overlay.activeWindow)
return;
if ((flags & this.DISALLOW_MULTILINE) && !this.widgets.mowContainer.collapsed)
complete: function complete(show, tabPressed) {
this.session.ignoredCount = 0;
+ this.waiting = null;
this.context.reset();
this.context.tabPressed = tabPressed;
this.wildIndex = this.wildtypes.length - 1;
if (idx && idx[1] >= idx[0].items.length) {
- this.waiting = idx;
- statusline.progress = _("completion.waitingForResults");
+ if (!idx[0].incomplete)
+ this.waiting = null;
+ else {
+ this.waiting = idx;
+ statusline.progress = _("completion.waitingForResults");
+ }
return;
}
});
modes.addMode("INPUT_MULTILINE", {
+ description: "Active when the command line's multiline input buffer is open",
bases: [modes.INSERT]
});
},
// Check if we've passed any incomplete contexts
let i = groups.indexOf(group);
+ util.assert(i >= 0, undefined, false);
for (; i < groups.length; i++) {
let end = groups[i].offsets.start + groups[i].itemCount;
if (start >= end && groups[i].context.incomplete)
* Opens one or more URLs. Returns true when load was initiated, or
* false on error.
*
- * @param {string|object|Array} urls A representation of the URLs to open. May be
- * either a string, which will be passed to
- * {@link Dactyl#parseURLs}, an array in the same format as
- * would be returned by the same, or an object as returned by
- * {@link DOM#formData}.
+ * @param {string|Array} urls A representation of the URLs to open.
+ * A string will be passed to {@link Dactyl#parseURLs}. An array may
+ * contain elements of the following forms:
+ *
+ * • {string} A URL to open.
+ * • {[string, {string|Array}]} Pair of a URL and POST data.
+ * • {object} Object compatible with those returned
+ * by {@link DOM#formData}.
+ *
* @param {object} params A set of parameters specifying how to open the
* URLs. The following properties are recognized:
*
loc = { url: loc[0], postData: loc[1] };
else if (isString(loc))
loc = { url: loc };
+ else
+ loc = Object.create(loc);
+
+ if (isString(loc.postData))
+ loc.postData = ["application/x-www-form-urlencoded", loc.postData];
+
+ if (isArray(loc.postData)) {
+ let stream = services.MIMEStream(services.StringStream(loc.postData[1]));
+ stream.addHeader("Content-Type", loc.postData[0]);
+ stream.addContentLength = true;
+ loc.postData = stream;
+ }
// decide where to load the first url
switch (where) {
if (dactyl.commandLineOptions.rcFile == "NONE" || dactyl.commandLineOptions.noPlugins)
options["loadplugins"] = [];
- if (options["loadplugins"])
+ if (options["loadplugins"].length)
dactyl.loadPlugins();
}
catch (e) {
}
},
- findChar: function findNumber(key, count, backward, offset) {
+ findChar: function findChar(key, count, backward, offset) {
count = count || 1; // XXX ?
offset = (offset || 0) - !!backward;
if (textBox) {
textBox.value = val;
- if (false) {
+ if (true) {
let elem = DOM(textBox);
elem.attrNS(NS, "modifiable", true)
.style.MozUserInput;
preExecute: function preExecute(args) {
if (editor.editor && !this.editor) {
this.editor = editor.editor;
- this.editor.beginTransaction();
+ if (!this.noTransaction)
+ this.editor.beginTransaction();
}
editor.inEditMap = true;
},
postExecute: function preExecute(args) {
editor.inEditMap = false;
if (this.editor) {
- this.editor.endTransaction();
+ if (!this.noTransaction)
+ this.editor.endTransaction();
this.editor = null;
}
},
// text edit mode
bind(["u"], "Undo changes",
function (args) {
- editor.executeCommand("cmd_undo", Math.max(args.count, 1));
+ editor.editor.undo(Math.max(args.count, 1));
editor.deselect();
},
- { count: true });
+ { count: true, noTransaction: true });
bind(["<C-r>"], "Redo undone changes",
function (args) {
- editor.executeCommand("cmd_redo", Math.max(args.count, 1));
+ editor.editor.redo(Math.max(args.count, 1));
editor.deselect();
},
- { count: true });
+ { count: true, noTransaction: true });
bind(["D"], "Delete characters from the cursor to the end of the line",
function () { editor.executeCommand("cmd_deleteToEndOfLine"); });
});
this._fullscreen = window.fullScreen;
- this._lastFocus = null;
+ this._lastFocus = { get: function () null };
this._macroKeys = [];
this._lastMacro = "";
this.active.push(elem);
}
- this.active = this.active.filter(function (e) e.popupBoxObject.popupState != "closed");
+ this.active = this.active.filter(function (e) e.popupBoxObject && e.popupBoxObject.popupState != "closed");
if (!this.active.length && !this.activeMenubar)
modes.remove(modes.MENU, true);
if (quiet)
commandline.quiet = quiet;
- keys = mappings.expandLeader(keys);
-
for (let [, evt_obj] in Iterator(DOM.Event.parse(keys))) {
let now = Date.now();
let key = DOM.Event.stringify(evt_obj);
}
if (elem instanceof Element)
- elem.dactylFocusAllowed = undefined;
+ delete overlay.getData(elem)["focus-allowed"];
},
/*
!this.processor && event.type === "keydown"
&& options.get("passunknown").getKey(modes.main.allBases)
&& let (key = DOM.Event.stringify(event))
- !modes.main.allBases.some(
+ !(modes.main.count && /^\d$/.test(key) ||
+ modes.main.allBases.some(
function (mode) mappings.hives.some(
- function (hive) hive.get(mode, key) || hive.getCandidates(mode, key)));
+ function (hive) hive.get(mode, key) || hive.getCandidates(mode, key))));
events.dbg("ON " + event.type.toUpperCase() + " " + DOM.Event.stringify(event) +
" passing: " + this.passing + " " +
}
let urlbar = document.getElementById("urlbar");
- if (elem == null && urlbar && urlbar.inputField == this._lastFocus)
+ if (elem == null && urlbar && urlbar.inputField == this._lastFocus.get())
util.threadYield(true); // Why? --Kris
while (modes.main.ownsFocus
- && modes.topOfStack.params.ownsFocus != elem
- && modes.topOfStack.params.ownsFocus != win
+ && let ({ ownsFocus } = modes.topOfStack.params)
+ (!ownsFocus ||
+ ownsFocus.get() != elem &&
+ ownsFocus.get() != win)
&& !modes.topOfStack.params.holdFocus)
modes.pop(null, { fromFocus: true });
}
finally {
- this._lastFocus = elem;
+ this._lastFocus = util.weakReference(elem);
if (modes.main.ownsFocus)
- modes.topOfStack.params.ownsFocus = elem;
+ modes.topOfStack.params.ownsFocus = util.weakReference(elem);
}
}),
<xsl:param name="contents" select="text()"/>
<xsl:variable name="tag" select="$contents"/>
<xsl:variable name="tag-url" select="
- regexp:replace(regexp:replace($tag, '%', 'g', '%25'),
- '#', 'g', '%23')"/>
+ regexp:replace(regexp:replace(regexp:replace($tag, '%', 'g', '%25'),
+ '#', 'g', '%23'),
+ ';', 'g', '%3B')"/>
<a style="color: inherit;">
<xsl:if test="not(@link) or @link != 'false'">
if (!rect.width || !rect.height)
if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && DOM(elem).style.float != "none" && isVisible(elem)))
- return false;
+ if (elem.textContent || !elem.name)
+ return false;
let computedStyle = doc.defaultView.getComputedStyle(elem, null);
if (computedStyle.visibility != "visible" || computedStyle.display == "none")
*/
removeHints: function _removeHints(timeout) {
for (let { doc, start, end } in values(this.docs)) {
+ DOM(doc.documentElement).highlight.remove("Hinting");
// Goddamn stupid fucking Gecko 1.x security manager bullshit.
try { delete doc.dactylLabels; } catch (e) { doc.dactylLabels = undefined; }
this.validHints = [];
for (let { doc, start, end } in values(this.docs)) {
+ DOM(doc.documentElement).highlight.add("Hinting");
let [offsetX, offsetY] = this.getContainerOffsets(doc);
inner:
if (!rect)
continue;
- hint.imgSpan = util.xmlToDom(<span highlight="Hint" dactyl:hl="HintImage" xmlns:dactyl={NS}/>, doc);
- hint.imgSpan.style.display = "none";
- hint.imgSpan.style.left = (rect.left + offsetX) + "px";
- hint.imgSpan.style.top = (rect.top + offsetY) + "px";
- hint.imgSpan.style.width = (rect.right - rect.left) + "px";
- hint.imgSpan.style.height = (rect.bottom - rect.top) + "px";
- hint.span.parentNode.appendChild(hint.imgSpan);
+ hint.imgSpan = DOM(<span highlight="Hint" dactyl:hl="HintImage" xmlns:dactyl={NS}/>, doc).css({
+ display: "none",
+ left: (rect.left + offsetX) + "px",
+ top: (rect.top + offsetY) + "px",
+ width: (rect.right - rect.left) + "px",
+ height: (rect.bottom - rect.top) + "px"
+ }).appendTo(hint.span.parentNode)[0];
}
}
* Display the current status to the user.
*/
updateStatusline: function _updateStatusline() {
- statusline.inputBuffer = (this.escapeNumbers ? options["mapleader"] : "") +
+ statusline.inputBuffer = (this.escapeNumbers ? "\\" : "") +
(this.hintNumber ? this.getHintString(this.hintNumber) : "");
},
});
this.addMode("V", "View hint source in external editor", function (elem, loc) buffer.viewSource(loc, true));
this.addMode("y", "Yank hint location", function (elem, loc) editor.setRegister(null, loc, true));
this.addMode("Y", "Yank hint description", function (elem) editor.setRegister(null, elem.textContent || "", true));
+ this.addMode("A", "Yank hint anchor url", function (elem) {
+ let uri = elem.ownerDocument.documentURIObject.clone();
+ uri.ref = elem.id || elem.name;
+ dactyl.clipboardWrite(uri.spec, true);
+ });
this.addMode("c", "Open context menu", function (elem) DOM(elem).contextmenu());
this.addMode("i", "Show image", function (elem) dactyl.open(elem.src));
this.addMode("I", "Show image in a new tab", function (elem) dactyl.open(elem.src, dactyl.NEW_TAB));
let indexOf = String.indexOf;
if (options.get("hintmatching").has("transliterated"))
- indexOf = Hints.indexOf;
+ indexOf = Hints.closure.indexOf;
switch (options["hintmatching"][0]) {
case "contains" : return containsMatcher(hintString);
[0x24d0, 0x24e9, "a"],
[0xfb00, 0xfb06, ["ff", "fi", "fl", "ffi", "ffl", "st", "st"]],
[0xff21, 0xff3a, "A"], [0xff41, 0xff5a, "a"]
- ].forEach(function (start, stop, val) {
+ ].forEach(function ([start, stop, val]) {
if (typeof val != "string")
for (let i = start; i <= stop; i++)
table[String.fromCharCode(i)] = val[(i - start) % val.length];
if (src.length == 0)
return 0;
outer:
- for (var i = 0; i < end; i++) {
+ for (var i = 0; i <= end; i++) {
var j = i;
for (var k = 0; k < src.length;) {
var s = dest[j++];
"Delete the previous character",
function ({ self }) self.backspace());
- bind(["<Leader>"],
+ bind(["\\"],
"Toggle hint filtering",
function ({ self }) { self.escapeNumbers = !self.escapeNumbers; });
},
options.add(["extendedhinttags", "eht"],
"XPath or CSS selector strings of hintable elements for extended hint modes",
"regexpmap", {
+ // Make sure to update the docs when you change this.
"[iI]": "img",
"[asOTvVWy]": [":-moz-any-link", "area[href]", "img[src]", "iframe[src]"],
+ "[A]": ["[id]", "a[name]"],
"[f]": "body",
"[F]": ["body", "code", "div", "html", "p", "pre", "span"],
"[S]": ["input:not([type=hidden])", "textarea", "button", "select"]
getKey: function (val, default_)
let (res = array.nth(this.value, function (re) let (match = re.exec(val)) match && match[0] == val, 0))
res ? res.matcher : default_,
- setter: function (vals) {
+ parse: function parse(val) {
+ let vals = parse.supercall(this, val);
for (let value in values(vals))
value.matcher = DOM.compileMatcher(Option.splitList(value.result));
return vals;
},
+ testValues: function testValues(vals, validator) vals.every(function (re) Option.splitList(re).every(validator)),
validator: DOM.validateMatcher
});
options.add(["hinttags", "ht"],
"XPath or CSS selector strings of hintable elements for Hints mode",
+ // Make sure to update the docs when you change this.
"stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]),select,textarea," +
"[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," +
"[tabindex],[role=link],[role=button],[contenteditable=true]",
get userHives() this.allHives.filter(function (h) h !== this.builtin, this),
- expandLeader: function expandLeader(keyString) keyString.replace(/<Leader>/i, function () options["mapleader"]),
+ expandLeader: deprecated("your brain", function expandLeader(keyString) keyString),
prefixes: Class.Memoize(function () {
let list = Array.map("CASM", function (s) s + "-");
}),
expand: function expand(keys) {
- keys = keys.replace(/<leader>/i, options["mapleader"]);
-
if (!/<\*-/.test(keys))
var res = keys;
else
args["-builtin"] = true;
if (!rhs) // list the mapping
- mappings.list(mapmodes, mappings.expandLeader(lhs), hives);
+ mappings.list(mapmodes, lhs, hives);
else {
util.assert(args["-group"].modifiable,
_("map.builtinImmutable"));
commands.add([ch + "no[remap]"],
"Map a key sequence without remapping keys" + modeDescription,
function (args) { map(args, true); },
- update({}, opts));
+ update({ deprecated: ":" + ch + "map -builtin" }, opts));
commands.add([ch + "unm[ap]"],
"Remove a mapping" + modeDescription,
function (context, obj, args) [[m.names, m.description] for (m in this.iterate(args[0]))]
]);
},
- options: function initOptions(dactyl, modules, window) {
- options.add(["mapleader", "ml"],
- "Define the replacement keys for the <Leader> pseudo-key",
- "string", "\\", {
- setter: function (value) {
- if (this.hasChanged)
- for (let hive in values(mappings.allHives))
- for (let stack in values(hive.stacks))
- delete stack.states;
- return value;
- }
- });
+ mappings: function initMappings(dactyl, modules, window) {
+ mappings.add([modes.COMMAND],
+ ["\\"], "Emits <Leader> pseudo-key",
+ function () { events.feedkeys("<Leader>") });
}
});
onKeyPress: function (events) { if (modes.main == modes.QUOTE) modes.pop(); }
});
this.addMode("IGNORE", { hidden: true }, {
- onKeyPress: function (events) Events.KILL,
+ onKeyPress: function (events) false,
bases: [],
passthrough: true
});
return;
}
- params = params || this.getMode(mainMode || this.main).params;
+ params = params || Object.create(this.getMode(mainMode || this.main).params);
if (!stack && mainMode != null && this._modeStack.length > 1)
this.reset();
"browser.overLink": function (link) {
switch (options["showstatuslinks"]) {
case "status":
+ this.overLink = link ? _("status.link", link) : null;
this.status = link ? _("status.link", link) : buffer.uri;
break;
case "command":
+ this.overLink = null;
if (link)
dactyl.echo(_("status.link", link), commandline.FORCE_SINGLELINE);
else
updateStatus: function updateStatus() {
this.timeout(function () {
- this.status = buffer.uri;
+ this.status = this.overLink || buffer.uri;
});
},
<item>
<tags><yank-location> y</tags>
+ <strut/>
<spec>y</spec>
- <description short="true">
- <p>Yank current location to the clipboard.</p>
+ <description>
+ <p>Yank current location to the clipboard. See also
+ <o>yankshort</o>.</p>
</description>
</item>
<hl key="HelpXMLTagStart"><plugin
<hl key="HelpXMLAttribute">name</hl><hl key="HelpXMLString">flashblock</hl>
<hl key="HelpXMLAttribute">version</hl><hl key="HelpXMLString">1.0</hl>
- <hl key="HelpXMLAttribute">href</hl><hl key="HelpXMLString">http://dactyl.sf.net/pentadactyl/plugins#flashblock-plugin</hl>
+ <hl key="HelpXMLAttribute">href</hl><hl key="HelpXMLString">http://5digits.org/pentadactyl/plugins#flashblock-plugin</hl>
<hl key="HelpXMLAttribute">summary</hl><hl key="HelpXMLString">Flash Blocker</hl>
<hl key="HelpXMLAttribute">xmlns</hl>{NS}></hl></escape>
<author email="maglione.k@gmail.com">Kris Maglione</author>
<dt><k name="Tab"/></dt>
<dd>Moves the focus to the next hintable element</dd>
- <dt><k name="Leader"/></dt>
+ <dt><k>\</k></dt>
<dd>Temporarily treats all numbers (or other keys, depending on the
value of <o>hintkeys</o>) as ordinary text</dd>
<li tag=";V"><em>V</em> to view its destination source in the external editor</li>
<li tag=";y"><em>y</em> to yank its destination location</li>
<li tag=";Y"><em>Y</em> to yank its text description</li>
+ <li tag=";A"><em>A</em> to yank its anchor URL</li>
<li tag=";c"><em>c</em> to open its context menu</li>
<li tag=";i"><em>i</em> to open an image</li>
<li tag=";I"><em>I</em> to open an image in a new tab.</li>
</dl>
<p>
- The ordinary <ex>:map</ex> and <ex>:noremap</ex> commands
- add mappings for Normal and Visual mode. In order to map key
- bindings in a different mode, any of the mapping commands may be
- prefixed with one of the above letters. For instance,
+ The ordinary <ex>:map</ex> command adds mappings for Normal and Visual
+ mode. In order to map key bindings in a different mode, any of the mapping
+ commands may be prefixed with one of the above letters. For instance,
<ex>:imap</ex> creates a new key mapping in Insert mode, while
- <ex>:cunmap</ex> removes a key mapping from Command Line mode.
- Other modes can be specified using the <em>-modes</em> option described below.
+ <ex>:cunmap</ex> removes a key mapping from Command Line mode. Other modes
+ can be specified using the <em>-modes</em> option described below.
</p>
<warning>
</p>
<dl dt="width: 20em;">
- <dt>:map :noremap :unmap</dt> <dd>Normal and Visual modes</dd>
- <dt>:nmap :nnoremap :nunmap</dt> <dd>Normal mode</dd>
- <dt>:vmap :vnoremap :vunmap</dt> <dd>Visual mode</dd>
- <dt>:imap :inoremap :iunmap</dt> <dd>Insert mode</dd>
- <dt>:tmap :tnoremap :tunmap</dt> <dd>Text Edit mode</dd>
- <dt>:cmap :cnoremap :cunmap</dt> <dd>Command Line mode</dd>
+ <dt>:map :unmap</dt> <dd>Normal and Visual modes</dd>
+ <dt>:nmap :nunmap</dt> <dd>Normal mode</dd>
+ <dt>:vmap :vunmap</dt> <dd>Visual mode</dd>
+ <dt>:imap :iunmap</dt> <dd>Insert mode</dd>
+ <dt>:tmap :tunmap</dt> <dd>Text Edit mode</dd>
+ <dt>:cmap :cunmap</dt> <dd>Command Line mode</dd>
</dl>
<note>
<tags>:cno :cnoremap</tags>
<spec>:cno<oa>remap</oa> <a>lhs</a> <a>rhs</a></spec>
<description>
+ <deprecated>These aliases are deprecated. The <em>-builtin</em> flag
+ should be used in their stead.</deprecated>
<p>
Map the <t>key-sequence</t> <a>lhs</a> to <a>rhs</a> for
the applicable mode(s). The keys in <a>rhs</a> do not
</description>
</item>
+<item>
+ <tags><![CDATA['mapleader' <Leader>]]></tags>
+ <strut/>
+ <spec><![CDATA[<Leader>]]></spec>
+ <description>
+ <p>
+ An arbitrary and meaningless key that some people seem
+ attached to. The <k>\</k> key will, by default, emit
+ this pseudo-key so that it can be used at the start of
+ other mappings. Note, however, that there is nothing
+ special about it and any key can be mapped to emit any
+ other arbitrary pseudo-key in the same way.
+ </p>
+
+ <code><ex>:map , <k name="Leader"/></ex>
+<ex>:map ; <k name="AwesomestLeaderEver"/></ex>
+
+<ex>:map <k name="Leader"/>k <key>-js</key> doStuff()</ex>
+<ex>:map <k name="AwesomestLeaderEver"/>k <key>-js</key> doOtherReallyNiftyStuff()</ex></code>
+
+ <p>
+ Needless to say this behavior is considered childish and
+ discouraged.
+ </p>
+ </description>
+</item>
+
<item>
<tags><![CDATA[<Pass>]]></tags>
<spec><![CDATA[<Pass>]]></spec>
</description>
</item>
-<item>
- <tags><![CDATA[<Leader> \]]></tags>
- <strut/>
- <spec><![CDATA[<Leader>]]></spec>
- <description>
- <p>
- A pseudo-key which expands to the value of the <o>mapleader</o>
- option. For example, by default,
- </p>
- <code><ex>:map <k name="Leader"/>h</ex> <ex>:echo <str>Hello</str><k name="CR"/></ex></code>
- <p>works like</p>
- <code><ex>:map \h</ex> <ex>:echo <str>Hello</str><k name="CR"/></ex></code>
- <p>but after</p>
- <set opt="mapleader"><str>,</str></set>
- <p>it works like</p>
- <code><ex>:map ,h</ex> <ex>:echo <str>Hello</str><k name="CR"/></ex></code>
- </description>
-</item>
-
<h3 tag="map-examples">Mapping examples</h3>
<p>Make <k name="A-n" link="false"/> do the same as <k name="Down" link="false"/> in input <t>modes</t>:</p>
<spec>:bmarks<oa>!</oa> <oa>filter</oa></spec>
<description>
<p>
- List or open multiple bookmarks. Opens the message window
- at the bottom of the screen with all bookmarks with
- titles or URLs matching <oa>filter</oa>. The resulting
- URLs can be clicked, or accessed via extended hint modes
- such as <k>;o</k>.
+ List or open multiple bookmarks. Opens the message window at the
+ bottom of the screen with all bookmarks with URLs matching
+ <oa>filter</oa>. The resulting URLs can be clicked, or accessed via
+ extended hint modes such as <k>;o</k>.
</p>
<p>
<p>
QuickMarks are bookmarks stripped to the bone for quickly getting to the
pages that you visit most. A QuickMark is simply a URL assigned to a letter
- or number. They can therefore be saved or opened with only three key
+ or digit. They can therefore be saved or opened with only three key
presses each. QuickMarks are persistent across browser sessions.
</p>
<spec>:qmark <a>a-zA-Z0-9</a> <a>url</a></spec>
<description>
<p>
- Mark <a>url</a> with a letter for quick access. See also
+ Mark <a>url</a> with a letter or digit for quick access. See also
<k>go</k>, <k>gn</k>, and <k>M</k>.
</p>
</p>
<dl dt="width: 10em;">
- <dt>boolean</dt> <dd>Can only be <hl key="Boolean">on</hl> or <hl key="Boolean">off</hl></dd>
+ <dt/><dd tag="boolean"/>
+ <dt>boolean</dt>
+ <dd>
+ Can only be <hl key="Boolean">on</hl> (<ex>:set <a>option</a></ex>) or
+ <hl key="Boolean">off</hl> (<ex>:set no<a>option</a></ex>)
+ </dd>
+
<dt>number</dt> <dd>A numeric value</dd>
<dt>string</dt> <dd>A string value</dd>
<strut/>
<type>&option.extendedhinttags.type;</type>
<default>[asOTvVWy]:':-moz-any-link',area[href],img[src],iframe[src],
+ [A]:[id],a[name],
[f]:body,
[F]:body,code,div,html,p,pre,span,
[iI]:img,
<tags>'ln' 'linenumbers'</tags>
<spec>'linenumbers' 'ln'</spec>
<type>&option.linenumbers.type;</type>
- <default>&option.linenumbers.default;</default>
+ <default><![CDATA['view-source:*':[id^=line],
+ code.google.com:'#nums [id^="nums_table"] a[href^="#"]',
+ github.com:.line_numbers>*,
+ mxr.mozilla.org:a.l,
+ pastebin.com:#code_frame>div>ol>li,
+ addons.mozilla.org:.gutter>.line>a,
+ bugzilla.mozilla.org:.bz_comment:not(.bz_first_comment):not(.ih_history),
+ *:'/* Hgweb/Gitweb */ .completecodeline a.codeline, a.linenr']]></default>
<description>
<p>
Patterns used to determine line numbers used by <k>G</k>. May be
- either a selector expression as accepted by <o>hinttags</o>, in
- which case the first matching element whose text content is equal to
- the desired line number is used or the <oa>count</oa>th element
- failing that, or the string <str delim="'">func:</str> followed by a
- function which, given arguments for the document and desired line
- number, must return the target element.
+ either a selector expression as accepted by <o>hinttags</o>, or the
+ string <str delim="'">func:</str> followed by a JavaScript
+ expression which evaluates to a function. In the former case, the
+ first matching element whose text content is equal to the desired
+ line number is used. If no such element exists, the <oa>count</oa>th
+ matching element is used. In the latter case, the provided function
+ must accept two arguments, the document in question and the desired
+ line number, and must return the element corresponding to the given
+ line.
</p>
</description>
</item>
</description>
</item>
-<item>
- <tags>'ml' 'mapleader'</tags>
- <spec>'mapleader' 'ml'</spec>
- <type>&option.mapleader.type;</type>
- <default>&option.mapleader.default;</default>
- <description>
- <p>Defines the replacement keys for the <k name="Leader"/> pseudo-key.</p>
- </description>
-</item>
-
<item>
<tags>'maxitems'</tags>
<spec>'maxitems'</spec>
<description>
<p>
Pass certain keys through directly for the given URLs.
- For any page with a URL matching a given regexp, all key
- events for keys listed in that regexp's value are passed
+ For any page with a URL matching a given <t>site-filter</t>, all key
+ events for keys listed in the corresponding value are passed
through directly to &dactyl.host;, and are not processed
by &dactyl.appName; in any way. Key names are separated
by commas, where the first key name is treated as a list
<ul>
<li>colors/</li>
- <li>macros/</li>
<li>plugins/</li>
</ul>
<p>
The regular expression used to split URL lists in commands
like <ex>:open</ex>. When set to the empty string, URL lists
- are never split. With the default value, the following will open
- three URLs in the current tab and two new background tabs,
+ are never split. With the default settings, the following will open
+ three URLs (the first one in the current tab and the latter two in
+ two new background tabs):
</p>
<code><ex>:open <str delim="">google Linux</str> | <str delim="">wikipedia Arch Linux</str> | <str delim="">imdb Serenity</str></ex></code>
</description>
name: "exte[nable]",
description: "Enable an extension",
action: function (addon) { addon.userDisabled = false; },
- filter: function ({ item }) item.userDisabled,
+ filter: function (addon) addon.userDisabled,
perm: "enable"
},
disable: {
name: "extd[isable]",
description: "Disable an extension",
action: function (addon) { addon.userDisabled = true; },
- filter: function ({ item }) !item.userDisabled,
+ filter: function (addon) !addon.userDisabled,
perm: "disable"
},
options: {
else
this.dactyl.open(addon.optionsURL, { from: "extoptions" });
},
- filter: function ({ item }) item.isActive && item.optionsURL
+ filter: function (addon) addon.isActive && addon.optionsURL
},
rehash: {
name: "extr[ehash]",
});
},
get filter() {
- return function ({ item }) !item.userDisabled &&
- !(item.operationsRequiringRestart & (AddonManager.OP_NEEDS_RESTART_ENABLE | AddonManager.OP_NEEDS_RESTART_DISABLE))
+ return function (addon) !addon.userDisabled &&
+ !(addon.operationsRequiringRestart & (AddonManager.OP_NEEDS_RESTART_ENABLE | AddonManager.OP_NEEDS_RESTART_DISABLE))
},
perm: "disable"
},
let action = actions[cmd];
if ("perm" in action && !(this.permissions & AddonManager["PERM_CAN_" + action.perm.toUpperCase()]))
return false;
- if ("filter" in action && !action.filter({ item: this }))
+ if ("filter" in action && !action.filter(this))
return false;
return true;
},
}, {
}, {
commands: function (dactyl, modules, window) {
- const { CommandOption, commands, completion } = modules;
+ const { CommandOption, commands, completion, io } = modules;
commands.add(["addo[ns]", "ao"],
"List installed extensions",
// TODO: handle extension dependencies
values(actions).forEach(function (command) {
let perm = command.perm && AddonManager["PERM_CAN_" + command.perm.toUpperCase()];
- function ok(addon) !perm || addon.permissions & perm;
+ function ok(addon) (!perm || addon.permissions & perm) && (!command.filter || command.filter(addon));
commands.add(Array.concat(command.name),
command.description,
dactyl.assert(list.some(ok), _("error.invalidOperation"));
list = list.filter(ok);
}
+ dactyl.assert(list.every(ok));
if (command.actions)
command.actions(list, this.modules);
else
completer: function (context, args) {
completion.addon(context, args["-types"]);
context.filters.push(function ({ item }) ok(item));
- if (command.filter)
- context.filters.push(command.filter);
},
literal: 0,
options: [
for (; obj; obj = prototypes && prototype(obj)) {
try {
if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn)
- var iter = values(Object.getOwnPropertyNames(obj));
+ var iter = (v for each (v in Object.getOwnPropertyNames(obj)));
}
catch (e) {}
if (!iter)
* @returns {Generator}
*/
function values(obj) iter(function values() {
- if (isinstance(obj, ["Generator", "Iterator"]))
+ if (isinstance(obj, ["Generator", "Iterator", Iter]))
for (let k in obj)
yield k;
else
return iter(obj.enumerator());
return iter(obj.enumerator);
}
- res.__noSuchMethod__ = function __noSuchMethod__(meth, args) {
- if (meth in iter)
- var res = iter[meth].apply(iter, [this].concat(args));
- else
- res = let (ary = array(this))
- ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args);
- if (isinstance(res, ["Iterator", "Generator"]))
- return iter(res);
- return res;
- };
- return res;
+ return Iter(res);
}
update(iter, {
toArray: function toArray(iter) array(iter).array,
}
});
+const Iter = Class("Iter", {
+ init: function init(iter) {
+ this.iter = iter;
+ if ("__iterator__" in iter)
+ this.iter = iter.__iterator__();
+
+ if (this.iter.finalize)
+ this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
+ },
+
+ next: function next() this.iter.next(),
+
+ send: function send() this.iter.send.apply(this.iter, arguments),
+
+ __iterator__: function () this.iter
+});
+
/**
* Array utility methods.
*/
}
});
+/* Make Minefield not explode, because Minefield exploding is not fun. */
+let iterProto = Iter.prototype;
+Object.keys(iter).forEach(function (k) {
+ iterProto[k] = function () {
+ let res = iter[k].apply(iter, [this].concat(Array.slice(arguments)));
+ if (isinstance(res, ["Iterator", "Generator"]))
+ return Iter(res);
+ return res;
+ };
+});
+
+Object.keys(array).forEach(function (k) {
+ if (!(k in iterProto))
+ iterProto[k] = function () {
+ let res = array[k].apply(array, [this.toArray()].concat(Array.slice(arguments)));
+ if (isinstance(res, ["Iterator", "Generator"]))
+ return Iter(res);
+ if (isArray(res))
+ return array(res);
+ return res;
+ };
+});
+
+Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
+ if (!(k in iterProto) && callable(Array.prototype[k]))
+ iterProto[k] = function () {
+ let ary = iter(this).toArray();
+ let res = ary[k].apply(ary, arguments);
+ if (isArray(res))
+ return array(res);
+ return res;
+ };
+});
+
endModule();
// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
if (!this.charset || this.charset === "UTF-8")
return encodeURIComponent(str);
let conv = services.CharsetConv(this.charset);
- return escape(conv.ConvertFromUnicode(str) + conv.Finish());
+ return escape(conv.ConvertFromUnicode(str) + conv.Finish()).replace(/\+/g, encodeURIComponent);
+ },
+
+ get folder() {
+ let res = [];
+ res.toString = function () this.join("/");
+
+ let id = this.id, parent, title;
+ while ((id = services.bookmarks.getFolderIdForItem(id)) &&
+ (title = services.bookmarks.getItemTitle(id)))
+ res.push(title);
+
+ return res.reverse();
}
})
Bookmark.prototype.members.uri = Bookmark.prototype.members.url;
let uri = newURI(node.uri);
let keyword = services.bookmarks.getKeywordForBookmark(node.itemId);
- let tags = tags in node ? (node.tags ? node.tags.split(/, /g) : [])
- : services.tagging.getTagsForURI(uri, {}) || [];
+ let tags = "tags" in node ? (node.tags ? node.tags.split(/, /g) : [])
+ : services.tagging.getTagsForURI(uri, {}) || [];
let post = BookmarkCache.getAnnotation(node.itemId, this.POST);
let charset = BookmarkCache.getAnnotation(node.itemId, this.CHARSET);
let query = services.history.getNewQuery();
let options = services.history.getNewQueryOptions();
options.queryType = options.QUERY_TYPE_BOOKMARKS;
- options.excludeItemIfParentHasAnnotation = "livemark/feedURI";
+ try {
+ // https://bugzil.la/702639
+ options.excludeItemIfParentHasAnnotation = "livemark/feedURI";
+ }
+ catch (e) {}
let { root } = services.history.executeQuery(query, options);
root.containerOpen = true;
},
cleanup: function unregister() {
- for each (let factory in this.factories.splice(0))
+ for each (let factory in this.factories)
this.manager.unregisterFactory(factory.classID, factory);
+ this.factories = {};
},
purge: function purge() {
}),
registerFactory: function registerFactory(factory) {
+ if (Set.has(this.factories, factory.contractID))
+ this.manager.unregisterFactory(this.factories[factory.contractID].classID,
+ this.factories[factory.contractID]);
+
this.manager.registerFactory(factory.classID,
String(factory.classID),
factory.contractID,
factory);
- this.factories.push(factory);
+ this.factories[factory.contractID] = factory;
}
};
}, this);
this.lazyRequire("finder", ["RangeFind"]);
+this.lazyRequire("io", ["io"]);
this.lazyRequire("overlay", ["overlay"]);
-this.lazyRequire("storage", ["storage"]);
+this.lazyRequire("storage", ["File", "storage"]);
this.lazyRequire("template", ["template"]);
/**
dactyl.assert(url instanceof Ci.nsIURL);
while (count-- && url.path != "/")
- url.path = url.path.replace(/[^\/]+\/*$/, "");
+ url.path = url.path.replace(/[^\/]*\/*$/, "");
dactyl.assert(!url.equals(this.documentURI));
dactyl.open(url.spec);
function a(regexp, elem) regexp.test(elem.textContent) === regexp.result ||
Array.some(elem.childNodes, function (child) regexp.test(child.alt) === regexp.result);
- function b(regexp, elem) regexp.test(elem.title);
+ function b(regexp, elem) regexp.test(elem.title) === regexp.result;
let res = Array.filter(frame.document.querySelectorAll(selector), Hints.isVisible);
for (let test in values([a, b]))
let { dactyl } = this.modules;
let ctrlKey = false, shiftKey = false;
+ let button = 0;
switch (dactyl.forceTarget || where) {
case dactyl.NEW_TAB:
case dactyl.NEW_BACKGROUND_TAB:
- ctrlKey = true;
+ button = 1;
shiftKey = dactyl.forceBackground != null ? dactyl.forceBackground
: where != dactyl.NEW_BACKGROUND_TAB;
break;
prefs.withContext(function () {
prefs.set("browser.tabs.loadInBackground", true);
let params = {
- screenX: offsetX, screenY: offsetY,
+ button: button, screenX: offsetX, screenY: offsetY,
ctrlKey: ctrlKey, shiftKey: shiftKey, metaKey: ctrlKey
};
| persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
let window = this.topWindow;
+ if (!file.exists())
+ file.create(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666));
+
let downloadListener = new window.DownloadListener(window,
services.Transfer(uri, File(file).URI, "",
null, null, null, persist));
- persist.progressListener = update(Object.create(downloadListener), {
- onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) {
- if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
- util.trapErrors(callback, self, uri, file, progress, request, flags, status);
+ if (callback)
+ persist.progressListener = update(Object.create(downloadListener), {
+ onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) {
+ if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0)
+ util.trapErrors(callback, self, uri, file, progress, request, flags, status);
- return onStateChange.superapply(this, arguments);
- })
- });
+ return onStateChange.superapply(this, arguments);
+ })
+ });
+ else
+ persist.progressListener = downloadListener;
persist.saveURI(uri, null, null, null, null, file);
},
dactyl.assert(!arg || arg[0] == ">" && !config.OS.isWindows,
_("error.trailingCharacters"));
- const PRINTER = "PostScript/default";
- const BRANCH = "print.printer_" + PRINTER + ".";
+ const PRINTER = "PostScript/default";
+ const BRANCH = "printer_" + PRINTER + ".";
+ const BRANCHES = ["print.", BRANCH, "print." + BRANCH];
+ function set(pref, value) {
+ BRANCHES.forEach(function (branch) { prefs.set(branch + pref, value) });
+ }
prefs.withContext(function () {
if (arg) {
prefs.set("print.print_printer", PRINTER);
- prefs.set( "print.print_to_file", true);
- prefs.set(BRANCH + "print_to_file", true);
-
- prefs.set( "print.print_to_filename", io.File(arg.substr(1)).path);
- prefs.set(BRANCH + "print_to_filename", io.File(arg.substr(1)).path);
+ set("print_to_file", true);
+ set("print_to_filename", io.File(arg.substr(1)).path);
dactyl.echomsg(_("print.toFile", arg.substr(1)));
}
dactyl.echomsg(_("print.sending"));
prefs.set("print.always_print_silent", args.bang);
- prefs.set("print.show_print_progress", !args.bang);
+ if (false)
+ prefs.set("print.show_print_progress", !args.bang);
config.browser.contentWindow.print();
});
- if (arg)
- dactyl.echomsg(_("print.printed", arg.substr(1)));
- else
- dactyl.echomsg(_("print.sent"));
+ dactyl.echomsg(_("print.sent"));
},
{
argCount: "?",
bang: true,
+ completer: function (context, args) {
+ if (args.bang && /^>/.test(context.filter))
+ context.fork("file", 1, modules.completion, "file");
+ },
literal: 0
});
context.incomplete = false;
try {
if (/filename="(.*?)"/.test(xhr.getResponseHeader("Content-Disposition")))
- context.completions.push([decodeURIComponent(RegExp.$1), _("buffer.save.suggested")]);
+ context.completions.push([decodeURIComponent(RegExp.$1),
+ _("buffer.save.suggested")]);
}
finally {
context.completions = context.completions.slice();
mappings.add([modes.NORMAL], ["[[", "<previous-page>"],
"Follow the link labeled 'prev', 'previous' or '<' if it exists",
function (args) {
- buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true);
+ buffer.findLink("prev", options["previouspattern"], (args.count || 1) - 1, true);
},
{ count: true });
options.add(["linenumbers", "ln"],
"Patterns used to determine line numbers used by G",
"sitemap", {
+ // Make sure to update the docs when you change this.
+ "view-source:*": 'body,[id^=line]',
"code.google.com": '#nums [id^="nums_table"] a[href^="#"]',
"github.com": '.line_numbers>*',
"mxr.mozilla.org": 'a.l',
"pastebin.com": '#code_frame>div>ol>li',
"addons.mozilla.org": '.gutter>.line>a',
+ "bugzilla.mozilla.org": ".bz_comment:not(.bz_first_comment):not(.ih_history)",
"*": '/* Hgweb/Gitweb */ .completecodeline a.codeline, a.linenr'
},
{
options.add(["nextpattern"],
"Patterns to use when guessing the next page in a document sequence",
- "regexplist", UTF8(/'\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source),
+ "regexplist", UTF8(/'^Next [>»]','^Next »','\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source),
{ regexpFlags: "i" });
options.add(["previouspattern"],
"Patterns to use when guessing the previous page in a document sequence",
- "regexplist", UTF8(/'\bprev|previous\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source),
+ "regexplist", UTF8(/'[<«] Prev$','« Prev$','\bprev(ious)?\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source),
{ regexpFlags: "i" });
options.add(["pageinfo", "pa"],
this._cache.offset = this.offset;
this.lastActivated = this.top.runCount;
}
- if (!this.itemCache[this.key]) {
+ if (!this.itemCache[this.key] && !this.waitingForTab) {
try {
let res = this._generate();
if (res != null)
}
catch (e) {
util.reportError(e);
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
cache[idx] = util.xmlToDom(
<div highlight="CompItem" style="white-space: nowrap">
<li highlight="CompResult">{this.text} </li>
context.hasItems = true;
context.completions = context.completions.filter(function ({ url, title })
words.every(function (w) (url + " " + title).toLowerCase().indexOf(w) >= 0))
- context.incomplete = true;
context.format = this.modules.bookmarks.format;
context.keys.extra = function (item) {
running[provider] = false;
};
- service.startSearch(context.filter, "", context.result, {
- onSearchResult: util.wrapCallback(function onSearchResult(search, result) {
- if (result.searchResult <= result.RESULT_SUCCESS)
- running[provider] = null;
-
- context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
- context.completions = [
- { url: result.getValueAt(i), title: result.getCommentAt(i), icon: result.getImageAt(i) }
- for (i in util.range(0, result.matchCount))
- ];
- }),
- get onUpdateSearchResult() this.onSearchResult
- });
- running[provider] = true;
+ if (!context.waitingForTab) {
+ context.incomplete = true;
+
+ service.startSearch(context.filter, "", context.result, {
+ onSearchResult: util.wrapCallback(function onSearchResult(search, result) {
+ if (result.searchResult <= result.RESULT_SUCCESS)
+ running[provider] = null;
+
+ context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
+ context.completions = [
+ { url: result.getValueAt(i), title: result.getCommentAt(i), icon: result.getImageAt(i) }
+ for (i in util.range(0, result.matchCount))
+ ];
+ }),
+ get onUpdateSearchResult() this.onSearchResult
+ });
+ running[provider] = true;
+ }
}),
urls: function (context, tags) {
this.lazyRequire("messages", ["_"]);
this.lazyRequire("prefs", ["localPrefs", "prefs"]);
this.lazyRequire("storage", ["storage", "File"]);
+this.lazyRequire("styles", ["Styles"]);
function AboutHandler() {}
AboutHandler.prototype = {
let jar = io.isJarURL(uri);
if (jar) {
let prefix = getDir(jar.JAREntry);
- var res = iter(s.slice(prefix.length).replace(/\/.*/, "") for (s in io.listJar(jar.JARFile, prefix)))
+ var res = iter(s.slice(prefix.length).replace(/\/.*/, "")
+ for (s in io.listJar(jar.JARFile, prefix)))
.toArray();
}
else {
if (f.isDirectory())).array;
}
- function exists(pkg) services["resource:"].hasSubstitution("dactyl-locale-" + pkg);
+ let exists = function exists(pkg) services["resource:"].hasSubstitution("dactyl-locale-" + pkg);
return array.uniq([this.appLocale, this.appLocale.replace(/-.*/, "")]
.filter(exists)
dtdDactyl: memoize({
get name() config.name,
- get home() "http://dactyl.sourceforge.net/",
+ get home() "http://5digits.org/",
get apphome() this.home + this.name,
code: "http://code.google.com/p/dactyl/",
get issues() this.home + "bug/" + this.name,
- get plugins() "http://dactyl.sf.net/" + this.name + "/plugins",
+ get plugins() "http://5digits.org/" + this.name + "/plugins",
get faq() this.home + this.name + "/faq",
"list.mailto": Class.Memoize(function () config.name + "@googlegroups.com"),
util.trapErrors("onUnload", self);
}
else {
- self = args && !isArray(args) ? args : newContext.apply(null, args || [userContext]);
+ let params = Array.slice(args || [userContext]);
+ params[2] = params[2] || File(file).URI.spec;
+
+ self = args && !isArray(args) ? args : newContext.apply(null, params);
update(self, {
NAME: Const(id),
exports: ["$", "DOM", "NS", "XBL", "XHTML", "XUL"]
}, this);
+this.lazyRequire("highlight", ["highlight"]);
+this.lazyRequire("template", ["template"]);
+
var XBL = Namespace("xbl", "http://www.mozilla.org/xbl");
var XHTML = Namespace("html", "http://www.w3.org/1999/xhtml");
var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
this[length++] = DOM.fromXML(val, context, this.nodes);
else if (val instanceof Ci.nsIDOMNode || val instanceof Ci.nsIDOMWindow)
this[length++] = val;
- else if ("length" in val)
- for (let i = 0; i < val.length; i++)
- this[length++] = val[i];
else if ("__iterator__" in val || isinstance(val, ["Iterator", "Generator"]))
for (let elem in val)
this[length++] = elem;
+ else if ("length" in val)
+ for (let i = 0; i < val.length; i++)
+ this[length++] = val[i];
else
this[length++] = val;
this.each(function (elem) {
while(true) {
elem = fn.call(this, elem)
- if (elem instanceof Ci.nsIDOMElement)
+ if (elem instanceof Ci.nsIDOMNode)
res[res.length++] = elem;
else if (elem && "length" in elem)
- for (let i = 0; i < tmp.length; i++)
- res[res.length++] = tmp[j];
+ for (let i = 0; i < elem.length; i++)
+ res[res.length++] = elem[j];
else
break;
}
get siblingsBefore() this.all(function (elem) elem.previousElementSibling),
get siblingsAfter() this.all(function (elem) elem.nextElementSibling),
+ get allSiblingsBefore() this.all(function (elem) elem.previousSibling),
+ get allSiblingsAfter() this.all(function (elem) elem.nextSibling),
+
get class() let (self = this) ({
toString: function () self[0].className,
this[0] ? this[0].getBoundingClientRect() : {},
get viewport() {
- if (this[0] instanceof Ci.nsIDOMWindow)
+ let node = this[0];
+ if (node instanceof Ci.nsIDOMDocument)
+ node = node.defaultView;
+
+ if (node instanceof Ci.nsIDOMWindow)
return {
get width() this.right - this.left,
get height() this.bottom - this.top,
- bottom: this[0].innerHeight,
- right: this[0].innerWidth,
+ bottom: node.innerHeight,
+ right: node.innerWidth,
top: 0, left: 0
};
let r = this.rect;
return {
- width: this[0].clientWidth,
- height: this[0].clientHeight,
- top: r.top + this[0].clientTop,
+ width: node.clientWidth,
+ height: node.clientHeight,
+ top: r.top + node.clientTop,
get bottom() this.top + this.height,
- left: r.left + this[0].clientLeft,
+ left: r.left + node.clientLeft,
get right() this.left + this.width
}
},
return editor;
},
- get isEditable() !!this.editor,
+ get isEditable() !!this.editor || this[0] instanceof Ci.nsIDOMElement && this.style.MozUserModify == "read-write",
get isInput() isinstance(this[0], [Ci.nsIDOMHTMLInputElement,
Ci.nsIDOMHTMLTextAreaElement,
else
event = array.toObject([[event, listener]]);
- for (let [k, v] in Iterator(event))
- event[k] = util.wrapCallback(v, true);
+ for (let [evt, callback] in Iterator(event))
+ event[evt] = util.wrapCallback(callback, true);
return this.each(function (elem) {
- for (let [k, v] in Iterator(event))
- elem.addEventListener(k, v, capture);
+ for (let [evt, callback] in Iterator(event))
+ elem.addEventListener(evt, callback, capture);
});
},
unlisten: function unlisten(event, listener, capture) {
if (isObject(event))
capture = listener;
else
- event = array.toObject([[key, val]]);
+ event = array.toObject([[event, listener]]);
return this.each(function (elem) {
for (let [k, v] in Iterator(event))
elem.removeEventListener(k, v.wrapper || v, capture);
});
},
+ once: function once(event, listener, capture) {
+ if (isObject(event))
+ capture = listener;
+ else
+ event = array.toObject([[event, listener]]);
+
+ for (let pair in Iterator(event)) {
+ let [evt, callback] = pair;
+ event[evt] = util.wrapCallback(function wrapper(event) {
+ this.removeEventListener(evt, wrapper.wrapper, capture);
+ return callback.apply(this, arguments);
+ }, true);
+ }
+
+ return this.each(function (elem) {
+ for (let [k, v] in Iterator(event))
+ elem.addEventListener(k, v, capture);
+ });
+ },
dispatch: function dispatch(event, params, extraProps) {
this.canceled = false;
// want to refer to within dactyl's source code for
// comparisons like if (key == "<Esc>") { ... }
this.keyTable = {
- add: ["Plus", "Add"],
+ add: ["+", "Plus", "Add"],
+ back_quote: ["`"],
+ back_slash: ["\\"],
back_space: ["BS"],
+ comma: [","],
count: ["count"],
+ close_bracket: ["]"],
delete: ["Del"],
+ equals: ["="],
escape: ["Esc", "Escape"],
insert: ["Insert", "Ins"],
leader: ["Leader"],
left_shift: ["LT", "<"],
nop: ["Nop"],
+ open_bracket: ["["],
pass: ["Pass"],
+ period: ["."],
+ quote: ["'"],
return: ["Return", "CR", "Enter"],
right_shift: [">"],
+ semicolon: [";"],
slash: ["/"],
space: ["Space", " "],
- subtract: ["Minus", "Subtract"]
+ subtract: ["-", "Minus", "Subtract"]
};
this.key_key = {};
if (event.keyCode in this.code_key) {
key = this.code_key[event.keyCode];
- if (event.shiftKey && (key.length > 1 || event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift)
+ if (event.shiftKey && (key.length > 1 || key.toUpperCase() == key.toLowerCase()
+ || event.ctrlKey || event.altKey || event.metaKey)
+ || event.dactylShift)
modifier += "S-";
else if (!modifier && key.length === 1)
if (event.shiftKey)
null
);
- return Object.create(result, {
- __iterator__: {
- value: asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
- : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
- }
- });
+ let res = {
+ iterateNext: function () result.iterateNext(),
+ get resultType() result.resultType,
+ get snapshotLength() result.snapshotLength,
+ snapshotItem: function (i) result.snapshotItem(i),
+ __iterator__:
+ asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
+ : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
+ };
+ return res;
}
catch (e) {
throw e.stack ? e : Error(e);
exports: ["Download", "Downloads", "downloads"]
}, this);
+this.lazyRequire("overlay", ["overlay"]);
+
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
let prefix = "DOWNLOAD_";
this.nodes = {
commandTarget: self
};
+ XML.ignoreWhitespace = true;
+ XML.prettyPrinting = false;
util.xmlToDom(
<tr highlight="Download" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
<td highlight="DownloadTitle">
message: Class.Memoize(function () {
+ XML.ignoreWhitespace = true;
+ XML.prettyPrinting = false;
util.xmlToDom(<table highlight="Downloads" key="list" xmlns={XHTML}>
- <tr highlight="DownloadHead">
+ <tr highlight="DownloadHead" key="head">
<span>{_("title.Title")}</span>
<span>{_("title.Status")}</span>
<span/>
</tr>
</table>, this.document, this.nodes);
+ this.index = Array.indexOf(this.nodes.list.childNodes,
+ this.nodes.head);
+
for (let row in iter(services.downloadManager.DBConnection
.createStatement("SELECT id FROM moz_downloads")))
this.addDownload(row.id);
.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) {
}
});
-var Downloads = Module("downloads", {
+var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
+ init: function () {
+ services.downloadManager.addListener(this);
+ },
+
+ destroy: 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) {
init: function init() {
prefs.safeSet("accessibility.typeaheadfind.autostart", false);
- // The above should be sufficient, but: http://dactyl.sf.net/bmo/348187
+ // The above should be sufficient, but: http://bugzil.la/348187
prefs.safeSet("accessibility.typeaheadfind", false);
},
| (?: ^ [^\S\n]* \n) +
]]>), "gmxy");
- let betas = util.regexp(/\[(b\d)\]/, "gx");
+ let betas = util.regexp(/\[((?:b|rc)\d)\]/, "gx");
let beta = array(betas.iterate(NEWS))
.map(function (m) m[1]).uniq().slice(-1)[0];
.split(" "));
function fix(node) {
switch(node.nodeType) {
- case Node.ELEMENT_NODE:
- if (isinstance(node, [HTMLBaseElement]))
+ case Ci.nsIDOMNode.ELEMENT_NODE:
+ if (isinstance(node, [Ci.nsIDOMHTMLBaseElement]))
return;
data.push("<"); data.push(node.localName);
- if (node instanceof HTMLHtmlElement)
+ if (node instanceof Ci.nsIDOMHTMLHtmlElement)
data.push(" xmlns=" + XHTML.uri.quote(),
" xmlns:dactyl=" + NS.uri.quote());
if (name == "href") {
value = node.href || value;
if (value.indexOf("dactyl://help-tag/") == 0) {
- let uri = services.io.newChannel(value, null, null).originalURI;
- value = uri.spec == value ? "javascript:;" : uri.path.substr(1);
+ try {
+ let uri = services.io.newChannel(value, null, null).originalURI;
+ value = uri.spec == value ? "javascript:;" : uri.path.substr(1);
+ }
+ catch (e) {
+ util.dump("Magical tag thingy failure for: " + value);
+ dactyl.reportError(e);
+ }
}
if (!/^#|[\/](#|$)|^[a-z]+:/.test(value))
value = value.replace(/(#|$)/, ".xhtml$1");
data.push(" />");
else {
data.push(">");
- if (node instanceof HTMLHeadElement)
+ if (node instanceof Ci.nsIDOMHTMLHeadElement)
data.push(<link rel="stylesheet" type="text/css" href="help.css"/>.toXMLString());
Array.map(node.childNodes, fix);
data.push("</", node.localName, ">");
}
break;
- case Node.TEXT_NODE:
+ case Ci.nsIDOMNode.TEXT_NODE:
data.push(<>{node.textContent}</>.toXMLString());
}
}
+ let { buffer, content, events } = modules;
let chromeFiles = {};
let styles = {};
+
for (let [file, ] in Iterator(help.files)) {
let url = "dactyl://help/" + file;
dactyl.open(url);
util.waitFor(function () content.location.href == url && buffer.loaded
- && content.document.documentElement instanceof HTMLHtmlElement,
+ && content.document.documentElement instanceof Ci.nsIDOMHTMLHtmlElement,
15000);
events.waitForPageLoad();
var data = [
this._lastRunCommand = ""; // updated whenever the users runs a command with :!
this._scriptNames = [];
-
- this.downloadListener = {
- 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;
-
- 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 });
- }
- },
- onStateChange: function () {},
- onProgressChange: function () {},
- onSecurityChange: function () {}
- };
-
- services.downloadManager.addListener(this.downloadListener);
},
CommandFileMode: Class("CommandFileMode", modules.CommandMode, {
}
}),
- destroy: function destroy() {
- services.downloadManager.removeListener(this.downloadListener);
- },
-
/**
* Returns all directories named *name* in 'runtimepath'.
*
*
* @returns {File}
*/
- createTempFile: function createTempFile(name) {
- if (name instanceof Ci.nsIFile)
+ createTempFile: function createTempFile(name, type) {
+ if (name instanceof Ci.nsIFile) {
var file = name.clone();
+ if (!type || type == "file")
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666));
+ else
+ file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, octal(777));
+ }
else {
file = services.directory.get("TmpD", Ci.nsIFile);
file.append(this.config.tempFile + (name ? "." + name : ""));
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666));
}
- file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(600));
services.externalApp.deleteTemporaryFileOnExit(file);
if (bin instanceof File || File.isAbsolutePath(bin))
return this.File(bin);
- let dirs = services.environment.get("PATH").split(config.OS.isWindows ? ";" : ":");
+ let dirs = services.environment.get("PATH")
+ .split(config.OS.pathListSep);
// Windows tries the CWD first TODO: desirable?
if (config.OS.isWindows)
dirs = [io.cwd].concat(dirs);
// TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is
// fixed use that instead of a tmpfile
/**
- * Runs *command* in a subshell and returns the output in a string. The
- * shell used is that specified by the 'shell' option.
+ * Runs *command* in a subshell and returns the output. The shell used is
+ * that specified by the 'shell' option.
*
- * @param {string} command The command to run.
+ * @param {string|[string]} command The command to run. This can be a shell
+ * command string or an array of strings (a command and arguments)
+ * which will be escaped and concatenated.
* @param {string} input Any input to be provided to the command on stdin.
* @param {function(object)} callback A callback to be called when
* the command completes. @optional
* otherwise, the return value of *func*.
*/
withTempFiles: function withTempFiles(func, self, checked, ext) {
- let args = array(util.range(0, func.length)).map(bind("createTempFile", this, ext)).array;
+ let args = array(util.range(0, func.length))
+ .map(bind("createTempFile", this, ext)).array;
try {
if (!args.every(util.identity))
return false;
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("javascript", {
- exports: ["JavaScript", "javascript"]
+ exports: ["JavaScript", "javascript"],
+ require: ["util"]
}, this);
let isPrototypeOf = Object.prototype.isPrototypeOf;
lazyInit: true,
- newContext: function () this.modules.newContext(this.modules.userContext, true),
+ newContext: function () this.modules.newContext(this.modules.userContext, true, "Dactyl JS Temp Context"),
- get completers() JavaScript.completers, // For backward compatibility
+ completers: Class.Memoize(function () Object.create(JavaScript.completers)),
// Some object members are only accessible as function calls
getKey: function (obj, key) {
}
catch (e) {}
if (!completer)
- completer = JavaScript.completers[funcName];
+ completer = this.completers[funcName];
if (!completer)
return null;
let self = this;
let sandbox = true || isinstance(context, ["Sandbox"]);
- this.context = modules.newContext(context, !sandbox);
+ this.context = modules.newContext(context, !sandbox, "Dactyl REPL Context");
this.js = modules.JavaScript();
this.js.replContext = this.context;
- this.js.newContext = function newContext() modules.newContext(self.context, !sandbox);
+ this.js.newContext = function newContext() modules.newContext(self.context, !sandbox, "Dactyl REPL Temp Context");
this.js.globals = [
[this.context, /*L*/"REPL Variables"],
toString: function () "[module " + this.constructor.className + "]"
});
+var _id = 0;
+
var Modules = function Modules(window) {
/**
* @constructor Module
}
},
- newContext: function newContext(proto, normal) {
+ newContext: function newContext(proto, normal, name) {
if (normal)
return create(proto);
var sandbox = services.dactyl.createGlobal();
else
sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules,
+ sandboxName: name || ("Dactyl Sandbox " + ++_id),
wantXrays: false });
// Hack:
this.loaded = {};
modules.loaded = this.loaded;
- defineModule.modules.forEach(function defModule(mod) {
- let names = Set(Object.keys(mod.INIT));
- if ("init" in mod.INIT)
- Set.add(names, "init");
-
- keys(names).forEach(function (name) { self.deferInit(name, mod.INIT, mod); });
- });
this.modules = modules;
this.scanModules();
let className = mod.className || mod.constructor.className;
- init[className] = function callee() {
- function finish() {
- this.currentDependency = className;
- defineModule.time(className, name, INIT[name], mod,
- modules.dactyl, modules, window);
- }
- if (!callee.frobbed) {
- callee.frobbed = true;
- if (modules[name] instanceof Class)
- modules[name].withSavedValues(["currentDependency"], finish);
- else
- finish.call({});
- }
- };
+ if (!Set.has(init, className)) {
+ init[className] = function callee() {
+ function finish() {
+ this.currentDependency = className;
+ defineModule.time(className, name, INIT[name], mod,
+ modules.dactyl, modules, window);
+ }
+ if (!callee.frobbed) {
+ callee.frobbed = true;
+ if (modules[name] instanceof Class)
+ modules[name].withSavedValues(["currentDependency"], finish);
+ else
+ finish.call({});
+ }
+ };
- INIT[name].require = function (name) { init[name](); };
+ INIT[name].require = function (name) { init[name](); };
+ }
},
scanModules: function scanModules() {
let self = this;
let { Module, modules } = this.modules;
+ defineModule.modules.forEach(function defModule(mod) {
+ let names = Set(Object.keys(mod.INIT));
+ if ("init" in mod.INIT)
+ Set.add(names, "init");
+
+ keys(names).forEach(function (name) { self.deferInit(name, mod.INIT, mod); });
+ });
+
Module.list.forEach(function frobModule(mod) {
if (!mod.frobbed) {
modules.__defineGetter__(mod.className, function () {
array.uniq([JSMLoader.getTarget("dactyl://locale/" + this.name + ".properties"),
JSMLoader.getTarget("dactyl://locale-local/" + this.name + ".properties"),
"resource://dactyl-locale/en-US/" + this.name + ".properties",
- "resource://dactyl-locale-local/en-US/" + this.name + ".properties"])
+ "resource://dactyl-locale-local/en-US/" + this.name + ".properties"],
+ true)
.map(services.stringBundle.createBundle)
.filter(function (bundle) { try { bundle.getSimpleEnumeration(); return true; } catch (e) { return false; } })),
if (isArray(defaultValue))
defaultValue = defaultValue.map(Option.quote).join(",");
else if (isObject(defaultValue))
- defaultValue = iter(defaultValue).map(function (val) val.map(Option.quote).join(":")).join(",");
+ defaultValue = iter(defaultValue).map(function (val) val.map(function (v) Option.quote(v, /:/))
+ .join(":"))
+ .join(",");
if (isArray(defaultValue))
defaultValue = defaultValue.map(Option.quote).join(",");
},
unparseRegexp: function unparseRegexp(re, quoted) re.bang + Option.quote(util.regexp.getSource(re), /^!|:/) +
- (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result))),
+ (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result, /:/))),
parseSite: function parseSite(pattern, result, rest) {
if (isArray(rest)) // Called by Array.map
result = undefined;
let [, bang, filter] = /^(!?)(.*)/.exec(pattern);
- filter = Option.dequote(filter);
+ filter = Option.dequote(filter).trim();
- let quote = this.keepQuotes ? util.identity : Option.quote;
+ let quote = this.keepQuotes ? util.identity : function (v) Option.quote(v, /:/);
return update(Styles.matchFilter(filter), {
bang: bang,
stringlist: function (vals) vals.map(Option.quote).join(","),
- stringmap: function (vals) [Option.quote(k, /:/) + ":" + Option.quote(v) for ([k, v] in Iterator(vals))].join(","),
+ stringmap: function (vals) [Option.quote(k, /:/) + ":" + Option.quote(v, /:/) for ([k, v] in Iterator(vals))].join(","),
regexplist: function (vals) vals.join(","),
get regexpmap() this.regexplist,
this._prefContexts = [];
this.branch = services.pref[defaults ? "getDefaultBranch" : "getBranch"](branch || "");
- this.branch instanceof Ci.nsIPrefBranch2;
+ if ("nsIPrefBranch2" in Ci)
+ this.branch instanceof Ci.nsIPrefBranch2;
this.defaults = defaults ? this : this.constructor(branch, true);
}
},
- /**
- * Returns the full name of this object's preference branch.
- */
- get root() this.branch.root,
-
/**
* Returns a new Prefs instance for the sub-branch *branch* of this
* branch.
*
- * @param {string} branch The branch to branch to.
+ * @param {string} branch The sub-branch to branch to.
* @returns {Prefs}
*/
Branch: function Branch(branch) Prefs(this.root + branch),
- observe: null,
- observers: {
- "nsPref:changed": function (subject, data) {
- let observers = this._observers[data];
- if (observers) {
- let value = this.get(data, false);
- this._observers[data] = observers.filter(function (callback) {
- if (!callback.get())
- return false;
- util.trapErrors(callback.get(), null, value);
- return true;
- });
- }
- }
- },
-
/**
- * Adds a new preference observer for the given preference.
+ * Clears the entire branch.
*
- * @param {string} pref The preference to observe.
- * @param {function(object)} callback The callback, called with the
- * new value of the preference whenever it changes.
+ * @param {string} name The name of the preference branch to delete.
*/
- watch: function watch(pref, callback, strong) {
- if (!this.observe) {
- util.addObserver(this);
- this.branch.addObserver("", this, false);
- }
-
- if (!this._observers[pref])
- this._observers[pref] = [];
- this._observers[pref].push(!strong ? util.weakReference(callback) : { get: function () callback });
+ clear: function clear(branch) {
+ this.branch.deleteBranch(branch || "");
},
/**
- * Lists all preferences matching *filter* or only those with changed
- * values if *onlyNonDefault* is specified.
- *
- * @param {boolean} onlyNonDefault Limit the list to prefs with a
- * non-default value.
- * @param {string} filter The list filter. A null filter lists all
- * prefs.
- * @optional
+ * Returns the full name of this object's preference branch.
*/
- list: function list(onlyNonDefault, filter) {
- if (!filter)
- filter = "";
-
- let prefArray = this.getNames();
- prefArray.sort();
- function prefs() {
- for (let [, pref] in Iterator(prefArray)) {
- let userValue = services.pref.prefHasUserValue(pref);
- if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1)
- continue;
-
- let value = this.get(pref);
-
- let option = {
- isDefault: !userValue,
- default: this.defaults.get(pref, null),
- value: <>={template.highlight(value, true, 100)}</>,
- name: pref,
- pre: "\u00a0\u00a0" // Unicode nonbreaking space.
- };
-
- yield option;
- }
- };
-
- return template.options(_("pref.hostPreferences", config.host), prefs.call(this));
- },
+ get root() this.branch.root,
/**
- * Returns the value of a preference.
+ * Returns the value of the preference *name*, or *defaultValue* if
+ * the preference does not exist.
*
- * @param {string} name The preference name.
- * @param {value} defaultValue The value to return if the preference
- * is unset.
+ * @param {string} name The name of the preference to return.
+ * @param {*} defaultValue The value to return if the preference has no value.
+ * @optional
*/
get: function get(name, defaultValue) {
if (defaultValue == null)
defaultValue = null;
+ if (isArray(name))
+ name = name.join(".");
let type = this.branch.getPrefType(name);
try {
getDefault: deprecated("Prefs#defaults.get", function getDefault(name, defaultValue) this.defaults.get(name, defaultValue)),
/**
- * Returns the names of all preferences.
+ * Returns an array of all preference names in this branch or the
+ * given sub-branch.
*
- * @param {string} branch The branch in which to search preferences.
- * @default ""
+ * @param {string} branch The sub-branch for which to return preferences.
+ * @optional
*/
getNames: function getNames(branch) this.branch.getChildList(branch || "", { value: 0 }),
/**
- * Returns true if the current branch has the given preference.
+ * Returns true if the given preference exists in this branch.
*
- * @param {string} name The preference name.
- * @returns {boolean}
+ * @param {string} name The name of the preference to check.
+ */
+ has: function has(name) this.branch.getPrefType(name) !== 0,
+
+ /**
+ * Returns true if the given preference is set to its default value.
+ *
+ * @param {string} name The name of the preference to check.
*/
- has: function get(name) this.branch.getPrefType(name) != 0,
+ isDefault: function isDefault(name) !this.branch.prefHasUserValue(name),
_checkSafe: function _checkSafe(name, message, value) {
let curval = this.get(name, null);
},
/**
- * Sets the preference *name* to *value*.
+ * Sets the preference *name* to *value*. If the preference already
+ * exists, it must have the same type as the given value.
*
- * @param {string} name The preference name.
- * @param {value} value The new preference value.
+ * @param {name} name The name of the preference to change.
+ * @param {string|number|boolean} value The value to set.
* @param {boolean} silent Ignore errors.
*/
set: function set(name, value, silent) {
/**
* Resets the preference *name* to its default value.
*
- * @param {string} name The preference name.
+ * @param {string} name The name of the preference to reset.
*/
reset: function reset(name) {
- try {
+ if (this.branch.prefHasUserValue(name))
this.branch.clearUserPref(name);
- }
- catch (e) {} // ignore - thrown if not a user set value
},
/**
finally {
this.popContext();
}
- }
+ },
+
+ observe: null,
+ observers: {
+ "nsPref:changed": function (subject, data) {
+ let observers = this._observers[data];
+ if (observers) {
+ let value = this.get(data, false);
+ this._observers[data] = observers.filter(function (callback) {
+ if (!callback.get())
+ return false;
+ util.trapErrors(callback.get(), null, value);
+ return true;
+ });
+ }
+ }
+ },
+
+ /**
+ * Adds a new preference observer for the given preference.
+ *
+ * @param {string} pref The preference to observe.
+ * @param {function(object)} callback The callback, called with the
+ * new value of the preference whenever it changes.
+ */
+ watch: function watch(pref, callback, strong) {
+ if (!this.observe) {
+ util.addObserver(this);
+ this.branch.addObserver("", this, false);
+ }
+
+ if (!this._observers[pref])
+ this._observers[pref] = [];
+ this._observers[pref].push(!strong ? util.weakReference(callback) : { get: function () callback });
+ },
+
+ /**
+ * Lists all preferences matching *filter* or only those with changed
+ * values if *onlyNonDefault* is specified.
+ *
+ * @param {boolean} onlyNonDefault Limit the list to prefs with a
+ * non-default value.
+ * @param {string} filter The list filter. A null filter lists all
+ * prefs.
+ * @optional
+ */
+ list: function list(onlyNonDefault, filter) {
+ if (!filter)
+ filter = "";
+
+ let prefArray = this.getNames();
+ prefArray.sort();
+ function prefs() {
+ for (let [, pref] in Iterator(prefArray)) {
+ let userValue = services.pref.prefHasUserValue(pref);
+ if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1)
+ continue;
+
+ let value = this.get(pref);
+
+ let option = {
+ isDefault: !userValue,
+ default: this.defaults.get(pref, null),
+ value: <>={template.highlight(value, true, 100)}</>,
+ name: pref,
+ pre: "\u00a0\u00a0" // Unicode nonbreaking space.
+ };
+
+ yield option;
+ }
+ };
+
+ return template.options(_("pref.hostPreferences", config.host), prefs.call(this));
+ },
}, {
}, {
completion: function init_completion(dactyl, modules) {
function LocaleChannel(pkg, locale, path, orig) {
for each (let locale in [locale, "en-US"])
for each (let sep in "-/") {
- var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true);
+ var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true, true);
if (channel)
return channel;
}
this.channel.contentType = contentType || channel.contentType;
this.channel.contentCharset = "UTF-8";
if (!unprivileged)
- this.channel.owner = systemPrincipal;
-
- let stream = services.InputStream(channelStream);
- let [, pre, doctype, url, extra, open, post] = util.regexp(<![CDATA[
- ^ ([^]*?)
- (?:
- (<!DOCTYPE \s+ \S+ \s+) (?:SYSTEM \s+ "([^"]*)" | ((?:[^[>\s]|\s[^[])*))
- (\s+ \[)?
- ([^]*)
- )?
- $
- ]]>, "x").exec(stream.read(4096));
- this.writes.push(pre);
- if (doctype) {
- this.writes.push(doctype + (extra || "") + " [\n");
- if (url)
- this.addChannel(url);
-
- if (!open)
- this.writes.push("\n]");
-
- for (let [, pre, url] in util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) {
- this.writes.push(pre);
+ this.channel.owner = systemPrincipal;
+
+ let type = this.channel.contentType;
+ if (/^text\/|[\/+]xml$/.test(type)) {
+ let stream = services.InputStream(channelStream);
+ let [, pre, doctype, url, extra, open, post] = util.regexp(<![CDATA[
+ ^ ([^]*?)
+ (?:
+ (<!DOCTYPE \s+ \S+ \s+) (?:SYSTEM \s+ "([^"]*)" | ((?:[^[>\s]|\s[^[])*))
+ (\s+ \[)?
+ ([^]*)
+ )?
+ $
+ ]]>, "x").exec(stream.read(4096));
+ this.writes.push(pre);
+ if (doctype) {
+ this.writes.push(doctype + (extra || "") + " [\n");
if (url)
this.addChannel(url);
+
+ if (!open)
+ this.writes.push("\n]");
+
+ for (let [, pre, url] in util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) {
+ this.writes.push(pre);
+ if (url)
+ this.addChannel(url);
+ }
}
}
this.writes.push(channelStream);
}, this);
this.lazyRequire("messages", ["_"]);
+this.lazyRequire("overlay", ["overlay"]);
this.lazyRequire("storage", ["storage"]);
this.lazyRequire("template", ["teplate"]);
-let tmp = {};
+let tmp = Object.create(this);
JSMLoader.loadSubScript("chrome://browser/content/sanitize.js", tmp);
tmp.Sanitizer.prototype.__proto__ = Class.prototype;
}
});
- let (branch = Item.PREFIX + Item.SHUTDOWN_BRANCH) {
- util.overlayWindow("chrome://browser/content/preferences/sanitize.xul",
- function (win) prefOverlay(branch, true, {
- append: {
- SanitizeDialogPane:
- <groupbox orient="horizontal" xmlns={XUL}>
- <caption label={config.appName + /*L*/" (see :help privacy)"}/>
- <grid flex="1">
- <columns><column flex="1"/><column flex="1"/></columns>
- <rows>{
- let (items = ourItems(true))
- template.map(util.range(0, Math.ceil(items.length / 2)), function (i)
- <row xmlns={XUL}>{
- template.map(items.slice(i * 2, i * 2 + 2), function (item)
- <checkbox xmlns={XUL} label={item.description} preference={branch + item.name}/>)
- }</row>)
- }</rows>
- </grid>
- </groupbox>
- }
- }));
- }
- let (branch = Item.PREFIX + Item.BRANCH) {
- util.overlayWindow("chrome://browser/content/sanitize.xul",
- function (win) prefOverlay(branch, false, {
- append: {
- itemList: <>
- <listitem xmlns={XUL} label={/*L*/"See :help privacy for the following:"} disabled="true" style="font-style: italic; font-weight: bold;"/>
- {
- template.map(ourItems(), function ([item, desc])
- <listitem xmlns={XUL} type="checkbox"
- label={config.appName + " " + desc}
- preference={branch + item}
- onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>)
- }
- </>
- },
- ready: function ready(win) {
- let elem = win.document.getElementById("itemList");
- elem.setAttribute("rows", elem.itemCount);
- win.Sanitizer = Class("Sanitizer", win.Sanitizer, {
- sanitize: function sanitize() {
- self.withSavedValues(["sanitizing"], function () {
- self.sanitizing = true;
- sanitize.superapply(this, arguments);
- sanitizer.sanitizeItems([item.name for (item in values(self.itemMap))
- if (item.shouldSanitize(false))],
- Range.fromArray(this.range || []));
- }, this);
- }
- });
- }
- }));
- }
+ util.timeout(function () { // Load order issue...
+
+ let (branch = Item.PREFIX + Item.SHUTDOWN_BRANCH) {
+ overlay.overlayWindow("chrome://browser/content/preferences/sanitize.xul",
+ function (win) prefOverlay(branch, true, {
+ append: {
+ SanitizeDialogPane:
+ <groupbox orient="horizontal" xmlns={XUL}>
+ <caption label={config.appName + /*L*/" (see :help privacy)"}/>
+ <grid flex="1">
+ <columns><column flex="1"/><column flex="1"/></columns>
+ <rows>{
+ let (items = ourItems(true))
+ template.map(util.range(0, Math.ceil(items.length / 2)), function (i)
+ <row xmlns={XUL}>{
+ template.map(items.slice(i * 2, i * 2 + 2), function (item)
+ <checkbox xmlns={XUL} label={item.description} preference={branch + item.name}/>)
+ }</row>)
+ }</rows>
+ </grid>
+ </groupbox>
+ }
+ }));
+ }
+ let (branch = Item.PREFIX + Item.BRANCH) {
+ overlay.overlayWindow("chrome://browser/content/sanitize.xul",
+ function (win) prefOverlay(branch, false, {
+ append: {
+ itemList: <>
+ <listitem xmlns={XUL} label={/*L*/"See :help privacy for the following:"} disabled="true" style="font-style: italic; font-weight: bold;"/>
+ {
+ template.map(ourItems(), function ([item, desc])
+ <listitem xmlns={XUL} type="checkbox"
+ label={config.appName + " " + desc}
+ preference={branch + item}
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>)
+ }
+ </>
+ },
+ ready: function ready(win) {
+ let elem = win.document.getElementById("itemList");
+ elem.setAttribute("rows", elem.itemCount);
+ win.Sanitizer = Class("Sanitizer", win.Sanitizer, {
+ sanitize: function sanitize() {
+ self.withSavedValues(["sanitizing"], function () {
+ self.sanitizing = true;
+ sanitize.superapply(this, arguments);
+ sanitizer.sanitizeItems([item.name for (item in values(self.itemMap))
+ if (item.shouldSanitize(false))],
+ Range.fromArray(this.range || []));
+ }, this);
+ }
+ });
+ }
+ }));
+ }
+ });
},
firstRun: 0,
this.addClass("HtmlEncoder", "@mozilla.org/layout/htmlCopyEncoder;1", "nsIDocumentEncoder");
this.addClass("InterfacePointer", "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer", "data");
this.addClass("InputStream", "@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init");
+ this.addClass("MIMEStream", "@mozilla.org/network/mime-input-stream;1", "nsIMIMEInputStream", "setData");
this.addClass("Persist", "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", "nsIWebBrowserPersist");
this.addClass("Pipe", "@mozilla.org/pipe;1", "nsIPipe", "init");
this.addClass("Process", "@mozilla.org/process/util;1", "nsIProcess", "init");
this.addClass("Transferable", "@mozilla.org/widget/transferable;1", "nsITransferable");
this.addClass("Timer", "@mozilla.org/timer;1", "nsITimer", "initWithCallback");
this.addClass("URL", "@mozilla.org/network/standard-url;1", ["nsIStandardURL", "nsIURL"], "init");
- this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest", "open");
+ this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", [], "open");
this.addClass("XPathEvaluator", "@mozilla.org/dom/xpath-evaluator;1", "nsIDOMXPathEvaluator");
this.addClass("XMLDocument", "@mozilla.org/xml/xml-document;1", ["nsIDOMXMLDocument", "nsIDOMNodeSelector"]);
this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open", false);
* @returns {nsIURI -> boolean}
*/
matchFilter: function (filter) {
+ filter = filter.trim();
+
if (filter === "*")
var test = function test(uri) true;
else if (!/^(?:[a-z-]+:|[a-z-.]+$)/.test(filter)) {
})
});
+["appendChild", "getAttribute", "insertBefore", "setAttribute"].forEach(function (key) {
+ Object.defineProperty(Binding.prototype, key, {
+ configurable: true,
+ enumerable: false,
+ value: function () this.node[key].apply(this.node, arguments),
+ writable: true
+ });
+});
+
var Template = Module("Template", {
add: function add(a, b) a + b,
join: function join(c) function (a, b) a + c + b,
map: function map(iter, func, sep, interruptable) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
- if (iter.length) // FIXME: Kludge?
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
+ if (typeof iter.length == "number") // FIXME: Kludge?
iter = array.iterValues(iter);
let res = <></>;
let n = 0;
var desc = this.processor[1].call(this, item, item.description);
}
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
// <e4x>
return <div highlight={highlightGroup || "CompItem"} style="white-space: nowrap">
<!-- The non-breaking spaces prevent empty elements
if (help.initialized && !Set.has(help.tags, topic))
return <span highlight={type || ""}>{text || token}</span>;
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
type = type || (/^'.*'$/.test(token) ? "HelpOpt" :
/^\[.*\]$|^E\d{3}$/.test(token) ? "HelpTopic" :
/^:\w/.test(token) ? "HelpEx" : "HelpKey");
if (help.initialized && !Set.has(help.tags, topic))
return <>{token}</>;
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
let tag = (/^'.*'$/.test(token) ? "o" :
/^\[.*\]$|^E\d{3}$/.test(token) ? "t" :
/^:\w/.test(token) ? "ex" : "k");
linkifyHelp: function linkifyHelp(str, help) {
let re = util.regexp(<![CDATA[
(?P<pre> [/\s]|^)
- (?P<tag> '[\w-]+' | :(?:[\w-]+!?|!) | (?:._)?<[\w-]+>\w* | \b[a-zA-Z]_(?:\w+|.) | \[[\w-]+\] | E\d{3} )
+ (?P<tag> '[\w-]+' | :(?:[\w-]+!?|!) | (?:._)?<[\w-]+>\w* | \b[a-zA-Z]_(?:[\w[\]]+|.) | \[[\w-;]+\] | E\d{3} )
(?= [[\)!,:;./\s]|$)
]]>, "gx");
return this.highlightSubstrings(str, (function () {
// if "processStrings" is true, any passed strings will be surrounded by " and
// any line breaks are displayed as \n
highlight: function highlight(arg, processStrings, clip, bw) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
// some objects like window.JSON or getBrowsers()._browsers need the try/catch
try {
let str = this.stringify(arg);
return this.highlightSubstrings(str, (function () {
if (filter.length == 0)
return;
+
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
let lcstr = String.toLowerCase(str);
let lcfilter = filter.toLowerCase();
let start = 0;
</>,
jumps: function jumps(index, elems) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
// <e4x>
return <table>
<tr style="text-align: left;" highlight="Title">
},
options: function options(title, opts, verbose) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
// <e4x>
return <table>
<tr highlight="Title" align="left">
let url = util.fixURI(frame.filename || "unknown");
let path = util.urlPath(url);
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
return <a xmlns:dactyl={NS} dactyl:command="buffer.viewSource"
href={url} path={path} line={frame.lineNumber}
highlight="URL">{
},
table: function table(title, data, indent) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
let table = // <e4x>
<table>
<tr highlight="Title" align="left">
tabular: function tabular(headings, style, iter) {
// TODO: This might be mind-bogglingly slow. We'll see.
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
// <e4x>
return <table>
<tr highlight="Title" align="left">
},
usage: function usage(iter, format) {
- XML.ignoreWhitespace = false; XML.prettyPrinting = false;
+ XML.ignoreWhitespace = XML.prettyPrinting = false;
format = format || {};
let desc = format.description || function (item) template.linkifyHelp(item.description);
let help = format.help || function (item) item.name;
*/
formatSeconds: function formatSeconds(seconds) {
function pad(n, val) ("0000000" + val).substr(-Math.max(n, String(val).length));
- function div(num, denom) [Math.round(num / denom), Math.round(num % denom)];
+ function div(num, denom) [Math.floor(num / denom), Math.round(num % denom)];
let days, hours, minutes;
- [minutes, seconds] = div(seconds, 60);
+ [minutes, seconds] = div(Math.round(seconds), 60);
[hours, minutes] = div(minutes, 60);
[days, hours] = div(hours, 24);
if (days)
* responseType: {string} Override the type of the "response"
* property.
*
+ * headers: {objects} Extra request headers.
+ *
* user: {string} The user name to send via HTTP Authentication.
* pass: {string} The password to send via HTTP Authentication.
*
params.data = data;
}
-
if (params.mimeType)
xmlhttp.overrideMimeType(params.mimeType);
- xmlhttp.open(params.method || "GET", url, async,
- params.user, params.pass);
+ let args = [params.method || "GET", url, async];
+ if (params.user != null || params.pass != null)
+ args.push(params.user);
+ if (params.pass != null)
+ args.push(prams.pass);
+ xmlhttp.open.apply(xmlhttp, args);
+
+ for (let [header, val] in Iterator(params.headers || {}))
+ xmlhttp.setRequestHeader(header, val);
if (params.responseType)
xmlhttp.responseType = params.responseType;
* Escapes a string against shell meta-characters and argument
* separators.
*/
- shellEscape: function shellEscape(str) '"' + String.replace(str, /[\\"$]/g, "\\$&") + '"',
+ shellEscape: function shellEscape(str) '"' + String.replace(str, /[\\"$`]/g, "\\$&") + '"',
/**
* Suspend execution for at least *delay* milliseconds. Functions by
Bell /* &dactyl.appName;’s visual bell */ \
background-color: black !important;
+Hinting;;* /* A document which is currently being hinted */
Hint;;* {
- /* A hint indicator. See <ex>:help hints</ex> */
+ /* A hint indicator. See :help hints */
font: bold 10px "Droid Sans Mono", monospace !important;
margin: -.2ex;
padding: 0 0 0 1px;
error: ["!"]
},
cookies: {
- anyOutput: ["dactyl.sf.net", "dactyl.sf.net list"],
+ anyOutput: ["5digits.org", "5digits.org list"],
error: ["!", ""],
completions: [
"",
- ["dactyl.sf.net ", hasItems]
+ ["5digits.org ", hasItems]
]
},
cunabbreviate: {},
em:version="0.1a1pre"
em:description="Songbird for Vim and CMus junkies."
em:creator="Prathyush Thota"
- em:homepageURL="http://dactyl.sourceforge.net/"
+ em:homepageURL="http://5digits.org/"
em:iconURL="chrome://melodactyl/skin/icon.png"
em:optionsURL="chrome://dactyl/content/preferences.xul">
<em:targetApplication>
• The command line is now hidden by default. Added c, C, and M to
'guioptions'. [b4]
• Hints mode improvements, including:
- - Added g; continued extended hints mode, which allows
+ - Added [;A] extended hint mode. [rc2]
+ - Added [g;] continued extended hints mode, which allows
selecting multiple hints. Removed ;F. [b1]
- Hints are now updated after scrolling and window resizing. [b3]
- - ;s now prompts for a filename on the command-line rather
+ - [;s] now prompts for a filename on the command-line rather
than in a dialog. [b5]
- - Added ;a and ;S modes for creating bookmarks and search keywords. [b4][b3]
+ - Added [;a] and [;S] modes for creating bookmarks and search keywords. [b4][b3]
- Added 'hintkeys' option. [b2]
- Added "transliterated" option to 'hintmatching'. [b1]
• The external editor can now be configured to open to a given
Hint, Multi-line Output, and Menu. [b4]
- <C-o> and <C-i> now behave more like Vim. [b8]
- n_G now uses 'linenumbers' to determine destination if possible. [b8]
- - Add n_s and n_S. [b8]
+ - Added n_s and n_S. [b8]
- Added Operator mode for motion maps, per Vim. [b8]
- Added site-specific mapping groups and related command
changes. [b6]
still be used for this purpose. [b4]
- 'loadplugins' is now a [regexplist] option rather than
a boolean. [b2]
- - 'mapleader' is now an option rather than a :let
- variable. [b4]
+ - Made 'mapleader' an option rather than a :let variable. [b4]
+ - Removed the 'mapleader' option. [rc2]
- 'passkeys' is now a [sitemap] with key chain support rather
than a [regexpmap]. [b6]
- The precise format of 'sanitizeitems' has changed slightly. [b8]
• Added BookmarkChange, BookmarkRemove autocommands. [b2]
• Removed the :source line at the end of files generated by
:mkpentadactylrc. [b2]
- • gf now toggles between source and content view.
+ • n_gf now toggles between source and content view.
The | key binding has been removed. [b1]
• Page zoom information is now shown in the status bar, and
change in zoom status no longer appears in :messages. [b1]
em:id="pentadactyl@dactyl.googlecode.com"
em:type="2"
em:name="Pentadactyl"
- em:version="1.0rc1"
+ em:version="1.0"
em:description="Firefox for Vim and Links addicts"
- em:homepageURL="http://dactyl.sourceforge.net/pentadactyl"
- em:bootstrap="true">
+ em:homepageURL="http://5digits.org/pentadactyl"
+ em:bootstrap="true"
+ em:strictCompatibility="true">
<em:creator>Kris Maglione, Doug Kearns</em:creator>
<em:developer>Kris Maglione</em:developer>
<em:targetApplication>
<Description
em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
- em:minVersion="3.6"
- em:maxVersion="11.*"/>
+ em:minVersion="4.0"
+ em:maxVersion="14.*"/>
</em:targetApplication>
</Description>
</RDF>
em:version="0.6"
em:description="Thunderbird for Mutt and Vim addicts"
em:creator="Kris Maglione"
- em:homepageURL="http://dactyl.sf.net/Teledactyl"
+ em:homepageURL="http://5digits.org/teledactyl"
em:iconURL="chrome://teledactyl/skin/icon.png"
em:bootstrap="true">
<em:targetApplication>