1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2026-01-30 03:05:44 +01:00

Major documentation updates and formatting fixes, and many, many other changes thanks to an MQ glitch, including:

* Significant completion speed improvements
 * Significantly improve startup speed, in large part by lazily
   instantiating Options and Commands, lazily installing highlight
   stylesheets, etc.
 * Update logos and icons, fix atrocious about page
 * Fix Teledactyl
 * JavaScript completion now avoids accessing property values
 * Add Option#persist to define which options are saved with :mkp
 * Add new Dactyl component which holds add-on-specific configuration
   information and removes need for separate components for each dactyl
   host
 * Several fixes for latest nightlies
 * Significant code cleanup and many bug fixes

--HG--
rename : muttator/AUTHORS => teledactyl/AUTHORS
rename : muttator/Donors => teledactyl/Donors
rename : muttator/Makefile => teledactyl/Makefile
rename : muttator/NEWS => teledactyl/NEWS
rename : muttator/TODO => teledactyl/TODO
rename : muttator/chrome.manifest => teledactyl/chrome.manifest
rename : muttator/components/commandline-handler.js => teledactyl/components/commandline-handler.js
rename : muttator/components/protocols.js => teledactyl/components/protocols.js
rename : muttator/content/addressbook.js => teledactyl/content/addressbook.js
rename : muttator/content/compose/compose.js => teledactyl/content/compose/compose.js
rename : muttator/content/compose/compose.xul => teledactyl/content/compose/compose.xul
rename : muttator/content/compose/dactyl.dtd => teledactyl/content/compose/dactyl.dtd
rename : muttator/content/compose/dactyl.xul => teledactyl/content/compose/dactyl.xul
rename : muttator/content/config.js => teledactyl/content/config.js
rename : muttator/content/dactyl.dtd => teledactyl/content/dactyl.dtd
rename : muttator/content/logo.png => teledactyl/content/logo.png
rename : muttator/content/mail.js => teledactyl/content/mail.js
rename : muttator/content/muttator.xul => teledactyl/content/pentadactyl.xul
rename : muttator/contrib/vim/Makefile => teledactyl/contrib/vim/Makefile
rename : muttator/contrib/vim/ftdetect/muttator.vim => teledactyl/contrib/vim/ftdetect/muttator.vim
rename : muttator/contrib/vim/mkvimball.txt => teledactyl/contrib/vim/mkvimball.txt
rename : muttator/contrib/vim/syntax/muttator.vim => teledactyl/contrib/vim/syntax/muttator.vim
rename : muttator/install.rdf => teledactyl/install.rdf
rename : muttator/locale/en-US/Makefile => teledactyl/locale/en-US/Makefile
rename : muttator/locale/en-US/all.xml => teledactyl/locale/en-US/all.xml
rename : muttator/locale/en-US/autocommands.xml => teledactyl/locale/en-US/autocommands.xml
rename : muttator/locale/en-US/gui.xml => teledactyl/locale/en-US/gui.xml
rename : muttator/locale/en-US/intro.xml => teledactyl/locale/en-US/intro.xml
rename : muttator/skin/icon.png => teledactyl/skin/icon.png
This commit is contained in:
Kris Maglione
2010-09-17 06:21:33 -04:00
parent bfbb4b1313
commit 1557b70f45
125 changed files with 4409 additions and 3969 deletions

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -18,7 +18,7 @@ const AutoCommands = Module("autocommands", {
this._store = [];
},
__iterator__: function () util.Array.itervalues(this._store),
__iterator__: function () array.itervalues(this._store),
/**
* Adds a new autocommand. <b>cmd</b> will be executed when one of the
@@ -94,7 +94,7 @@ const AutoCommands = Module("autocommands", {
+
template.map(items, function (item)
<tr>
<td>&#160;{item.pattern.source}</td>
<td>&#xa0;{item.pattern.source}</td>
<td>{item.command}</td>
</tr>))
}

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -11,13 +11,11 @@ const DEFAULT_FAVICON = "chrome://mozapps/skin/places/defaultFavicon.png";
// also includes methods for dealing with keywords and search engines
const Bookmarks = Module("bookmarks", {
init: function () {
let bookmarkObserver = function (key, event, arg) {
storage.addObserver("bookmark-cache", function (key, event, arg) {
if (event == "add")
autocommands.trigger("BookmarkAdd", arg);
statusline.updateUrl();
};
storage.addObserver("bookmark-cache", bookmarkObserver, window);
}, window);
},
get format() ({
@@ -36,7 +34,8 @@ const Bookmarks = Module("bookmarks", {
add: function add(starOnly, title, url, keyword, tags, force) {
try {
let uri = util.createURI(url);
if (!force) {
if (!force && bookmarks.isBookmarked(uri.spec)) {
// WTF? This seems wrong... --Kris
for (let bmark in bookmarkcache) {
if (bmark[0] == uri.spec) {
var id = bmark[5];
@@ -74,21 +73,22 @@ const Bookmarks = Module("bookmarks", {
let count = this.remove(url);
if (count > 0)
commandline.echo("Removed bookmark: " + url, commandline.HL_NORMAL, commandline.FORCE_SINGLELINE);
dactyl.echomsg({ domains: [util.getHost(url)], message: "Removed bookmark: " + url });
else {
let title = buffer.title || url;
let extra = "";
if (title != url)
extra = " (" + title + ")";
this.add(true, title, url);
commandline.echo("Added bookmark: " + url + extra, commandline.HL_NORMAL, commandline.FORCE_SINGLELINE);
dactyl.echomsg({ domains: [util.getHost(url)], message: "Added bookmark: " + url + extra });
}
},
isBookmarked: function isBookmarked(url) {
try {
return services.get("bookmarks").getBookmarkIdsForURI(makeURI(url), {})
.some(bookmarkcache.isRegularBookmark);
return services.get("bookmarks")
.getBookmarkIdsForURI(makeURI(url), {})
.some(bookmarkcache.closure.isRegularBookmark);
}
catch (e) {
return false;
@@ -99,13 +99,14 @@ const Bookmarks = Module("bookmarks", {
remove: function remove(url) {
try {
let uri = util.newURI(url);
let bmarks = services.get("bookmarks").getBookmarkIdsForURI(uri, {})
.filter(bookmarkcache.isRegularBookmark);
let bmarks = services.get("bookmarks")
.getBookmarkIdsForURI(uri, {})
.filter(bookmarkcache.closure.isRegularBookmark);
bmarks.forEach(services.get("bookmarks").removeItem);
return bmarks.length;
}
catch (e) {
dactyl.log(e, 0);
dactyl.reportError(e);
return 0;
}
},
@@ -287,7 +288,7 @@ const Bookmarks = Module("bookmarks", {
args.completeFilter = have.pop();
let prefix = filter.substr(0, filter.length - args.completeFilter.length);
let tags = array.uniq(util.Array.flatten([b.tags for ([k, b] in Iterator(bookmarkcache.bookmarks))]));
let tags = array.uniq(array.flatten([b.tags for ([k, b] in Iterator(bookmarkcache.bookmarks))]));
return [[prefix + tag, tag] for ([i, tag] in Iterator(tags)) if (have.indexOf(tag) < 0)];
},
@@ -330,7 +331,8 @@ const Bookmarks = Module("bookmarks", {
if (bookmarks.add(false, title, url, keyword, tags, args.bang)) {
let extra = (title == url) ? "" : " (" + title + ")";
dactyl.echomsg("Added bookmark: " + url + extra, 1, commandline.FORCE_SINGLELINE);
dactyl.echomsg({ domains: [util.getHost(url)], message: "Added bookmark: " + url + extra },
1, commandline.FORCE_SINGLELINE);
}
else
dactyl.echoerr("Exxx: Could not add bookmark " + title.quote(), commandline.FORCE_SINGLELINE);
@@ -385,7 +387,8 @@ const Bookmarks = Module("bookmarks", {
let url = args.string || buffer.URL;
let deletedCount = bookmarks.remove(url);
dactyl.echomsg(deletedCount + " bookmark(s) with url " + url.quote() + " deleted", 1, commandline.FORCE_SINGLELINE);
dactyl.echomsg({ domains: [util.getHost(url)], message: deletedCount + " bookmark(s) with url " + url.quote() + " deleted" },
1, commandline.FORCE_SINGLELINE);
}
},
@@ -493,13 +496,12 @@ const Bookmarks = Module("bookmarks", {
return history.get({ uri: window.makeURI(begin), uriIsPrefix: true }).map(function (item) {
let rest = item.url.length - end.length;
let query = item.url.substring(begin.length, rest);
if (item.url.substr(rest) == end && query.indexOf("&") == -1) {
if (item.url.substr(rest) == end && query.indexOf("&") == -1)
try {
item.url = decodeURIComponent(query.replace(/#.*/, ""));
item.url = decodeURIComponent(query.replace(/#.*/, "").replace(/\+/g, " "));
return item;
}
catch (e) {}
}
return null;
}).filter(util.identity);
};

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -84,7 +84,7 @@ const Buffer = Module("buffer", {
const ACCESS_READ = Ci.nsICache.ACCESS_READ;
let cacheKey = doc.location.toString().replace(/#.*$/, "");
for (let proto in util.Array.itervalues(["HTTP", "FTP"])) {
for (let proto in array.itervalues(["HTTP", "FTP"])) {
try {
var cacheEntryDescriptor = services.get("cache").createSession(proto, 0, true)
.openCacheEntry(cacheKey, ACCESS_READ, false);
@@ -174,9 +174,11 @@ const Buffer = Module("buffer", {
// event listener which is is called on each page load, even if the
// page is loaded in a background tab
onPageLoad: function onPageLoad(event) {
if (!dactyl.helpInitialized && event.originalTarget instanceof Document)
if (/^dactyl:/.test(event.originalTarget.location.href))
if (event.originalTarget instanceof Document)
if (/^dactyl:/.test(event.originalTarget.location.href)) {
dactyl.initHelp();
config.styleHelp();
}
if (event.originalTarget instanceof HTMLDocument) {
let doc = event.originalTarget;
@@ -197,7 +199,7 @@ const Buffer = Module("buffer", {
doc.pageIsFullyLoaded = 1;
if (doc != config.browser.contentDocument)
dactyl.echomsg("Background tab loaded: " + doc.title || doc.location.href, 3);
dactyl.echomsg({ domains: [util.getHost(doc.location.href)], message: "Background tab loaded: " + doc.title || doc.location.href }, 3);
this._triggerLoadAutocmd("PageLoad", doc);
}
@@ -206,7 +208,11 @@ const Buffer = Module("buffer", {
/**
* @property {Object} The document loading progress listener.
*/
progressListener: update({ __proto__: window.XULBrowserWindow }, {
progressListener: update(Object.create(window.XULBrowserWindow), {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIWebProgressListener]),
loadCount: 0,
// XXX: function may later be needed to detect a canceled synchronous openURL()
onStateChange: function onStateChange(webProgress, request, flags, status) {
onStateChange.superapply(this, arguments);
@@ -223,7 +229,7 @@ const Buffer = Module("buffer", {
// don't reset mode if a frame of the frameset gets reloaded which
// is not the focused frame
if (document.commandDispatcher.focusedWindow == webProgress.DOMWindow) {
if (document.commandDispatcher.focusedWindow == webProgress.DOMWindow && this.loadCount++) {
util.timeout(function () { modes.reset(false); },
dactyl.mode == modes.HINTS ? 500 : 0);
}
@@ -1012,7 +1018,7 @@ const Buffer = Module("buffer", {
if (win.scrollMaxX > 0 || win.scrollMaxY > 0)
return win;
for (let frame in util.Array.itervalues(win.frames))
for (let frame in array.itervalues(win.frames))
if (frame.scrollMaxX > 0 || frame.scrollMaxY > 0)
return frame;
@@ -1314,39 +1320,39 @@ const Buffer = Module("buffer", {
group[1].push([i, tab.linkedBrowser]);
});
let orig = context;
for (let [id, [name, browsers]] in Iterator(tabGroups)) {
context = orig.fork(id, 0);
context.anchored = false;
context.title = [name || "Buffers"];
context.keys = { text: "text", description: "url", icon: "icon" };
context.compare = CompletionContext.Sort.number;
let process = context.process[0];
context.process = [function (item, text)
<>
<span highlight="Indicator" style="display: inline-block;">{item.item.indicator}</span>
{ process.call(this, item, text) }
</>];
context.pushProcessor(0, function (item, text, next) <>
<span highlight="Indicator" style="display: inline-block;">{item.item.indicator}</span>
{ next.call(this, item, text) }
</>);
context.process[1] = function (item, text) template.highlightURL(text);
context.completions = util.map(util.Array.itervalues(browsers), function ([i, browser]) {
let indicator = " ";
if (i == tabs.index())
indicator = "%"
else if (i == tabs.index(tabs.alternate))
indicator = "#";
context.anchored = false;
context.keys = { text: "text", description: "url", icon: "icon" };
context.compare = CompletionContext.Sort.number;
let tab = tabs.getTab(i);
let url = browser.contentDocument.location.href;
i = i + 1;
for (let [id, vals] in Iterator(tabGroups))
context.fork(id, 0, this, function (context, [name, browsers]) {
context.title = [name || "Buffers"];
context.generate = function ()
util.map(array.itervalues(browsers), function ([i, browser]) {
let indicator = " ";
if (i == tabs.index())
indicator = "%"
else if (i == tabs.index(tabs.alternate))
indicator = "#";
return {
text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url],
url: template.highlightURL(url),
indicator: indicator,
icon: tab.image || DEFAULT_FAVICON
};
});
}
let tab = tabs.getTab(i);
let url = browser.contentDocument.location.href;
i = i + 1;
return {
text: [i + ": " + (tab.label || "(Untitled)"), i + ": " + url],
url: url,
indicator: indicator,
icon: tab.image || DEFAULT_FAVICON
};
});
}, vals);
};
},
events: function () {
@@ -1354,6 +1360,15 @@ const Buffer = Module("buffer", {
config.browser.removeProgressListener(window.XULBrowserWindow);
}
catch (e) {} // Why? --djk
// I hate this whole hack. --Kris
let obj = window.XULBrowserWindow, getter;
for (let p in properties(obj))
if ((getter = obj.__lookupGetter__(p)) && !obj.__lookupSetter__(p)) {
this.progressListener.__defineGetter__(p, getter);
delete obj[p];
}
config.browser.addProgressListener(this.progressListener, Ci.nsIWebProgress.NOTIFY_ALL);
window.XULBrowserWindow = this.progressListener;
window.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1503,7 +1518,8 @@ const Buffer = Module("buffer", {
if (elem.readOnly || elem instanceof HTMLInputElement && !set.has(Events.editableInputs, elem.type))
return false;
let computedStyle = util.computedStyle(elem);
return computedStyle.visibility != "hidden" && computedStyle.display != "none";
return computedStyle.visibility != "hidden" && computedStyle.display != "none" &&
computedStyle.MozUserFocus != "ignore";
});
dactyl.assert(elements.length > 0);
@@ -1617,13 +1633,13 @@ const Buffer = Module("buffer", {
function () { buffer.showPageInfo(true); });
},
options: function () {
options.add(["nextpattern"], // \u00BB is » (>> in a single char)
options.add(["nextpattern"],
"Patterns to use when guessing the 'next' page in a document sequence",
"stringlist", "\\bnext\\b,^>$,^(>>|\u00BB)$,^(>|\u00BB),(>|\u00BB)$,\\bmore\\b");
"stringlist", UTF8("\\bnext\\b,^>$,^(>>|»)$,^(>|»),(>|»)$,\\bmore\\b"));
options.add(["previouspattern"], // \u00AB is « (<< in a single char)
options.add(["previouspattern"],
"Patterns to use when guessing the 'previous' page in a document sequence",
"stringlist", "\\bprev|previous\\b,^<$,^(<<|\u00AB)$,^(<|\u00AB),(<|\u00AB)$");
"stringlist", UTF8("\\bprev|previous\\b,^<$,^(<<|«)$,^(<|«),(<|«)$"));
options.add(["pageinfo", "pa"],
"Desired info in the :pageinfo output",

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -103,31 +103,32 @@ const CommandLine = Module("commandline", {
////////////////////// VARIABLES ///////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
this._completionList = ItemList("dactyl-completions");
memoize(this, "_completionList", function () ItemList("dactyl-completions"));
this._completions = null;
this._history = null;
this._startHints = false; // whether we're waiting to start hints mode
this._lastSubstring = "";
this.widgets = {
commandline: document.getElementById("dactyl-commandline"),
prompt: document.getElementById("dactyl-commandline-prompt"),
command: document.getElementById("dactyl-commandline-command"),
memoize(this, "widgets", function () {
let widgets = {
commandline: document.getElementById("dactyl-commandline"),
prompt: document.getElementById("dactyl-commandline-prompt"),
command: document.getElementById("dactyl-commandline-command"),
message: document.getElementById("dactyl-message"),
message: document.getElementById("dactyl-message"),
multilineOutput: document.getElementById("dactyl-multiline-output"),
multilineInput: document.getElementById("dactyl-multiline-input"),
};
multilineOutput: document.getElementById("dactyl-multiline-output"),
multilineInput: document.getElementById("dactyl-multiline-input"),
};
this.widgets.command.inputField.QueryInterface(Ci.nsIDOMNSEditableElement);
this.widgets.message.inputField.QueryInterface(Ci.nsIDOMNSEditableElement);
widgets.command.inputField.QueryInterface(Ci.nsIDOMNSEditableElement);
widgets.message.inputField.QueryInterface(Ci.nsIDOMNSEditableElement);
widgets.mowContainer = widgets.multilineOutput.parentNode;
// the widget used for multiline output
this._outputContainer = this.widgets.multilineOutput.parentNode;
this.widgets.multilineOutput.contentDocument.body.id = "dactyl-multiline-output-content";
widgets.multilineOutput.contentDocument.body.id = "dactyl-multiline-output-content";
return widgets;
});
// we need to save the mode which were in before opening the command line
// this is then used if we focus the command line again without the "official"
@@ -181,12 +182,14 @@ const CommandLine = Module("commandline", {
}
},
/**
* Highlight the messageBox according to <b>group</b>.
*/
_setHighlightGroup: function (group) {
this.widgets.message.setAttributeNS(NS.uri, "highlight", group);
set highlightGroup(group) {
highlight.highlightNode(this.widgets.message, group);
},
get highlightGroup() this.widgets.message.getAttributeNS(NS.uri, "highlight"),
/**
* Determines whether the command line should be visible.
@@ -206,7 +209,7 @@ const CommandLine = Module("commandline", {
this.widgets.prompt.value = val;
this.widgets.prompt.size = val.length;
this.widgets.prompt.collapsed = (val == "");
this.widgets.prompt.setAttributeNS(NS.uri, "highlight", highlightGroup || commandline.HL_NORMAL);
highlight.highlightNode(this.widgets.prompt, highlightGroup || commandline.HL_NORMAL);
},
/**
@@ -229,8 +232,8 @@ const CommandLine = Module("commandline", {
* @param {boolean} forceSingle If provided, don't let over-long
* messages move to the MOW.
*/
_echoLine: function (str, highlightGroup, forceSingle) {
this._setHighlightGroup(highlightGroup);
_echoLine: function echoLine(str, highlightGroup, forceSingle) {
this.highlightGroup = highlightGroup;
this.widgets.message.value = str;
dactyl.triggerObserver("echoLine", str, highlightGroup, forceSingle);
@@ -250,7 +253,7 @@ const CommandLine = Module("commandline", {
* @param {string} highlightGroup
*/
// TODO: resize upon a window resize
_echoMultiline: function (str, highlightGroup) {
_echoMultiline: function echoMultiline(str, highlightGroup) {
let doc = this.widgets.multilineOutput.contentDocument;
let win = this.widgets.multilineOutput.contentWindow;
@@ -260,14 +263,15 @@ const CommandLine = Module("commandline", {
// Otherwise, white space is significant.
// The problem elsewhere is that E4X tends to insert new lines
// after interpolated data.
XML.ignoreWhitespace = typeof str != "xml";
this._lastMowOutput = <div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}>{template.maybeXML(str)}</div>;
XML.ignoreWhitespace = false;
XML.prettyPrinting = false;
let style = typeof str === "string" ? "pre" : "nowrap";
this._lastMowOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{str}</div>;
let output = util.xmlToDom(this._lastMowOutput, doc);
XML.ignoreWhitespace = true;
// FIXME: need to make sure an open MOW is closed when commands
// that don't generate output are executed
if (this._outputContainer.collapsed)
if (this.widgets.mowContainer.collapsed)
doc.body.innerHTML = "";
doc.body.appendChild(output);
@@ -417,6 +421,8 @@ const CommandLine = Module("commandline", {
this._currentExtendedMode = null;
commandline.triggerCallback("cancel", mode);
if (this._completions)
this._completions.previewClear();
if (this._history)
this._history.save();
@@ -431,11 +437,11 @@ const CommandLine = Module("commandline", {
this._completionList.hide();
if (!this._keepCommand || this._silent || this._quiet) {
this._outputContainer.collapsed = true;
this.widgets.mowContainer.collapsed = true;
commandline.updateMorePrompt();
this.hide();
}
if (!this._outputContainer.collapsed) {
if (!this.widgets.mowContainer.collapsed) {
modes.set(modes.COMMAND_LINE, modes.OUTPUT_MULTILINE);
commandline.updateMorePrompt();
}
@@ -492,39 +498,42 @@ const CommandLine = Module("commandline", {
if (flags & this.APPEND_TO_MESSAGES) {
let message = isobject(str) ? str : { message: str };
this._messageHistory.add(update({ highlight: highlightGroup }, str));
this._messageHistory.add(update({ highlight: highlightGroup }, message));
str = message.message;
}
if ((flags & this.ACTIVE_WINDOW) &&
window != services.get("windowWatcher").activeWindow &&
services.get("windowWatcher").activeWindow.dactyl)
return;
if ((flags & this.DISALLOW_MULTILINE) && !this._outputContainer.collapsed)
if ((flags & this.DISALLOW_MULTILINE) && !this.widgets.mowContainer.collapsed)
return;
let single = flags & (this.FORCE_SINGLELINE | this.DISALLOW_MULTILINE);
let action = this._echoLine;
// TODO: this is all a bit convoluted - clean up.
// assume that FORCE_MULTILINE output is fully styled
if (!(flags & this.FORCE_MULTILINE) && !single && (!this._outputContainer.collapsed || this.widgets.message.value == this._lastEcho)) {
highlightGroup += " Message";
action = this._echoMultiline;
}
if ((flags & this.FORCE_MULTILINE) || (/\n/.test(str) || typeof str == "xml") && !(flags & this.FORCE_SINGLELINE))
action = this._echoMultiline;
if (single)
this._lastEcho = null;
else {
if (this.widgets.message.value == this._lastEcho)
this._echoMultiline(<span highlight="Message">{this._lastEcho}</span>,
this.widgets.message.getAttributeNS(NS.uri, "highlight"));
this.highlightGroup);
this._lastEcho = (action == this._echoLine) && str;
}
// TODO: this is all a bit convoluted - clean up.
// assume that FORCE_MULTILINE output is fully styled
if (!(flags & this.FORCE_MULTILINE) && !single
&& (!this.widgets.mowContainer.collapsed || this.widgets.message.value == this._lastEcho)) {
highlightGroup += " Message";
action = this._echoMultiline;
}
if ((flags & this.FORCE_MULTILINE) || (/\n/.test(str) || typeof str == "xml") && !(flags & this.FORCE_SINGLELINE))
action = this._echoMultiline;
if (action)
action.call(this, str, highlightGroup, single);
}),
@@ -937,7 +946,7 @@ const CommandLine = Module("commandline", {
* and what they do.
*/
updateMorePrompt: function updateMorePrompt(force, showHelp) {
if (this._outputContainer.collapsed) {
if (this.widgets.mowContainer.collapsed) {
this._echoLine("", this.HL_NORMAL);
return;
}
@@ -960,19 +969,25 @@ const CommandLine = Module("commandline", {
* @param {boolean} open If true, the widget will be opened if it's not
* already so.
*/
updateOutputHeight: function updateOutputHeight(open) {
if (!open && this._outputContainer.collapsed)
updateOutputHeight: function updateOutputHeight(open, extra) {
if (!open && this.widgets.mowContainer.collapsed)
return;
let doc = this.widgets.multilineOutput.contentDocument;
let availableHeight = config.outputHeight;
if (!this._outputContainer.collapsed)
availableHeight += parseFloat(this._outputContainer.height);
if (!this.widgets.mowContainer.collapsed)
availableHeight += parseFloat(this.widgets.mowContainer.height);
availableHeight -= extra || 0;
doc.body.style.minWidth = this.widgets.commandline.scrollWidth + "px";
this._outputContainer.height = Math.min(doc.height, availableHeight) + "px";
this.widgets.mowContainer.height = Math.min(doc.height, availableHeight) + "px";
this.timeout(function ()
this.widgets.mowContainer.height = Math.min(doc.height, availableHeight) + "px",
0);
doc.body.style.minWidth = "";
this._outputContainer.collapsed = false;
this.widgets.mowContainer.collapsed = false;
},
resetCompletions: function resetCompletions() {
@@ -1015,7 +1030,12 @@ const CommandLine = Module("commandline", {
if (/^\s*$/.test(str))
return;
this.store.mutate("filter", function (line) (line.value || line) != str);
this.store.push({ value: str, timestamp: Date.now()*1000, privateData: this.checkPrivate(str) });
try {
this.store.push({ value: str, timestamp: Date.now()*1000, privateData: this.checkPrivate(str) });
}
catch (e) {
dactyl.reportError(e);
}
this.store.truncate(options["history"], true);
},
/**
@@ -1142,6 +1162,8 @@ const CommandLine = Module("commandline", {
get wildtype() this.wildtypes[this.wildIndex] || "",
get wildtypes() this.wildmode.values,
complete: function complete(show, tabPressed) {
this.context.reset();
this.context.tabPressed = tabPressed;
@@ -1162,7 +1184,7 @@ const CommandLine = Module("commandline", {
let substring = "";
switch (this.wildtype.replace(/.*:/, "")) {
case "":
substring = this.items[0].text;
substring = this.items[0].result;
break;
case "longest":
if (this.items.length > 1) {
@@ -1173,7 +1195,7 @@ const CommandLine = Module("commandline", {
case "full":
let item = this.items[this.selected != null ? this.selected + 1 : 0];
if (item)
substring = item.text;
substring = item.result;
break;
}
@@ -1227,14 +1249,14 @@ const CommandLine = Module("commandline", {
this.wildIndex = 0;
}
this.wildtypes = this.wildmode.values;
this.preview();
},
_reset: function _reset() {
this.prefix = this.context.value.substring(0, this.start);
this.value = this.context.value.substring(this.start, this.caret);
this.suffix = this.context.value.substring(this.caret);
let value = this.editor.selection.focusNode.textContent;
this.prefix = value.substring(0, this.start);
this.value = value.substring(this.start, this.caret);
this.suffix = value.substring(this.caret);
this.itemList.reset();
this.itemList.selectItem(this.selected);
@@ -1301,7 +1323,7 @@ const CommandLine = Module("commandline", {
return;
this.selected = idx;
this.completion = this.items[idx].text;
this.completion = this.items[idx].result;
}
this.itemList.selectItem(idx);
@@ -1320,6 +1342,8 @@ const CommandLine = Module("commandline", {
return;
while (this.tabs.length) {
this.wildIndex = Math.min(this.wildIndex, this.wildtypes.length - 1);
reverse = this.tabs.shift();
switch (this.wildtype.replace(/.*:/, "")) {
case "":
@@ -1340,7 +1364,7 @@ const CommandLine = Module("commandline", {
if (this.haveType("list"))
this.itemList.show();
this.wildIndex = Math.constrain(this.wildIndex + 1, 0, this.wildtypes.length - 1);
this.wildIndex++;
this.preview();
commandline._statusTimer.tell();
@@ -1373,7 +1397,7 @@ const CommandLine = Module("commandline", {
if (typeof arg === "object")
arg = util.objectToString(arg, useColor);
else if (typeof arg == "string" && /\n/.test(arg))
else if (typeof arg === "string" && /\n/.test(arg))
arg = <span highlight="CmdOutput">{arg}</span>;
else
arg = String(arg);
@@ -1533,7 +1557,7 @@ const CommandLine = Module("commandline", {
styles: function () {
let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize;
styles.registerSheet("chrome://dactyl/skin/dactyl.css");
let error = styles.addSheet(true, "font-size", "chrome://dactyl/content/buffer.xhtml",
styles.addSheet(true, "font-size", "chrome://dactyl/content/buffer.xhtml",
"body { font-size: " + fontSize + "; }");
}
});
@@ -1542,7 +1566,7 @@ const CommandLine = Module("commandline", {
* The list which is used for the completion box (and QuickFix window in
* future).
*
* @param {string} id The id of the <iframe> which will display the list. It
* @param {string} id The id of the iframe which will display the list. It
* must be in its own container element, whose height it will update as
* necessary.
*/
@@ -1579,12 +1603,15 @@ const ItemList = Class("ItemList", {
this._minHeight = Math.max(this._minHeight,
this._win.scrollY + this._divNodes.completions.getBoundingClientRect().bottom);
this._container.height = this._minHeight;
if (this._container.collapsed)
this._div.style.minWidth = "";
// FIXME: Belongs elsewhere.
commandline.updateOutputHeight(false, Math.max(0, this._minHeight - this._container.height));
this._container.height = this._minHeight;
this._container.height -= commandline.getSpaceNeeded()
commandline.updateOutputHeight(false);
this.timeout(function () { this._container.height -= commandline.getSpaceNeeded(); }, 0);
},
@@ -1678,7 +1705,7 @@ const ItemList = Class("ItemList", {
for (let [i, row] in Iterator(context.getRows(start, end, this._doc)))
nodes[i] = row;
for (let [i, row] in util.Array.iteritems(nodes)) {
for (let [i, row] in array.iteritems(nodes)) {
if (!row)
continue;
let display = (i >= start && i < end);

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -171,16 +171,10 @@ const Command = Class("Command", {
* @returns {boolean}
*/
hasName: function (name) {
for (let [, spec] in Iterator(this.specs)) {
let fullName = spec.replace(/\[(\w+)]$/, "$1");
let index = spec.indexOf("[");
let min = index == -1 ? fullName.length : index;
if (fullName.indexOf(name) == 0 && name.length >= min)
return true;
}
return false;
return this.specs.some(function (spec) {
let [, head, tail] = spec.match(/([^[]+)(?:\[(.*)])?/);
return name.indexOf(head) == 0 && (head + (tail || "")).indexOf(name) == 0;
});
},
/**
@@ -340,25 +334,29 @@ const Commands = Module("commands", {
/** @property {Iterator(Command)} @private */
__iterator__: function () {
let sorted = this._exCommands.sort(function (a, b) a.name > b.name);
return util.Array.itervalues(sorted);
return array.itervalues(sorted);
},
/** @property {string} The last executed Ex command line. */
repeat: null,
_addCommand: function (command, replace) {
if (command.name in this._exMap) {
if (command.user && replace)
commands.removeUserCommand(command.name);
else {
dactyl.log("Warning: :" + command.name + " already exists, NOT replacing existing command.", 1);
return false;
}
}
_addCommand: function (args, replace) {
let names = array.flatten(Command.parseSpecs(args[0]));
dactyl.assert(!names.some(function (name) name in this._exMap && !this._exMap[name].user, this),
"E182: Can't replace non-user command: " + args[0]);
if (!replace && args[3] && args[3].user)
dactyl.assert(!names.some(function (name) name in this._exMap, this),
"Not replacing command " + args[0]);
for (let name in names)
if (name in this._exMap)
commands.removeUserCommand(name);
this._exCommands.push(command);
for (let [,name] in Iterator(command.names))
this._exMap[name] = command;
let name = names[0];
let closure = function () commands._exMap[name];
memoize(this._exMap, name, function () Command.apply(null, args));
memoize(this._exCommands, this._exCommands.length, closure);
for (let alias in values(names.slice(1)))
memoize(this._exMap, alias, closure);
return true;
},
@@ -375,7 +373,7 @@ const Commands = Module("commands", {
* @optional
*/
add: function (names, description, action, extra) {
return this._addCommand(Command(names, description, action, extra), false);
return this._addCommand([names, description, action, extra], false);
},
/**
@@ -395,7 +393,7 @@ const Commands = Module("commands", {
extra.user = true;
description = description || "User defined command";
return this._addCommand(Command(names, description, action, extra), replace);
return this._addCommand([names, description, action, extra], replace);
},
/**
@@ -407,7 +405,7 @@ const Commands = Module("commands", {
*/
commandToString: function (args) {
let res = [args.command + (args.bang ? "!" : "")];
function quote(str) Commands.quoteArg[/[\s"'\\]|^$/.test(str) ? '"' : ""](str);
function quote(str) Commands.quoteArg[/[\s"'\\]|^$/.test(str) ? "'" : ""](str);
for (let [opt, val] in Iterator(args.options || {})) {
let chr = /^-.$/.test(opt) ? " " : "=";
@@ -431,8 +429,8 @@ const Commands = Module("commands", {
* any of the command's names.
* @returns {Command}
*/
get: function (name) {
return this._exMap[name] || this._exCommands.filter(function (cmd) cmd.hasName(name))[0] || null;
get: function (name, full) {
return this._exMap[name] || !full && this._exCommands.filter(function (cmd) cmd.hasName(name))[0] || null;
},
/**
@@ -573,7 +571,7 @@ const Commands = Module("commands", {
argCount = "*";
var args = []; // parsed options
args.__iterator__ = function () util.Array.iteritems(this);
args.__iterator__ = function () array.iteritems(this);
args.string = str; // for access to the unparsed string
args.literalArg = "";
@@ -833,11 +831,11 @@ const Commands = Module("commands", {
* any of the command's names.
*/
removeUserCommand: function (name) {
for (let [,cmd] in Iterator(this._exCommands))
if (cmd.user && cmd.hasName(name))
for (let [,name] in Iterator(cmd.names))
delete this._exMap[name];
this._exCommands = this._exCommands.filter(function (cmd) !(cmd.user && cmd.hasName(name)));
let cmd = this.get(name);
dactyl.assert(cmd.user, "E184: No such user-defined command: " + name);
for (let name in values(cmd.names))
delete this._exMap[name];
this._exCommands = this._exCommands.filter(function (c) c != cmd);
},
// FIXME: still belong here? Also used for autocommand parameters.
@@ -867,8 +865,6 @@ const Commands = Module("commands", {
});
}
}, {
QUOTE_STYLE: "rc-ish",
// returns [count, parsed_argument]
parseArg: function (str) {
let arg = "";
@@ -918,7 +914,7 @@ const Commands = Module("commands", {
// dynamically get completions as specified with the command's completer function
let command = commands.get(cmd);
if (!command) {
context.highlight(0, cmd.length, "SPELLCHECK");
context.highlight(0, cmd && cmd.length, "SPELLCHECK");
return;
}
@@ -936,7 +932,8 @@ const Commands = Module("commands", {
cmdContext.filter = args.completeFilter;
try {
let compObject = command.completer.call(command, cmdContext, args);
if (compObject instanceof Array) // for now at least, let completion functions return arrays instead of objects
if (isarray(compObject)) // for now at least, let completion functions return arrays instead of objects
compObject = { start: compObject[0], items: compObject[1] };
if (compObject != null) {
cmdContext.advance(compObject.start);
@@ -1041,14 +1038,13 @@ const Commands = Module("commands", {
function completerToString(completer) {
if (completer)
return [k for ([k, v] in Iterator(completeOptionMap)) if (completer == completion[v])][0] || "custom";
else
return "";
return "";
}
// TODO: using an array comprehension here generates flakey results across repeated calls
// : perhaps we shouldn't allow options in a list call but just ignore them for now
// : No, array comprehensions are fine, generator statements aren't. --Kris
let cmds = this._exCommands.filter(function (c) c.user && (!cmd || c.name.match("^" + cmd)));
let cmds = commands._exCommands.filter(function (c) c.user && (!cmd || c.name.match("^" + cmd)));
if (cmds.length > 0)
commandline.commandOutput(
@@ -1101,7 +1097,7 @@ const Commands = Module("commands", {
serialize: function () [ {
command: this.name,
bang: true,
options: util.Array.toObject(
options: array.toObject(
[[v, typeof cmd[k] == "boolean" ? null : cmd[k]]
// FIXME: this map is expressed multiple times
for ([k, v] in Iterator({ argCount: "-nargs", bang: "-bang", count: "-count", description: "-description" }))
@@ -1162,17 +1158,20 @@ const Commands = Module("commands", {
};
function quote(q, list) {
let re = RegExp("[" + list + "]", "g");
return function (str) q + String.replace(str, re, function ($0) $0 in Commands.quoteMap ? Commands.quoteMap[$0] : ("\\" + $0)) + q;
let res = function (str) q + String.replace(str, re, function ($0) $0 in Commands.quoteMap ? Commands.quoteMap[$0] : ("\\" + $0)) + q;
res.list = list;
return res;
};
Commands.complQuote = { // FIXME
Commands.complQuote = {
'"': ['"', quote("", '\n\t"\\\\'), '"'],
"'": ["'", quote("", "\\\\'"), "'"],
"": ["", quote("", "\\\\ "), ""]
"": ["", quote("", "\\\\ '\""), ""]
};
Commands.quoteArg = {
'"': quote('"', '\n\t"\\\\'),
"'": quote("'", "\\\\'"),
"": quote("", "\\\\ ")
"": quote("", "\\\\ '\"")
};
Commands.parseBool = function (arg) {

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -54,7 +54,7 @@ const CompletionContext = Class("CompletionContext", {
["filters", "keys", "title", "quote"].forEach(function (key)
self[key] = parent[key] && util.cloneObject(parent[key]));
["anchored", "compare", "editor", "_filter", "filterFunc", "keys", "_process", "top"].forEach(function (key)
["anchored", "compare", "editor", "_filter", "filterFunc", "keys", "process", "top"].forEach(function (key)
self[key] = parent[key]);
self.__defineGetter__("value", function () this.top.value);
@@ -90,14 +90,34 @@ const CompletionContext = Class("CompletionContext", {
this._value = editor;
else
this.editor = editor;
this.compare = function (a, b) String.localeCompare(a.text, b.text);
/**
* @property {boolean} Specifies whether this context results must
* match the filter at the beginning of the string.
* @default true
*/
this.anchored = true;
this.compare = function (a, b) String.localeCompare(a.text, b.text);
/**
* @property {function} This function is called when we close
* a completion window with Esc or Ctrl-c. Usually this callback
* is only needed for long, asynchronous completions
*/
this.cancel = null;
/**
* @property {[CompletionContext]} A list of active
* completion contexts, in the order in which they were
* instantiated.
*/
this.contextList = [];
/**
* @property {Object} A map of all contexts, keyed on their names.
* Names are assigned when a context is forked, with its specified
* name appended, after a '/', to its parent's name. May
* contain inactive contexts. For active contexts, see
* {@link #contextList}.
*/
this.contexts = { "": this };
/**
* @property {function} The function used to filter the results.
* @default Selects all results which match every predicate in the
@@ -114,20 +134,6 @@ const CompletionContext = Class("CompletionContext", {
* results.
*/
this.filters = [CompletionContext.Filter.text];
/**
* @property {boolean} Specifies whether this context results must
* match the filter at the beginning of the string.
* @default true
*/
this.anchored = true;
/**
* @property {Object} A map of all contexts, keyed on their names.
* Names are assigned when a context is forked, with its specified
* name appended, after a '/', to its parent's name. May
* contain inactive contexts. For active contexts, see
* {@link #contextList}.
*/
this.contexts = { "": this };
/**
* @property {Object} A mapping of keys, for {@link #getKey}. Given
* { key: value }, getKey(item, key) will return values as such:
@@ -146,6 +152,9 @@ const CompletionContext = Class("CompletionContext", {
* {@link #updateAsync} is true.
*/
this.onUpdate = function () true;
this.runCount = 0;
/**
* @property {CompletionContext} The top-level completion context.
*/
@@ -190,6 +199,7 @@ const CompletionContext = Class("CompletionContext", {
: item.item[key];
return this;
},
// Temporary
/**
* @property {Object}
@@ -202,7 +212,7 @@ const CompletionContext = Class("CompletionContext", {
get allItems() {
try {
let self = this;
let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.items.length && context.hasItems)]);
let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.hasItems && context.items.length)]);
if (minStart == Infinity)
minStart = 0;
let items = this.contextList.map(function (context) {
@@ -214,7 +224,7 @@ const CompletionContext = Class("CompletionContext", {
__proto__: item
}));
});
return { start: minStart, items: util.Array.flatten(items), longestSubstring: this.longestAllSubstring };
return { start: minStart, items: array.flatten(items), longestSubstring: this.longestAllSubstring };
}
catch (e) {
dactyl.reportError(e);
@@ -235,7 +245,7 @@ const CompletionContext = Class("CompletionContext", {
lists.pop());
if (!substrings) // FIXME: How is this undefined?
return [];
return util.Array.uniq(Array.slice(substrings));
return array.uniq(Array.slice(substrings));
},
// Temporary
get longestAllSubstring() {
@@ -253,14 +263,16 @@ const CompletionContext = Class("CompletionContext", {
// Accept a generator
if (!isarray(items))
items = [x for (x in Iterator(items))];
delete this.cache.filtered;
delete this.cache.filter;
this.cache.rows = [];
this.hasItems = items.length > 0;
this._completions = items;
let self = this;
if (this._completions !== items) {
delete this.cache.filtered;
delete this.cache.filter;
this.cache.rows = [];
this.hasItems = items.length > 0;
this._completions = items;
this.itemCache[this.key] = items;
}
if (this.updateAsync && !this.noUpdate)
util.callInMainThread(function () { self.onUpdate.call(self); });
util.callInMainThread(function () { this.onUpdate(); }, this);
},
get createRow() this._createRow || template.completionRow, // XXX
@@ -293,14 +305,18 @@ const CompletionContext = Class("CompletionContext", {
get proto() {
let res = {};
for (let i in Iterator(this.keys)) {
function result(quote) {
yield ["result", quote ? function () quote[0] + quote[1](this.text) + quote[2]
: function () this.text]
};
for (let i in iterall(this.keys, result(this.quote))) {
let [k, v] = i;
if (typeof v == "string" && /^[.[]/.test(v))
// This is only allowed to be a simple accessor, and shouldn't
// reference any variables. Don't bother with eval context.
v = Function("i", "return i" + v);
if (typeof v == "function")
res.__defineGetter__(k, function () Class.replaceProperty(this, k, v(this.item)));
res.__defineGetter__(k, function () Class.replaceProperty(this, k, v.call(this, this.item)));
else
res.__defineGetter__(k, function () Class.replaceProperty(this, k, this.item[v]));
res.__defineSetter__(k, function (val) Class.replaceProperty(this, k, val));
@@ -312,11 +328,16 @@ const CompletionContext = Class("CompletionContext", {
set regenerate(val) { if (val) delete this.itemCache[this.key]; },
get generate() !this._generate ? null : function () {
if (this.offset != this.cache.offset)
if (this.offset != this.cache.offset || this.lastActivated != this.top.runCount) {
this.itemCache = {};
this.cache.offset = this.offset;
if (!this.itemCache[this.key])
this.itemCache[this.key] = this._generate.call(this) || [];
this.cache.offset = this.offset;
this.lastActivated = this.top.runCount;
}
if (!this.itemCache[this.key]) {
let res = this._generate.call(this) || [];
if (res != null)
this.itemCache[this.key] = res;
}
return this.itemCache[this.key];
},
set generate(arg) {
@@ -354,16 +375,27 @@ const CompletionContext = Class("CompletionContext", {
get items() {
if (!this.hasItems || this.backgroundLock)
return [];
if (this.cache.filtered && this.cache.filter == this.filter)
return this.cache.filtered;
this.cache.rows = [];
let items = this.completions;
// Regenerate completions if we must
if (this.generate && !this.background) {
// XXX
this.noUpdate = true;
this.completions = items = this.generate();
this.completions = this.generate();
this.noUpdate = false;
}
let items = this.completions;
// Check for cache miss
if (this.cache.completions !== this.completions) {
this.cache.completions = this.completions;
this.cache.constructed = null;
this.cache.filtered = null;
}
if (this.cache.filtered && this.cache.filter == this.filter)
return this.cache.filtered;
this.cache.rows = [];
this.cache.filter = this.filter;
if (items == null)
return items;
@@ -371,6 +403,7 @@ const CompletionContext = Class("CompletionContext", {
let self = this;
delete this._substrings;
// Item matchers
if (this.ignoreCase)
this.matchString = this.anchored ?
function (filter, str) String.toLowerCase(str).indexOf(filter.toLowerCase()) == 0 :
@@ -380,36 +413,28 @@ const CompletionContext = Class("CompletionContext", {
function (filter, str) String.indexOf(str, filter) == 0 :
function (filter, str) String.indexOf(str, filter) >= 0;
// Item formatters
this.processor = Array.slice(this.process);
if (!this.anchored)
this.processor[0] = function (item, text) self.process[0].call(self, item,
template.highlightFilter(item.text, self.filter));
// Item prototypes
let proto = this.proto;
let filtered = this.filterFunc(items.map(function (item) ({ __proto__: proto, item: item })));
if (!this.cache.constructed)
this.cache.constructed = items.map(function (item) Object.create(proto, { item: { value: item, enumerable: true } }));
// Filters
let filtered = this.filterFunc(this.cache.constructed);
if (this.maxItems)
filtered = filtered.slice(0, this.maxItems);
// Sorting
if (this.sortResults && this.compare)
filtered.sort(this.compare);
let quote = this.quote;
if (quote)
filtered.forEach(function (item) {
item.unquoted = item.text;
item.text = quote[0] + quote[1](item.text) + quote[2];
});
return this.cache.filtered = filtered;
},
get process() { // FIXME
let self = this;
let process = this._process;
process = [process[0] || template.icon, process[1] || function (item, k) k];
let first = process[0];
let filter = this.filter;
if (!this.anchored)
process[0] = function (item, text) first.call(self, item, template.highlightFilter(item.text, filter));
return process;
},
set process(process) {
this._process = process;
},
get substrings() {
let items = this.items;
if (items.length == 0 || !this.hasItems)
@@ -418,7 +443,10 @@ const CompletionContext = Class("CompletionContext", {
return this._substrings;
let fixCase = this.ignoreCase ? String.toLowerCase : util.identity;
let text = fixCase(items[0].unquoted || items[0].text);
let text = fixCase(items[0].text);
// Exceedingly long substrings cause Gecko to go into convulsions
if (text.length > 100)
text = text.substr(0, 100);
let filter = fixCase(this.filter);
if (this.anchored) {
var compare = function compare(text, s) text.substr(0, s.length) == s;
@@ -521,7 +549,6 @@ const CompletionContext = Class("CompletionContext", {
context.waitingForTab = true;
else if (completer)
return completer.apply(self || this, [context].concat(Array.slice(arguments, fork.length)));
if (completer)
return null;
return context;
@@ -535,20 +562,24 @@ const CompletionContext = Class("CompletionContext", {
},
highlight: function highlight(start, length, type) {
try { // Gecko < 1.9.1 doesn't have repaintSelection
this.selectionTypes[type] = null;
if (arguments.length == 0) {
for (let type in this.selectionTypes)
this.highlight(0, 0, type);
this.selectionTypes = {};
}
try {
// Requires Gecko >= 1.9.1
this.selectionTypes[type] = true;
const selType = Ci.nsISelectionController["SELECTION_" + type];
const editor = this.editor;
let sel = editor.selectionController.getSelection(selType);
let sel = this.editor.selectionController.getSelection(selType);
if (length == 0)
sel.removeAllRanges();
else {
let range = editor.selection.getRangeAt(0).cloneRange();
let range = this.editor.selection.getRangeAt(0).cloneRange();
range.setStart(range.startContainer, this.offset + start);
range.setEnd(range.startContainer, this.offset + start + length);
sel.addRange(range);
}
editor.selectionController.repaintSelection(selType);
}
catch (e) {}
},
@@ -557,23 +588,19 @@ const CompletionContext = Class("CompletionContext", {
return this.matchString(this.filter, str);
},
pushProcessor: function pushProcess(i, fn) {
let next = this.process[i];
this.process[i] = function (item, text) fn(item, text, next);
},
reset: function reset() {
let self = this;
if (this.parent)
throw Error();
// Not ideal.
for (let type in this.selectionTypes)
this.highlight(0, 0, type);
/**
* @property {[CompletionContext]} A list of active
* completion contexts, in the order in which they were
* instantiated.
*/
this.contextList = [];
this.offset = 0;
this.process = [];
this.selectionTypes = {};
this.process = [template.icon, function (item, k) k];
this.filters = [CompletionContext.Filter.text];
this.tabPressed = false;
this.title = ["Completions"];
this.updateAsync = false;
@@ -595,6 +622,10 @@ const CompletionContext = Class("CompletionContext", {
if (context != context.top)
context.incomplete = false;
}
this.runCount++;
for each (let context in this.contextList)
context.lastActivated = this.runCount;
this.contextList = [];
},
/**
@@ -668,7 +699,7 @@ const Completion = Module("completion", {
commandline.commandOutput(
<div highlight="Completions">
{ template.map(context.contextList.filter(function (c) c.hasItems),
{ template.map(context.contextList.filter(function (c) c.hasItems && c.items.length),
function (context)
template.completionRow(context.title, "CompTitle") +
template.map(context.items, function (item) context.createRow(item), null, 100)) }
@@ -747,7 +778,7 @@ const Completion = Module("completion", {
let re = RegExp(tokens.filter(util.identity).map(util.escapeRegex).join("|"), "g");
function highlight(item, text, i) process[i].call(this, item, template.highlightRegexp(text, re));
let process = [template.icon, function (item, k) k];
let process = context.process;
context.process = [
function (item, text) highlight.call(this, item, item.text, 0),
function (item, text) highlight.call(this, item, text, 1)

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -12,8 +12,33 @@ const ConfigBase = Class(ModuleBase, {
* initialization code. Must call superclass's init function.
*/
init: function () {
this.name = services.get("dactyl:").name;
this.appname = services.get("dactyl:").appname;
this.host = services.get("dactyl:").host;
highlight.styleableChrome = this.styleableChrome;
highlight.loadCSS(this.CSS);
highlight.loadCSS(this.helpCSS);
let img = Image();
img.src = this.logo || "chrome://" + this.name + "/content/logo.png";
img.onload = function () {
highlight.set("Logo", String(<>
display: inline-block;
background: url({img.src});
width: {img.width}px;
height: {img.height}px;
</>));
img = null;
}
},
styleHelp: function () {
if (!this.helpStyled)
for (let k in keys(highlight.loaded))
if (/^(Help|StatusLine)|^(Boolean|Indicator|MoreMsg|Number|Logo|Key(word)?|String)$/.test(k))
highlight.loaded[k] = true;
this.helpCSS = true;
},
/**
@@ -92,7 +117,7 @@ const ConfigBase = Class(ModuleBase, {
* @property {number} The height (px) that is available to the output
* window.
*/
get outputHeight() config.browser.mPanelContainer.boxObject.height,
get outputHeight() this.browser.mPanelContainer.boxObject.height,
/**
* @property {[string]} A list of extra scripts in the dactyl or
@@ -105,45 +130,41 @@ const ConfigBase = Class(ModuleBase, {
* @property {string} The leaf name of any temp files created by
* {@link io.createTempFile}.
*/
get tempFile() this.name.toLowerCase() + ".tmp",
get tempFile() this.name + ".tmp",
/**
* @constant
* @property {string} The default highlighting rules. They have the
* form:
* rule ::= selector space space+ css
* selector ::= group
* | group "," css-selector
* | group "," css-selector "," scope
* group ::= groupname
* | groupname css-selector
* @property {string} The default highlighting rules.
* See {@link Highlights#loadCSS} for details.
*/
// <css>
CSS: <![CDATA[
Boolean color: red;
Function color: navy;
Null color: blue;
Number color: blue;
Object color: maroon;
String color: green;
CSS: UTF8(<><![CDATA[
// <css>
Boolean color: red;
Function color: navy;
Null color: blue;
Number color: blue;
Object color: maroon;
String color: green;
Key font-weight: bold;
Key font-weight: bold;
Enabled color: blue;
Disabled color: red;
Enabled color: blue;
Disabled color: red;
Normal color: black; background: white;
ErrorMsg color: white; background: red; font-weight: bold;
InfoMsg color: black; background: white;
ModeMsg color: black; background: white;
MoreMsg color: green; background: white;
WarningMsg color: red; background: white;
Message white-space: normal; min-width: 100%; padding-left: 2em; text-indent: -2em; display: block;
NonText color: blue; min-height: 16px; padding-left: 2px;
Preview color: gray;
!Normal color: black !important; background: white !important;
ErrorMsg color: white !important; background: red !important; font-weight: bold !important;
InfoMsg color: black !important; background: white !important;
LineNr color: orange !important; background: white !important;
ModeMsg color: black !important; background: white !important;
MoreMsg color: green !important; background: white !important;
Message white-space: normal; min-width: 100%; padding-left: 2em; text-indent: -2em; display: block;
NonText color: blue; min-height: 16px; padding-left: 2px;
*Preview color: gray;
Question color: green !important; background: white !important; font-weight: bold !important;
WarningMsg color: red !important; background: white !important;
CmdLine,>* font-family: monospace; padding: 1px;
CmdOutput white-space: pre;
!CmdLine;>* font-family: monospace !important; padding: 1px !important;
CmdOutput white-space: pre;
CompGroup
CompGroup:not(:first-of-type) margin-top: .5em;
@@ -158,9 +179,10 @@ const ConfigBase = Class(ModuleBase, {
CompResult width: 45%; overflow: hidden;
CompDesc color: gray; width: 50%;
CompLess text-align: center; height: 0; line-height: .5ex; padding-top: 1ex;
CompLess::after content: "\2303" /* Unicode up arrowhead */
CompLess::after content: "⌃";
CompMore text-align: center; height: .5ex; line-height: .5ex; margin-bottom: -.5ex;
CompMore::after content: "\2304" /* Unicode down arrowhead */
CompMore::after content: "⌄";
CompGroup:last-of-type padding-bottom: 1.5ex;
Gradient height: 1px; margin-bottom: -1px; margin-top: -1px;
GradientLeft background-color: magenta;
@@ -172,19 +194,16 @@ const ConfigBase = Class(ModuleBase, {
Keyword color: red;
Tag color: blue;
LineNr color: orange; background: white;
Question color: green; background: white; font-weight: bold;
!StatusLine color: white !important; background: black !important
StatusLineBroken color: black !important; background: #FFa0a0 !important /* light-red */
StatusLineSecure color: black !important; background: #a0a0FF !important /* light-blue */
StatusLineExtended color: black !important; background: #a0FFa0 !important /* light-green */
StatusLine color: white; background: black;
StatusLineBroken color: black; background: #FFa0a0 /* light-red */
StatusLineSecure color: black; background: #a0a0FF /* light-blue */
StatusLineExtended color: black; background: #a0FFa0 /* light-green */
TabClose,.tab-close-button
TabIcon,.tab-icon
TabText,.tab-text
TabNumber font-weight: bold; margin: 0px; padding-right: .3ex;
TabIconNumber {
TabClose;.tab-close-button
TabIcon;.tab-icon
TabText;.tab-text
!TabNumber font-weight: bold; margin: 0px; padding-right: .3ex;
!TabIconNumber {
font-weight: bold;
color: white;
text-align: center;
@@ -195,47 +214,52 @@ const ConfigBase = Class(ModuleBase, {
URL text-decoration: none; color: green; background: inherit;
URL:hover text-decoration: underline; cursor: pointer;
FrameIndicator,,* {
background-color: red;
opacity: 0.5;
z-index: 999;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
FrameIndicator;;* {
/* This gets released into the wild, so everything is important */
background-color: red !important;
opacity: 0.5 !important;
z-index: 999999 !important;
position: fixed !important;
top: 0 !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
}
Bell border: none; background-color: black;
Hint,,* {
font-family: monospace;
font-size: 10px;
font-weight: bold;
color: white;
background-color: red;
border-color: ButtonShadow;
border-width: 0px;
border-style: solid;
padding: 0px 1px 0px 1px;
!Bell border: none; background-color: black;
Hint;;* {
/* This gets released into the wild, so everything is important */
font: bold 10px monospace !important;
background-color: red !important;
color: white !important;
border: 0px solid ButtonShadow !important;
padding: 0px 1px !important;
}
Hint::after,,* content: attr(number);
HintElem,,* background-color: yellow; color: black;
HintActive,,* background-color: #88FF00; color: black;
HintImage,,* opacity: .5;
!Hint::after;;* content: attr(number) !important;
!HintElem;;* background-color: yellow !important; color: black !important;
!HintActive;;* background-color: #88FF00 !important; color: black !important;
!HintImage;;* opacity: .5 !important;
Help font-size: 8pt; line-height: 1.4em; font-family: -moz-fixed;
!Logo
// </css>
]]></>),
helpCSS: UTF8(<><![CDATA[
// <css>
Help font-size: 8pt; line-height: 1.4em; font-family: -moz-fixed, monospace;
HelpArg color: #6A97D4;
HelpOptionalArg color: #6A97D4;
HelpBody display: block; margin: 1em auto; max-width: 100ex;
HelpBorder,*,dactyl://help/* border-color: silver; border-width: 0px; border-style: solid;
HelpCode display: block; white-space: pre; margin-left: 2em; font-family: Terminus, Fixed, monospace;
HelpBody display: block; margin: 1em auto; max-width: 100ex; padding-bottom: 1em; margin-bottom: 4em; border-bottom-width: 1px;
HelpBorder;*;dactyl://help/* border-color: silver; border-width: 0px; border-style: solid;
HelpCode display: block; white-space: pre; margin-left: 2em; font-family: monospace;
HelpDefault margin-right: 1ex; white-space: pre;
HelpDefault display: inline-block; margin-right: 1ex; white-space: pre;
HelpDescription display: block;
HelpEm,html|em,dactyl://help/* font-weight: bold; font-style: normal;
HelpDescription display: block; clear: right;
HelpDescription[short] clear: none;
HelpEm;html|em;dactyl://help/* font-weight: bold; font-style: normal;
HelpEx display: inline-block; color: #527BBD; font-weight: bold;
@@ -249,32 +273,42 @@ const ConfigBase = Class(ModuleBase, {
HelpItem display: block; margin: 1em 1em 1em 10em; clear: both;
HelpKey color: #102663;
HelpKeyword font-weight: bold; color: navy;
HelpLink,html|a,dactyl://help/* text-decoration: none;
HelpLink[href]:hover text-decoration: underline;
HelpLink;html|a;dactyl://help/* text-decoration: none !important;
HelpLink[href]:hover text-decoration: underline !important;
HelpLink[href^="mailto:"]::after content: "✉"; padding-left: .2em;
HelpLink[rel=external] {
/* Thanks, Wikipedia */
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAFVBMVEVmmcwzmcyZzP8AZswAZv////////9E6giVAAAAB3RSTlP///////8AGksDRgAAADhJREFUGFcly0ESAEAEA0Ei6/9P3sEcVB8kmrwFyni0bOeyyDpy9JTLEaOhQq7Ongf5FeMhHS/4AVnsAZubxDVmAAAAAElFTkSuQmCC) no-repeat scroll right center;
padding-right: 13px;
}
HelpList,html|ul,dactyl://help/* display: block; list-style: outside disc;
HelpOrderedList,html|ol,dactyl://help/* display: block; list-style: outside decimal;
HelpListItem,html|li,dactyl://help/* display: list-item;
HelpOrderedList;ol[level="1"],ol;dactyl://help/* display: block; list-style: outside decimal;
HelpOrderedList2;ol[level="2"],ol ol;dactyl://help/* list-style: outside upper-alpha;
HelpOrderedList3;ol[level="3"],ol ol ol;dactyl://help/* list-style: outside lower-roman;
HelpList;html|ul;dactyl://help/* display: block; list-style: outside disc;
HelpListItem;html|li;dactyl://help/* display: list-item;
HelpNote color: red; font-weight: bold;
HelpOpt color: #106326;
HelpOptInfo display: inline-block; margin-bottom: 1ex;
HelpOptInfo display: block; margin-bottom: 1ex; padding-left: 4em;
HelpParagraph,html|p,dactyl://help/* display: block; margin: 1em 0em;
HelpParagraph;html|p;dactyl://help/* display: block; margin: 1em 0em;
HelpParagraph:first-child margin-top: 0;
HelpSpec display: block; margin-left: -10em; float: left; clear: left; color: #527BBD; margin-right: 2em;
HelpParagraph:last-child margin-bottom: 0;
HelpSpec display: block; margin-left: -10em; float: left; clear: left; color: #527BBD; margin-right: 1em;
HelpString display: inline-block; color: green; font-weight: normal; vertical-align: text-top;
HelpString color: green; font-weight: normal;
HelpString::before content: '"';
HelpString::after content: '"';
HelpString[delim]::before content: attr(delim);
HelpString[delim]::after content: attr(delim);
HelpHead,html|h1,dactyl://help/* {
HelpHead;html|h1;dactyl://help/* {
display: block;
margin: 1em 0;
margin: 2em 0 1em;
padding-bottom: .2ex;
border-bottom-width: 1px;
font-size: 2em;
@@ -282,9 +316,9 @@ const ConfigBase = Class(ModuleBase, {
color: #527BBD;
clear: both;
}
HelpSubhead,html|h2,dactyl://help/* {
HelpSubhead;html|h2;dactyl://help/* {
display: block;
margin: 1em 0;
margin: 2em 0 1em;
padding-bottom: .2ex;
border-bottom-width: 1px;
font-size: 1.2em;
@@ -292,7 +326,7 @@ const ConfigBase = Class(ModuleBase, {
color: #527BBD;
clear: both;
}
HelpSubsubhead,html|h3,dactyl://help/* {
HelpSubsubhead;html|h3;dactyl://help/* {
display: block;
margin: 1em 0;
padding-bottom: .2ex;
@@ -305,12 +339,20 @@ const ConfigBase = Class(ModuleBase, {
HelpTOC
HelpTOC>ol ol margin-left: -1em;
HelpTab,html|dl,dactyl://help/* display: table; width: 100%; margin: 1em 0; border-bottom-width: 1px; border-top-width: 1px; padding: .5ex 0; table-layout: fixed;
HelpTabColumn,html|column,dactyl://help/* display: table-column;
HelpTabColumn:first-child width: 25%;
HelpTabTitle,html|dt,dactyl://help/* display: table-cell; padding: .1ex 1ex; font-weight: bold;
HelpTabDescription,html|dd,dactyl://help/* display: table-cell; padding: .1ex 1ex; border-width: 0px;
HelpTabRow,html|dl>html|tr,dactyl://help/* display: table-row;
HelpTab;html|dl;dactyl://help/* {
display: table;
width: 100%;
margin: 1em 0;
border-bottom-width: 1px;
border-top-width: 1px;
padding: .5ex 0;
table-layout: fixed;
}
HelpTabColumn;html|column;dactyl://help/* display: table-column;
HelpTabColumn:first-child width: 25%;
HelpTabTitle;html|dt;dactyl://help/* display: table-cell; padding: .1ex 1ex; font-weight: bold;
HelpTabDescription;html|dd;dactyl://help/* display: table-cell; padding: .1ex 1ex; border-width: 0px;
HelpTabRow;html|dl>html|tr;dactyl://help/* display: table-row;
HelpTag display: inline-block; color: #527BBD; margin-left: 1ex; font-size: 8pt; font-weight: bold;
HelpTags display: block; float: right; clear: right;
@@ -319,15 +361,31 @@ const ConfigBase = Class(ModuleBase, {
HelpWarning color: red; font-weight: bold;
Logo
Search,,* {
font-size: inherit;
padding: 0;
color: black;
background-color: yellow;
HelpXML color: #C5F779; background-color: #444444; font-family: Terminus, Fixed, monospace;
HelpXMLBlock { white-space: pre; color: #C5F779; background-color: #444444;
border: 1px dashed #aaaaaa;
display: block;
margin-left: 2em;
font-family: Terminus, Fixed, monospace;
}
]]>.toString()
HelpXMLAttribute color: #C5F779;
HelpXMLAttribute::after color: #E5E5E5; content: "=";
HelpXMLComment color: #444444;
HelpXMLComment::before content: "<!--";
HelpXMLComment::after content: "-->";
HelpXMLProcessing color: #C5F779;
HelpXMLProcessing::before color: #444444; content: "<?";
HelpXMLProcessing::after color: #444444; content: "?>";
HelpXMLString color: #C5F779; white-space: pre;
HelpXMLString::before content: '"';
HelpXMLString::after content: '"';
HelpXMLNamespace color: #FFF796;
HelpXMLNamespace::after color: #777777; content: ":";
HelpXMLTagStart color: #FFF796; white-space: normal; display: inline-block; text-indent: -1.5em; padding-left: 1.5em;
HelpXMLTagEnd color: #71BEBE;
HelpXMLText color: #E5E5E5;
// </css>
]]></>)
});
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -20,8 +20,10 @@
return;
}
catch (e) {
if (e !== "Error opening input stream (invalid filename?)")
if (e !== "Error opening input stream (invalid filename?)") {
dump("dactyl: Trying: " + (base + script + ".js") + ": " + e + "\n" + e.stack);
Components.utils.reportError(e);
}
}
}
try {
@@ -30,6 +32,7 @@
catch (e) {
dump("dactyl: Loading script " + script + ": " + e.result + " " + e + "\n");
dump(Error().stack + "\n");
Components.utils.reportError(e);
}
};
@@ -64,7 +67,7 @@
"template",
].forEach(modules.load);
prefix.unshift("chrome://" + modules.Config.prototype.name.toLowerCase() + "/content/");
prefix.unshift("chrome://" + modules.services.get("dactyl:").name + "/content/");
modules.Config.prototype.scripts.forEach(modules.load);
})();

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -204,14 +204,14 @@ const Dactyl = Module("dactyl", {
*
* @param {string|Object} msg The message to print.
*/
dump: function () {
dump: function dump() {
let msg = Array.map(arguments, function (msg) {
if (typeof msg == "object")
msg = util.objectToString(msg);
return msg;
}).join(", ");
msg = String.replace(msg, /\n?$/, "\n");
window.dump(msg.replace(/^./gm, ("config" in modules && config.name.toLowerCase()) + ": $&"));
window.dump(msg.replace(/^./gm, ("config" in modules && config.name) + ": $&"));
},
/**
@@ -220,7 +220,7 @@ const Dactyl = Module("dactyl", {
* @param {string} msg The trace message.
* @param {number} frames The number of frames to print.
*/
dumpStack: function (msg, frames) {
dumpStack: function dumpStack(msg, frames) {
let stack = Error().stack.replace(/(?:.*\n){2}/, "");
if (frames != null)
[stack] = stack.match(RegExp("(?:.*\n){0," + frames + "}"));
@@ -234,7 +234,7 @@ const Dactyl = Module("dactyl", {
* @param {number} flags These control the multiline message behaviour.
* See {@link CommandLine#echo}.
*/
echo: function (str, flags) {
echo: function echo(str, flags) {
commandline.echo(str, commandline.HL_NORMAL, flags);
},
@@ -246,7 +246,7 @@ const Dactyl = Module("dactyl", {
* @param {number} flags These control the multiline message behaviour.
* See {@link CommandLine#echo}.
*/
echoerr: function (str, flags) {
echoerr: function echoerr(str, flags) {
flags |= commandline.APPEND_TO_MESSAGES;
if (isinstance(str, ["Error", "Exception"]))
@@ -293,8 +293,6 @@ const Dactyl = Module("dactyl", {
* should be loaded.
*/
loadScript: function (uri, context) {
XML.ignoreWhiteSpace = false;
XML.prettyPrinting = false;
services.get("subscriptLoader").loadSubScript(uri, context);
},
@@ -395,7 +393,7 @@ const Dactyl = Module("dactyl", {
let command = commands.get(cmd);
if (command === null) {
err = "E492: Not a " + config.name.toLowerCase() + " command: " + str;
err = "E492: Not a " + config.name + " command: " + str;
dactyl.focusContent();
}
else if (command.action === null)
@@ -486,27 +484,19 @@ const Dactyl = Module("dactyl", {
* Initialize the help system.
*/
initHelp: function () {
if ("noscriptOverlay" in window) {
noscriptOverlay.safeAllow("chrome-data:", true, false);
noscriptOverlay.safeAllow("dactyl:", true, false);
}
if (!this.helpInitialized) {
if ("noscriptOverlay" in window) {
noscriptOverlay.safeAllow("chrome-data:", true, false);
noscriptOverlay.safeAllow("dactyl:", true, false);
}
if(!this.helpInitialized) {
let namespaces = [config.name.toLowerCase(), "dactyl"];
let namespaces = [config.name, "dactyl"];
services.get("dactyl:").init({});
let tagMap = services.get("dactyl:").HELP_TAGS;
let fileMap = services.get("dactyl:").FILE_MAP;
let overlayMap = services.get("dactyl:").OVERLAY_MAP;
// Left as an XPCOM instantiation so it can easilly be moved
// into XPCOM code.
function XSLTProcessor(sheet) {
let xslt = Cc["@mozilla.org/document-transformer;1?type=xslt"].createInstance(Ci.nsIXSLTProcessor);
xslt.importStylesheet(util.httpGet(sheet).responseXML);
return xslt;
}
// Find help and overlay files with the given name.
function findHelpFile(file) {
let result = [];
@@ -525,23 +515,20 @@ const Dactyl = Module("dactyl", {
}
// Find the tags in the document.
function addTags(file, doc) {
doc = XSLT.transformToDocument(doc);
for (let elem in util.evaluateXPath("//xhtml:a/@id", doc))
tagMap[elem.value] = file;
for (let elem in util.evaluateXPath("//@tag|//dactyl:tags/text()|//dactyl:tag/text()", doc))
for (let tag in array((elem.value || elem.textContent).split(/\s+/)).compact().itervalues())
tagMap[tag] = file;
}
const XSLT = XSLTProcessor("chrome://dactyl/content/help-single.xsl");
// Scrape the list of help files from all.xml
// Always process main and overlay files, since XSLTProcessor and
// Manually process main and overlay files, since XSLTProcessor and
// XMLHttpRequest don't allow access to chrome documents.
tagMap.all = "all";
let files = findHelpFile("all").map(function (doc)
[f.value for (f in util.evaluateXPath(
"//dactyl:include/@href", doc))]);
[f.value for (f in util.evaluateXPath("//dactyl:include/@href", doc))]);
// Scrape the tags from the rest of the help files.
util.Array.flatten(files).forEach(function (file) {
array.flatten(files).forEach(function (file) {
findHelpFile(file).forEach(function (doc) {
addTags(file, doc);
});
@@ -550,7 +537,6 @@ const Dactyl = Module("dactyl", {
// Process plugin help entries.
XML.ignoreWhiteSpace = false;
XML.prettyPrinting = false;
XML.prettyIndent = 4;
let body = XML();
for (let [, context] in Iterator(plugins.contexts))
@@ -558,11 +544,12 @@ const Dactyl = Module("dactyl", {
body += <h2 xmlns={NS.uri} tag={context.INFO.@name + '-plugin'}>{context.INFO.@summary}</h2> +
context.INFO;
let help = '<?xml version="1.0"?>\n' +
'<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>\n' +
'<!DOCTYPE document SYSTEM "chrome://dactyl/content/dactyl.dtd">' +
let help =
'<?xml version="1.0"?>\n' +
'<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>\n' +
'<!DOCTYPE document SYSTEM "chrome://dactyl/content/dactyl.dtd">\n' +
<document xmlns={NS}
name="plugins" title={config.name + " Plugins"}>
name="plugins" title={config.appname + " Plugins"}>
<h1 tag="using-plugins">Using Plugins</h1>
<toc start="2"/>
@@ -589,11 +576,11 @@ const Dactyl = Module("dactyl", {
function addDataEntry(file, data) // Inideal to an extreme.
addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data));
let empty = util.Array.toObject(
"area base basefont br col frame hr img input isindex link meta param"
.split(" ").map(Array.concat));
let empty = set("area base basefont br col frame hr img input isindex link meta param"
.split(" "));
let chrome = {};
let styles = {};
for (let [file,] in Iterator(services.get("dactyl:").FILE_MAP)) {
dactyl.open("dactyl://help/" + file);
dactyl.modules.events.waitForPageLoad();
@@ -612,10 +599,11 @@ const Dactyl = Module("dactyl", {
if (node instanceof HTMLHtmlElement)
data.push(" xmlns=" + XHTML.uri.quote());
for (let { name: name, value: value } in util.Array.itervalues(node.attributes)) {
for (let { name, value } in array.itervalues(node.attributes)) {
if (name == "dactyl:highlight") {
name = "class";
value = "hl-" + value;
set.add(styles, value);
}
if (name == "href") {
if (value.indexOf("dactyl://help-tag/") == 0)
@@ -651,11 +639,11 @@ const Dactyl = Module("dactyl", {
addDataEntry(file + ".xhtml", data.join(""));
}
let data = [h.selector.replace(/^\[.*?=(.*?)\]/, ".hl-$1").replace(/html\|/, "") +
"\t{" + h.value + "}"
for (h in highlight) if (/^Help|^Logo/.test(h.class))];
data = data.join("\n");
let data = [h for (h in highlight) if (set.has(styles, h.class) || /^Help/.test(h.class))]
.map(function (h)
h.selector.replace(/^\[.*?=(.*?)\]/, ".hl-$1").replace(/html\|/, "") + "\t" +
"{" + h.value + "}")
.join("\n");
addDataEntry("help.css", data.replace(/chrome:[^ ")]+\//g, ""));
let re = /(chrome:[^ ");]+\/)([^ ");]+)/g;
@@ -668,6 +656,49 @@ const Dactyl = Module("dactyl", {
zip.close();
},
/**
* Generates a help entry.
*
* @param {Command|Map|Option} obj A dactyl <b>Command</b>,
* <b>Map</b> or <b>Option</b> object
* @param {XMLList} extraHelp Extra help text beyond the description.
* @returns {string}
*/
generateHelp: function generateHelp(obj, extraHelp)
{
default xml namespace = "";
let spec = util.identity;
let tag = util.identity;
if (obj instanceof Command)
tag = spec = function (cmd) <>:{cmd}</>;
else if (obj instanceof Map && obj.count)
spec = function (map) <><oa>count</oa>{map}</>;
else if (obj instanceof Option)
tag = spec = function (opt) <>'{opt}'</>;
XML.prettyPrinting = false;
XML.ignoreWhitespace = false;
// E4X has its warts.
let br = <>
</>;
return <>
<item>
<tags>{template.map(obj.names, tag, " ")}</tags>
<spec>{spec((obj.specs || obj.names)[0])}</spec>{
!obj.type ? "" : <>
<type>{obj.type}</type>
<default>{obj.defaultValue}</default></>}
<description>{
obj.description ? br+<p>{obj.description.replace(/\.?$/, ".")}</p> : "" }{
extraHelp ? br+extraHelp : "" }{
!(extraHelp || obj.description) ? br+<p>Sorry, no help available.</p> : "" }
</description>
</item></>.toXMLString();
},
/**
* Opens the help page containing the specified <b>topic</b> if it
* exists.
@@ -692,8 +723,6 @@ const Dactyl = Module("dactyl", {
dactyl.assert(page != null, "E149: Sorry, no help for " + topic);
dactyl.open("dactyl://help/" + page, { from: "help" });
if (options.get("activate").has("all", "help"))
content.postMessage("fragmentChange", "*");
},
/**
@@ -723,15 +752,15 @@ const Dactyl = Module("dactyl", {
});
}
let dirs = io.getRuntimeDirectories("plugin");
let dirs = io.getRuntimeDirectories("plugins");
if (dirs.length == 0) {
dactyl.log("No user plugin directory found", 3);
return;
}
dactyl.echomsg('Searching for "plugin/**/*.{js,vimp}" in '
+ [dir.path.replace(/.plugin$/, "") for ([, dir] in Iterator(dirs))]
dactyl.echomsg('Searching for "plugins/**/*.{js,vimp}" in '
+ [dir.path.replace(/.plugins$/, "") for ([, dir] in Iterator(dirs))]
.join(",").quote(), 2);
dirs.forEach(function (dir) {
@@ -765,20 +794,33 @@ const Dactyl = Module("dactyl", {
if (typeof msg == "object")
msg = util.objectToString(msg, false);
services.get("console").logStringMessage(config.name.toLowerCase() + ": " + msg);
services.get("console").logStringMessage(config.name + ": " + msg);
},
/**
* Opens one or more URLs. Returns true when load was initiated, or
* false on error.
*
* @param {string|string[]} urls Either a URL string or an array of URLs.
* The array can look like this:
* ["url1", "url2", "url3", ...]
* or:
* [["url1", postdata1], ["url2", postdata2], ...]
* @param {number|Object} where If ommited, CURRENT_TAB is assumed but NEW_TAB
* is set when dactyl.forceNewTab is true.
* @param {string|Array} urls A representation of the URLs to open. May be
* either a string, which will be bassed to
* {@see Dactyl#stringToURLArray}, or an array in the same format as
* would be returned by the same.
* @param {object} params A set of parameters specifing to open the
* URLs. The following properties are recognized:
*
* • background If true, new tabs are opened in the background.
*
* • from The desgination of the opener, as appears in
* 'activate' and 'newtab' options. If present,
* the newtab option provides the default 'where'
* parameter, and the value of the 'activate'
* parameter is inverted if 'background' is true.
*
* • where One of CURRENT_TAB, NEW_TAB, or NEW_WINDOW
*
* As a deprecated special case, the where paramater may be provided
* by itself, in which case it is transformed into { where: params }.
*
* @param {boolean} force Don't prompt whether to open more than 20
* tabs.
* @returns {boolean}
@@ -787,30 +829,29 @@ const Dactyl = Module("dactyl", {
if (typeof urls == "string")
urls = dactyl.stringToURLArray(urls);
if (urls.length > 20 && !force) {
commandline.input("This will open " + urls.length + " new tabs. Would you like to continue? (yes/[no]) ",
if (urls.length > 20 && !force)
return commandline.input("This will open " + urls.length + " new tabs. Would you like to continue? (yes/[no]) ",
function (resp) {
if (resp && resp.match(/^y(es)?$/i))
dactyl.open(urls, params, true);
});
return;
}
let flags = 0;
params = params || {};
if (isarray(params))
params = { where: params };
let flags = 0;
for (let [opt, flag] in Iterator({ replace: "REPLACE_HISTORY", hide: "BYPASS_HISTORY" }))
if (params[opt])
flags |= Ci.nsIWebNavigation["LOAD_FLAGS_" + flag];
flags |= params[opt] && Ci.nsIWebNavigation["LOAD_FLAGS_" + flag];
let where = params.where || dactyl.CURRENT_TAB;
let background = ("background" in params) ? params.background : params.where == dactyl.NEW_BACKGROUND_TAB;
if ("from" in params && dactyl.has("tabs")) {
if (!('where' in params) && options.get("newtab").has("all", params.from))
let background = ("background" in params) ? params.background
: params.where == dactyl.NEW_BACKGROUND_TAB;
if (params.from && dactyl.has("tabs")) {
if (!params.where && options.get("newtab").has("all", params.from))
where = dactyl.NEW_TAB;
background = !options.get("activate").has("all", params.from);
background ^= !options.get("activate").has("all", params.from);
}
if (urls.length == 0)
@@ -829,10 +870,8 @@ const Dactyl = Module("dactyl", {
break;
case dactyl.NEW_TAB:
if (!dactyl.has("tabs")) {
open(urls, dactyl.NEW_WINDOW);
return;
}
if (!dactyl.has("tabs"))
return open(urls, dactyl.NEW_WINDOW);
options.withContext(function () {
options.setPref("browser.tabs.loadInBackground", true);
@@ -849,6 +888,9 @@ const Dactyl = Module("dactyl", {
}
}
catch(e) {}
// Unfortunately, failed page loads throw exceptions and
// cause a lot of unwanted noise. This solution means that
// any genuine errors go unreported.
}
if (dactyl.forceNewTab)
@@ -860,6 +902,7 @@ const Dactyl = Module("dactyl", {
for (let [, url] in Iterator(urls)) {
open(url, where);
where = dactyl.NEW_TAB;
background = true;
}
},
@@ -1037,12 +1080,10 @@ const Dactyl = Module("dactyl", {
services.get("observer").notifyObservers(null, "quit-application-granted", null);
// enumerate all windows and call shutdown handlers
let windows = services.get("windowMediator").getEnumerator(null);
while (windows.hasMoreElements()) {
let win = windows.getNext();
for (let win in iter(services.get("windowMediator").getEnumerator(null)))
if (("tryToClose" in win) && !win.tryToClose())
return;
}
services.get("appStartup").quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
},
@@ -1092,14 +1133,7 @@ const Dactyl = Module("dactyl", {
* @property {Window[]} Returns an array of all the host application's
* open windows.
*/
get windows() {
let windows = [];
let enumerator = services.get("windowMediator").getEnumerator("navigator:browser");
while (enumerator.hasMoreElements())
windows.push(enumerator.getNext());
return windows;
}
get windows() [win for (win in iter(services.get("windowMediator").getEnumerator("navigator:browser")))],
}, {
// initially hide all GUI elements, they are later restored unless the user
@@ -1193,26 +1227,27 @@ const Dactyl = Module("dactyl", {
this);
let class_ = dir.map(function (dir) "html|html > xul|scrollbar[orient=" + dir + "]");
if (class_.length)
styles.addSheet(true, "scrollbar", "*", class_.join(", ") + " { visibility: collapse !important; }", true);
else
styles.removeSheet(true, "scrollbar");
styles.addSheet(true, "scrollbar", "*",
class_.length ? class_.join(", ") + " { visibility: collapse !important; }" : "");
options.safeSetPref("layout.scrollbar.side", opts.indexOf("l") >= 0 ? 3 : 2,
"See 'guioptions' scrollbar flags.");
},
validator: function (opts) (opts.indexOf("l") < 0 || opts.indexOf("r") < 0)
},
tab: {
feature: "tabs",
opts: {
n: ["Tab number", highlight.selector("TabNumber")],
N: ["Tab number over icon", highlight.selector("TabIconNumber")]
},
setter: function (opts) {
const self = this;
let classes = [v[1] for ([k, v] in Iterator(this.opts)) if (opts.indexOf(k) < 0)];
let css = classes.length ? classes.join(",") + "{ display: none; }" : "";
styles.addSheet(true, "taboptions", "chrome://*", css);
tabs.tabsBound = Array.some(opts, function (k) k in self.opts);
styles.addSheet(true, "taboptions", "chrome://*",
classes.length ? classes.join(",") + "{ display: none; }" : "");
tabs.tabBinding.enabled = Array.some(opts, function (k) k in this.opts, this);
statusline.updateTabCount();
}
}
@@ -1230,13 +1265,14 @@ const Dactyl = Module("dactyl", {
"charlist", config.defaults.guioptions || "", {
setter: function (value) {
for (let [, group] in Iterator(groups))
group.setter(value);
if (!group.feature || dactyl.has(group.feature))
group.setter(value);
return value;
},
completer: function (context) {
let opts = [v.opts for ([k, v] in Iterator(groups))];
let opts = [v.opts for ([k, v] in Iterator(groups)) if (!v.feature || dactyl.has(v.feature))];
opts = opts.map(function (opt) [[k, v[0]] for ([k, v] in Iterator(opt))]);
return util.Array.flatten(opts);
return array.flatten(opts);
},
validator: function (val) Option.validateCompleter.call(this, val) &&
[v for ([k, v] in Iterator(groups))].every(function (g) !g.validator || g.validator(val))
@@ -1252,7 +1288,7 @@ const Dactyl = Module("dactyl", {
options.add(["titlestring"],
"Change the title of the window",
"string", config.defaults.titlestring || config.hostApplication,
"string", config.defaults.titlestring || config.host,
{
setter: function (value) {
let win = document.documentElement;
@@ -1330,13 +1366,13 @@ const Dactyl = Module("dactyl", {
{ argCount: "0" });
commands.add(["dia[log]"],
"Open a " + config.name + " dialog",
"Open a " + config.appname + " dialog",
function (args) {
let arg = args[0];
let dialog = args[0];
dactyl.assert(dialog in config.dialogs, "E475: Invalid argument: " + dialog);
try {
dactyl.assert(args[0] in config.dialogs, "E475: Invalid argument: " + arg);
config.dialogs[args[0]][1]();
config.dialogs[dialog][1]();
}
catch (e) {
dactyl.echoerr("Error opening " + arg.quote() + ": " + e);
@@ -1383,15 +1419,8 @@ const Dactyl = Module("dactyl", {
///////////////////////////////////////////////////////////////////////////
if (typeof AddonManager == "undefined") {
if (typeof AddonManager == "undefined")
modules.AddonManager = {
getInstallForFile: function (file, callback, mimetype) {
callback({
install: function () {
services.get("extensionManager").installItemFromFile(file, "app-profile");
}
});
},
getAddonById: function (id, callback) {
let addon = id;
if (!isobject(addon))
@@ -1438,11 +1467,19 @@ const Dactyl = Module("dactyl", {
.getItemList(Ci.nsIUpdateItem["TYPE_" + type.toUpperCase()], {})))
res.append(this.getAddonById(item));
return res;
}
},
getInstallForFile: function (file, callback, mimetype) {
callback({
install: function () {
services.get("extensionManager").installItemFromFile(file, "app-profile");
}
});
},
getInstallForURL: function (url, callback, mimetype) {
dactyl.assert(false, "Install by URL not implimented");
},
};
}
///////////////////////////////////////////////////////////////////////////
function callResult(method) {
@@ -1467,7 +1504,7 @@ const Dactyl = Module("dactyl", {
}, {
argCount: "1",
completer: function (context) {
context.filters.push(function ({ item: f }) f.isDirectory() || /\.xpi$/.test(f.leafName));
context.filters.push(function ({ item }) item.isDirectory() || /\.xpi$/.test(item.leafName));
completion.file(context);
}
});
@@ -1483,13 +1520,13 @@ const Dactyl = Module("dactyl", {
name: "exte[nable]",
description: "Enable an extension",
action: function (addon) addon.userDisabled = false,
filter: function ({ item: e }) e.userDisabled
filter: function ({ item }) item.userDisabled
},
{
name: "extd[isable]",
description: "Disable an extension",
action: function (addon) addon.userDisabled = true,
filter: function ({ item: e }) !e.userDisabled
filter: function ({ item }) !item.userDisabled
}
].forEach(function (command) {
commands.add([command.name],
@@ -1535,7 +1572,7 @@ const Dactyl = Module("dactyl", {
bang: true,
completer: function (context) {
completion.extension(context);
context.filters.push(function ({ item: e }) e.isActive && e.optionsURL);
context.filters.push(function ({ item }) item.isActive && item.optionsURL);
},
literal: 0
});
@@ -1543,6 +1580,23 @@ const Dactyl = Module("dactyl", {
commands.add(["extens[ions]", "exts"],
"List available extensions",
function (args) {
function addonExtra(e) {
let extra;
if (e.pendingOperations & AddonManager.PENDING_UNINSTALL)
extra = ["Disabled", "uninstalled"];
else if (e.pendingOperations & AddonManager.PENDING_DISABLE)
extra = ["Disabled", "disabled"];
else if (e.pendingOperations & AddonManager.PENDING_INSTALL)
extra = ["Enabled", "installed"];
else if (e.pendingOperations & AddonManager.PENDING_ENABLE)
extra = ["Enabled", "enabled"];
else if (e.pendingOperations & AddonManager.PENDING_UPGRADE)
extra = ["Enabled", "upgraded"];
if (extra)
return <>&#xa0;(<span highlight={extra[0]}>{extra[1]}</span>
&#xa0;on restart)</>;
return <></>;
}
AddonManager.getAddonsByTypes(["extension"], function (extensions) {
if (args[0])
extensions = extensions.filter(function (extension) extension.name.indexOf(args[0]) >= 0);
@@ -1555,12 +1609,7 @@ const Dactyl = Module("dactyl", {
e.version,
(e.isActive ? <span highlight="Enabled">enabled</span>
: <span highlight="Disabled">disabled</span>) +
((e.userDisabled || e.appDisabled) == !e.isActive ? XML() :
<>&#xa0;({e.userDisabled || e.appDisabled
? <span highlight="Disabled">disabled</span>
: <span highlight="Enabled">enabled</span>}
on restart)
</>),
addonExtra(e),
e.description]
for ([, e] in Iterator(extensions)))));
else if (filter)
@@ -1658,7 +1707,7 @@ const Dactyl = Module("dactyl", {
});
commands.add(["res[tart]"],
"Force " + config.name + " to restart",
"Force " + config.appname + " to restart",
function () { dactyl.restart(); },
{ argCount: "0" });
@@ -1810,7 +1859,7 @@ const Dactyl = Module("dactyl", {
dactyl.open("about:");
else
commandline.commandOutput(<>
{config.name} {dactyl.version} running on:<br/>{navigator.userAgent}
{config.appname} {dactyl.version} running on:<br/>{navigator.userAgent}
</>);
}, {
argCount: "0",
@@ -1836,11 +1885,13 @@ const Dactyl = Module("dactyl", {
context.title = ["Extension"];
context.anchored = false;
context.keys = { text: "name", description: "description", icon: "iconURL" },
context.incomplete = true;
AddonManager.getAddonsByTypes(["extension"], function (addons) {
context.incomplete = false;
context.completions = addons;
});
context.generate = function () {
context.incomplete = true;
AddonManager.getAddonsByTypes(["extension"], function (addons) {
context.incomplete = false;
context.completions = addons;
});
};
};
completion.help = function help(context, unchunked) {
@@ -1877,7 +1928,7 @@ const Dactyl = Module("dactyl", {
dactyl.log("All modules loaded", 3);
services.add("commandLineHandler", "@mozilla.org/commandlinehandler/general-startup;1?type=" + config.name.toLowerCase());
services.add("commandLineHandler", "@mozilla.org/commandlinehandler/general-startup;1?type=" + config.name);
let commandline = services.get("commandLineHandler").optionValue;
if (commandline) {
@@ -1892,7 +1943,7 @@ const Dactyl = Module("dactyl", {
dactyl.log("Command-line options: " + util.objectToString(dactyl.commandLineOptions), 3);
// first time intro message
const firstTime = "extensions." + config.name.toLowerCase() + ".firsttime";
const firstTime = "extensions." + config.name + ".firsttime";
if (options.getPref(firstTime, true)) {
util.timeout(function () {
dactyl.help();
@@ -1904,7 +1955,7 @@ const Dactyl = Module("dactyl", {
modes.reset();
// TODO: we should have some class where all this guioptions stuff fits well
Dactyl.hideGUI();
// Dactyl.hideGUI();
if (dactyl.commandLineOptions.preCommands)
dactyl.commandLineOptions.preCommands.forEach(function (cmd) {
@@ -1914,7 +1965,7 @@ const Dactyl = Module("dactyl", {
// finally, read the RC file and source plugins
// make sourcing asynchronous, otherwise commands that open new tabs won't work
util.timeout(function () {
let extensionName = config.name.toUpperCase();
let extensionName = config.idname;
let init = services.get("environment").get(extensionName + "_INIT");
let rcFile = io.getRCFile("~");
@@ -1951,13 +2002,18 @@ const Dactyl = Module("dactyl", {
// after sourcing the initialization files, this function will set
// all gui options to their default values, if they have not been
// set before by any RC file
for (let option in options) {
for (let option in values(options.needInit))
// FIXME:
// 'encoding' option should not be set at this timing.
// Probably a wrong value is set into the option,
// if current page's encoging is not UTF-8.
if (option.name != "encoding" && option.setter)
option.value = option.value;
}
try {
if (option.name != "encoding");
option.value = option.value;
}
catch (e) {
dactyl.reportError(e);
}
if (dactyl.commandLineOptions.postCommands)
dactyl.commandLineOptions.postCommands.forEach(function (cmd) {
@@ -1969,7 +2025,7 @@ const Dactyl = Module("dactyl", {
}, 0);
statusline.update();
dactyl.log(config.name + " fully initialized", 0);
dactyl.log(config.appname + " fully initialized", 0);
dactyl.initialized = true;
}
});

View File

@@ -23,11 +23,6 @@
<script type="application/x-javascript;version=1.8" src="&dactyl.content;dactyl-overlay.js"/>
<window id="&dactyl.mainWindow;">
<stringbundleset id="dactyl-stringbundles">
<stringbundle id="dactyl-charset-bundle"
src="chrome://global/locale/charsetTitles.properties"/>
</stringbundleset>
<keyset id="mainKeyset">
<key id="key_open_vimbar" key=":" oncommand="window.dactyl &and; dactyl.modules.commandline.open(':', '', dactyl.modules.modes.EX);" modifiers=""/>
<key id="key_stop" keycode="VK_ESCAPE" oncommand="window.dactyl &and; dactyl.modules.events.onEscape();"/>
@@ -43,13 +38,13 @@
<commandset id="onPentadactylFocus"
commandupdater="true"
events="focus"
oncommandupdate="if (window.dactyl &and; dactyl.modules.events != undefined) dactyl.modules.events.onFocusChange(event);"/>
oncommandupdate="if (window.dactyl &and; dactyl.modules.loaded.events) dactyl.modules.events.onFocusChange(event);"/>
<commandset id="onPentadactylSelect"
commandupdater="true"
events="select"
oncommandupdate="if (window.dactyl &and; dactyl.modules.events != undefined) dactyl.modules.events.onSelectionChange(event);"/>
oncommandupdate="if (window.dactyl &and; dactyl.modules.loaded.events) dactyl.modules.events.onSelectionChange(event);"/>
<!-- As of Firefox 3.1pre, <iframe>.height changes do not seem to have immediate effect,
<!-- As of Firefox 3.1pre, iframe.height changes do not seem to have immediate effect,
therefore we need to put them into a <vbox> for which that works just fine -->
<vbox class="dactyl-container" hidden="false" collapsed="true">
<iframe id="dactyl-multiline-output" src="chrome://dactyl/content/buffer.xhtml"
@@ -81,7 +76,6 @@
oninput="window.dactyl &and; dactyl.modules.commandline.onMultilineInputEvent(event);"
onblur="window.dactyl &and; dactyl.modules.commandline.onMultilineInputEvent(event);"/>
</vbox>
</window>
<statusbar id="status-bar" dactyl:highlight="StatusLine">
@@ -98,7 +92,6 @@
<statusbarpanel id="statusbar-display" hidden="true"/>
<statusbarpanel id="statusbar-progresspanel" hidden="true"/>
</statusbar>
</overlay>
<!-- vim: set fdm=marker sw=4 ts=4 et: -->

View File

@@ -569,7 +569,7 @@ const Editor = Module("editor", {
let list = this.getAbbreviations(filter, lhs);
if (!list.length)
dactyl.echomsg("No this._abbreviations found");
dactyl.echomsg("No abbreviations found");
else if (list.length == 1) {
let [mode, lhs, rhs] = list[0];

View File

@@ -4,7 +4,7 @@ catch (e) { __dactyl_eval_error = e; }
// IMPORTANT: The eval statement *must* remain on the first line
// in order for line numbering in any errors to remain correct.
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -1173,7 +1173,7 @@ const Events = Module("events", {
function () { document.commandDispatcher.rewindFocus(); });
mappings.add(modes.all,
["<C-z>"], "Temporarily ignore all " + config.name + " key bindings",
["<C-z>"], "Temporarily ignore all " + config.appname + " key bindings",
function () { modes.passAllKeys = true; });
mappings.add(modes.all,

View File

@@ -27,20 +27,25 @@ const RangeFinder = Module("rangefinder", {
let highlighted = this.rangeFind && this.rangeFind.highlighted;
let selections = this.rangeFind && this.rangeFind.selections;
let regex = false;
let matchCase = !(options["ignorecase"] || options["smartcase"] && !/[A-Z]/.test(str));
let linksOnly = options["linksearch"];
str = str.replace(/\\(.|$)/g, function (m, n1) {
if (n1 == "l")
linksOnly = true;
else if (n1 == "L")
linksOnly = false;
else if (n1 == "c")
if (n1 == "c")
matchCase = false;
else if (n1 == "C")
matchCase = true;
else if (n1 == "l")
linksOnly = true;
else if (n1 == "L")
linksOnly = false;
else if (n1 == "r")
regex = true;
else if (n1 == "R")
regex = false;
else
return n1;
return m;
return "";
});
@@ -49,12 +54,13 @@ const RangeFinder = Module("rangefinder", {
if (!this.rangeFind
|| this.rangeFind.window.get() != window
|| linksOnly != !!this.rangeFind.elementPath
|| regex != this.rangeFind.regex
|| matchCase != this.rangeFind.matchCase
|| !!backward != this.rangeFind.reverse) {
if (this.rangeFind)
this.rangeFind.cancel();
this.rangeFind = RangeFind(matchCase, backward, linksOnly && options["hinttags"]);
this.rangeFind = RangeFind(matchCase, backward, linksOnly && options["hinttags"], regex);
this.rangeFind.highlighted = highlighted;
this.rangeFind.selections = selections;
}
@@ -201,7 +207,7 @@ const RangeFinder = Module("rangefinder", {
},
options: function () {
options.safeSetPref("accessibility.typeaheadfind.autostart", false);
// options.safeSetPref("accessibility.typeaheadfind.autostart", false);
// The above should be sufficient, but: https://bugzilla.mozilla.org/show_bug.cgi?id=348187
options.safeSetPref("accessibility.typeaheadfind", false);
@@ -262,13 +268,14 @@ const RangeFinder = Module("rangefinder", {
* large amounts of data are concerned (e.g., for API documents).
*/
const RangeFind = Class("RangeFind", {
init: function (matchCase, backward, elementPath) {
init: function (matchCase, backward, elementPath, regex) {
this.window = Cu.getWeakReference(window);
this.elementPath = elementPath || null;
this.matchCase = Boolean(matchCase);
this.reverse = Boolean(backward);
this.finder = services.create("find");
this.finder.caseSensitive = this.matchCase;
this.matchCase = Boolean(matchCase);
this.regex = Boolean(regex);
this.ranges = this.makeFrameList(content);
@@ -281,6 +288,12 @@ const RangeFind = Class("RangeFind", {
get backward() this.finder.findBackwards,
get matchCase() this.finder.caseSensitive,
set matchCase(val) this.finder.caseSensitive = Boolean(val),
get regex() this.finder.regularExpression,
set regex(val) this.finder.regularExpression = Boolean(val),
get searchString() this.lastString,
get selectedRange() {
@@ -437,7 +450,7 @@ const RangeFind = Class("RangeFind", {
let pageStart = RangeFind.endpoint(pageRange, true);
let pageEnd = RangeFind.endpoint(pageRange, false);
for (let frame in util.Array.itervalues(win.frames)) {
for (let frame in array.itervalues(win.frames)) {
let range = doc.createRange();
if (util.computedStyle(frame.frameElement).visibility == "visible") {
range.selectNode(frame.frameElement);

View File

@@ -1,36 +0,0 @@
<!DOCTYPE document SYSTEM "chrome://dactyl/content/dactyl.dtd">
<xsl:stylesheet version="1.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:dactyl="http://vimperator.org/namespaces/liberator"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl str">
<xsl:output method="xml" indent="no"/>
<xsl:variable name="root" select="/dactyl:document"/>
<xsl:variable name="tags">
<xsl:text> </xsl:text>
<xsl:for-each select="$root//@tag|$root//dactyl:tags/text()|$root//dactyl:tag/text()">
<xsl:value-of select="concat(., ' ')"/>
</xsl:for-each>
</xsl:variable>
<xsl:template name="parse-tags">
<xsl:param name="text"/>
<div dactyl:highlight="HelpTags">
<xsl:for-each select="str:tokenize($text)">
<a id="{.}" dactyl:highlight="HelpTag"><xsl:value-of select="."/></a>
</xsl:for-each>
</div>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="$tags"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2009 by Kris Maglione <kris@vimperator.org>
// Copyright (c) 2009-2010 by Kris Maglione <kris@vimperator.org>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -13,9 +13,6 @@ function checkFragment() {
}
document.addEventListener("load", checkFragment, true);
window.addEventListener("message", function (event) {
if (event.data == "fragmentChange")
checkFragment();
}, true);
document.addEventListener("hashChange", checkFragment, true);
// vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -6,40 +6,27 @@
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:dactyl="http://vimperator.org/namespaces/liberator"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl str">
xmlns:regexp="http://exslt.org/regular-expressions"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl regexp str">
<xsl:output method="xml" indent="no"/>
<!-- Variable Definitions {{{1 -->
<xsl:variable name="doc">
<xsl:apply-templates select="/dactyl:document" mode="overlay"/>
</xsl:variable>
<xsl:variable name="root" select="exsl:node-set($doc)"/>
<xsl:variable name="tags">
<xsl:text> </xsl:text>
<xsl:for-each select="$root//@tag|$root//dactyl:tags/text()|$root//dactyl:tag/text()">
<xsl:value-of select="concat(., ' ')"/>
</xsl:for-each>
</xsl:variable>
<!-- Process Overlays {{{1 -->
<xsl:variable name="overlay" select="concat('dactyl://help-overlay/', /dactyl:document/@name)"/>
<xsl:variable name="overlaydoc" select="document($overlay)/dactyl:overlay"/>
<xsl:template name="splice-overlays">
<xsl:param name="elem"/>
<xsl:param name="tag"/>
<xsl:for-each select="$overlaydoc/*[@insertbefore=$tag]">
<xsl:for-each select="ancestor::*/dactyl:overlay/*[@insertbefore=$tag]">
<xsl:apply-templates select="." mode="overlay"/>
</xsl:for-each>
<xsl:choose>
<xsl:when test="$overlaydoc/*[@replace=$tag] and not($elem[@replace])">
<xsl:for-each select="$overlaydoc/*[@replace=$tag]">
<xsl:when test="ancestor::*/dactyl:overlay/*[@replace=$tag] and not($elem[@replace])">
<xsl:for-each select="ancestor::*/dactyl:overlay/*[@replace=$tag]">
<xsl:apply-templates select="." mode="overlay-2"/>
</xsl:for-each>
</xsl:when>
@@ -49,7 +36,7 @@
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<xsl:for-each select="$overlaydoc/*[@insertafter=$tag]">
<xsl:for-each select="ancestor::*/dactyl:overlay/*[@insertafter=$tag]">
<xsl:apply-templates select="." mode="overlay"/>
</xsl:for-each>
</xsl:template>
@@ -75,9 +62,32 @@
<!-- Process Inclusions {{{1 -->
<xsl:template name="include">
<xsl:param name="root-node" select="."/>
<xsl:param name="overlay" select="concat('dactyl://help-overlay/', $root-node/@name)"/>
<!-- Ridiculous three-pass processing is needed to deal with
- lack of dynamic variable scope in XSL 1.0. -->
<!-- Store a copy of the overlay for the current document. -->
<xsl:variable name="doc">
<dactyl:document>
<xsl:copy-of select="document($overlay)/dactyl:overlay"/>
<xsl:copy-of select="$root-node/node()"/>
</dactyl:document>
</xsl:variable>
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="concat($root-node/@name, '.xml')"/>
</xsl:call-template>
<xsl:apply-templates select="exsl:node-set($doc)/dactyl:document/node()[position() != 1]" mode="overlay"/>
</xsl:template>
<xsl:template match="dactyl:include" mode="overlay-2">
<div dactyl:highlight="HelpInclude">
<xsl:apply-templates select="document(@href)/dactyl:document/node()" mode="overlay"/>
<xsl:call-template name="include">
<xsl:with-param name="root-node" select="document(@href)/dactyl:document"/>
</xsl:call-template>
</div>
</xsl:template>
@@ -93,22 +103,39 @@
<!-- Root {{{1 -->
<xsl:template match="/">
<xsl:for-each select="$root/dactyl:document">
<html dactyl:highlight="Help">
<head>
<title><xsl:value-of select="@title"/></title>
<script type="text/javascript"
src="chrome://dactyl/content/help.js"/>
</head>
<body dactyl:highlight="HelpBody">
<div dactyl:highlight="Logo"/>
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="concat(@name, '.html')"/>
</xsl:call-template>
<xsl:apply-templates/>
</body>
</html>
</xsl:for-each>
<!-- Ridiculous three-pass processing is needed to deal with
- lack of dynamic variable scope in XSL 1.0. -->
<xsl:variable name="doc1">
<xsl:call-template name="include">
<xsl:with-param name="root-node" select="dactyl:document"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="root" select="exsl:node-set($doc1)"/>
<!-- Store a cache of all tags defined -->
<xsl:variable name="doc2">
<dactyl:document>
<xsl:attribute name="document-tags">
<xsl:text> </xsl:text>
<xsl:for-each select="$root//@tag|$root//dactyl:tags/text()|$root//dactyl:tag/text()">
<xsl:value-of select="concat(., ' ')"/>
</xsl:for-each>
</xsl:attribute>
<xsl:copy-of select="$root/node()"/>
</dactyl:document>
</xsl:variable>
<html dactyl:highlight="Help">
<head>
<title><xsl:value-of select="@title"/></title>
<script type="text/javascript" src="chrome://dactyl/content/help.js"/>
</head>
<body dactyl:highlight="HelpBody">
<xsl:apply-templates select="exsl:node-set($doc2)/dactyl:document/node()" mode="help-1"/>
</body>
</html>
</xsl:template>
<!-- Table of Contents {{{1 -->
@@ -125,14 +152,14 @@
local-name() = $tag and not(preceding::*[local-name() = $lasttag][position() = 1 and not(.=$context)])]"/>
<xsl:if test="$nodes">
<ol dactyl:highlight="HelpOrderedList">
<ol level="{$level}" dactyl:highlight="HelpOrderedList">
<xsl:for-each select="$nodes">
<li>
<a>
<xsl:if test="@tag">
<xsl:attribute name="href"><xsl:value-of select="concat('#', substring-before(concat(@tag, ' '), ' '))"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="node()" mode="help-1"/>
</a>
<xsl:call-template name="toc">
<xsl:with-param name="level" select="$level + 1"/>
@@ -144,7 +171,7 @@
</ol>
</xsl:if>
</xsl:template>
<xsl:template match="dactyl:toc" mode="pass-2">
<xsl:template match="dactyl:toc" mode="help-2">
<xsl:variable name="TOC">
<context/>
<xsl:for-each
@@ -174,36 +201,38 @@
<!-- Items {{{1 -->
<xsl:template match="dactyl:strut" mode="pass-2">
<xsl:template match="dactyl:strut" mode="help-2">
<div style="clear: both"/>
</xsl:template>
<xsl:template match="dactyl:item" mode="pass-2">
<xsl:template match="dactyl:item" mode="help-2">
<div dactyl:highlight="HelpItem">
<xsl:apply-templates select="dactyl:tags|dactyl:spec|dactyl:strut"/>
<xsl:apply-templates select="dactyl:tags|dactyl:spec|dactyl:strut" mode="help-1"/>
<xsl:if test="not(dactyl:description/@short)">
<hr style="border: 0; height: 0; margin: 0; width: 100%; float: right;"/>
<div dactyl:highlight="HelpOptInfo">
<xsl:apply-templates select="dactyl:type|dactyl:default"/>
<div style="clear: both;"/>
</div>
<xsl:if test="dactyl:type|dactyl:default">
<div dactyl:highlight="HelpOptInfo">
<xsl:apply-templates select="dactyl:type|dactyl:default" mode="help-1"/>
<div style="clear: both;"/>
</div>
</xsl:if>
</xsl:if>
<xsl:apply-templates select="dactyl:description"/>
<xsl:apply-templates select="dactyl:description" mode="help-1"/>
<div style="clear: both;"/>
</div>
</xsl:template>
<xsl:template match="dactyl:spec[preceding-sibling::dactyl:spec]" mode="pass-2">
<!--
<xsl:template match="dactyl:item/dactyl:spec[position() = last()]" mode="help-2">
<div style="clear: both;"/>
<div dactyl:highlight="HelpSpec">
<xsl:apply-templates/>
</div>
<div dactyl:highlight="HelpSpec"><xsl:apply-templates mode="help-1"/></div>
</xsl:template>
-->
<xsl:template match="dactyl:default[not(@type='plain')]" mode="pass-2">
<xsl:template match="dactyl:default[not(@type='plain')]" mode="help-2">
<xsl:variable name="type" select="preceding-sibling::dactyl:type[1] | following-sibling::dactyl:type[1]"/>
<span dactyl:highlight="HelpDefault">(default:<xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="starts-with($type, 'string') or starts-with($type, 'regex')">
<span dactyl:highlight="HelpString"><xsl:apply-templates/></span>
<span dactyl:highlight="HelpString"><xsl:apply-templates mode="help-1"/></span>
</xsl:when>
<xsl:otherwise>
<span>
@@ -214,22 +243,32 @@
<xsl:when test="$type = 'charlist'">String</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates/>
<xsl:apply-templates select="node()" mode="help-1"/>
</span>
</xsl:otherwise>
</xsl:choose>)
</span>
</xsl:choose>)</span>
</xsl:template>
<!-- Tag Definitions {{{1 -->
<xsl:template match="dactyl:tags" mode="pass-2">
<xsl:template match="dactyl:item/dactyl:tags[position() = last()]" mode="help-2">
<div style="clear: right"/>
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="dactyl:tag|@tag" mode="pass-2">
<xsl:template match="dactyl:tags" mode="help-2">
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="@tag[parent::dactyl:p]" mode="help-2">
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
<div style="clear: right"/>
</xsl:template>
<xsl:template match="dactyl:tag|@tag" mode="help-2">
<xsl:call-template name="parse-tags">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
@@ -249,31 +288,40 @@
<xsl:param name="contents" select="text()"/>
<xsl:variable name="tag" select="str:tokenize($contents, ' [!')[1]"/>
<a href="dactyl://help-tag/{$tag}" style="color: inherit;">
<xsl:if test="contains($tags, concat(' ', $tag, ' '))">
<xsl:if test="contains(ancestor::*/@document-tags, concat(' ', $tag, ' '))">
<xsl:attribute name="href">#<xsl:value-of select="$tag"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="$contents"/>
</a>
</xsl:template>
<xsl:template match="dactyl:o" mode="pass-2">
<span dactyl:highlight="HelpOption">
<xsl:template match="dactyl:o" mode="help-2">
<span dactyl:highlight="HelpOpt">
<xsl:call-template name="linkify-tag">
<xsl:with-param name="contents" select='concat("&#39;", text(), "&#39;")'/>
</xsl:call-template>
</span>
</xsl:template>
<xsl:template match="dactyl:t" mode="pass-2">
<xsl:template match="dactyl:pref" mode="help-2">
<a href="http://kb.mozillazine.org/{text()}" dactyl:highlight="HelpOpt"
>'<xsl:apply-templates select="@*|node()" mode="help-1"/>'</a>
</xsl:template>
<xsl:template match="dactyl:t" mode="help-2">
<span dactyl:highlight="HelpTopic">
<xsl:call-template name="linkify-tag"/>
</span>
</xsl:template>
<xsl:template match="dactyl:k" mode="pass-2">
<xsl:template match="dactyl:k" mode="help-2">
<span dactyl:highlight="HelpKey">
<xsl:call-template name="linkify-tag"/>
</span>
</xsl:template>
<xsl:template match="dactyl:k[@name]" mode="pass-2">
<xsl:template match="dactyl:kwd" mode="help-2">
<span dactyl:highlight="HelpKeyword">
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</span>
</xsl:template>
<xsl:template match="dactyl:k[@name]" mode="help-2">
<span dactyl:highlight="HelpKey">
<xsl:call-template name="linkify-tag">
<xsl:with-param name="contents" select="concat('&lt;', @name, '>', .)"/>
@@ -283,100 +331,117 @@
<!-- HTML-ish elements {{{1 -->
<xsl:template match="dactyl:dl" mode="pass-2">
<xsl:template match="dactyl:dl" mode="help-2">
<dl>
<column/>
<column/>
<xsl:for-each select="dactyl:dt">
<tr>
<xsl:apply-templates select="."/>
<xsl:apply-templates select="following-sibling::dactyl:dd[1]"/>
<xsl:apply-templates select="." mode="help-1"/>
<xsl:apply-templates select="following-sibling::dactyl:dd[1]" mode="help-1"/>
</tr>
</xsl:for-each>
</dl>
</xsl:template>
<xsl:template match="dactyl:link" mode="pass-2">
<a href="{@topic}"><xsl:apply-templates select="@*|node()"/></a>
<xsl:template match="dactyl:link" mode="help-2">
<a href="{@topic}">
<xsl:if test="regexp:match(@topic, '^[a-zA-Z]*:', '')
and not(starts-with(@topic, 'mailto:'))">
<xsl:attribute name="rel">external</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</a>
</xsl:template>
<xsl:template match="dactyl:hl" mode="help-2">
<span dactyl:highlight="{@key}"><xsl:apply-templates select="@*|node()" mode="help-1"/></span>
</xsl:template>
<xsl:template match="dactyl:h" mode="help-2">
<em><xsl:apply-templates select="@*|node()" mode="help-1"/></em>
</xsl:template>
<xsl:template match="dactyl:em | dactyl:tt | dactyl:p |
dactyl:dt | dactyl:dd |
dactyl:ol | dactyl:ul | dactyl:li |
dactyl:h1 | dactyl:h2 | dactyl:h3"
mode="pass-2">
<xsl:element name="html:{local-name()}">
<xsl:apply-templates select="@*|node()"/>
mode="help-2">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</xsl:element>
</xsl:template>
<xsl:template match="dactyl:code" mode="pass-2">
<pre dactyl:highlight="HelpCode"><xsl:apply-templates select="@*|node()"/></pre>
<xsl:template match="dactyl:code" mode="help-2">
<pre dactyl:highlight="HelpCode"><xsl:apply-templates select="@*|node()" mode="help-1"/></pre>
</xsl:template>
<!-- Help elements {{{1 -->
<xsl:template match="dactyl:a" mode="pass-2">
<span dactyl:highlight="HelpArg">{<xsl:apply-templates select="@*|node()"/>}</span>
<xsl:template match="dactyl:a" mode="help-2">
<span dactyl:highlight="HelpArg">{<xsl:apply-templates select="@*|node()" mode="help-1"/>}</span>
</xsl:template>
<xsl:template match="dactyl:oa" mode="pass-2">
<span dactyl:highlight="HelpOptionalArg">[<xsl:apply-templates select="@*|node()"/>]</span>
<xsl:template match="dactyl:oa" mode="help-2">
<span dactyl:highlight="HelpOptionalArg">[<xsl:apply-templates select="@*|node()" mode="help-1"/>]</span>
</xsl:template>
<xsl:template match="dactyl:note" mode="pass-2">
<xsl:template match="dactyl:note" mode="help-2">
<p style="clear: both;">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="@*" mode="help-1"/>
<div style="clear: both;"/>
<span dactyl:highlight="HelpNote">Note:</span>
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="node()" mode="help-1"/>
</p>
</xsl:template>
<xsl:template match="dactyl:warning" mode="pass-2">
<xsl:template match="dactyl:warning" mode="help-2">
<p style="clear: both;">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="@*" mode="help-1"/>
<div style="clear: both;"/>
<span dactyl:highlight="HelpWarning">Warning:</span>
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="node()" mode="help-1"/>
</p>
</xsl:template>
<xsl:template match="dactyl:default" mode="pass-2">
<span dactyl:highlight="HelpDefault">
(default:<xsl:text> </xsl:text><xsl:apply-templates select="@*|node()"/>)
</span>
<xsl:template match="dactyl:default" mode="help-2">
<span dactyl:highlight="HelpDefault">(default:<xsl:text> </xsl:text><xsl:apply-templates select="@*|node()" mode="help-1"/>)</span>
</xsl:template>
<!-- HTML-ify other elements {{{1 -->
<xsl:template match="dactyl:ex" mode="pass-2">
<xsl:template match="dactyl:ex" mode="help-2">
<span dactyl:highlight="HelpEx">
<xsl:variable name="tag" select="str:tokenize(text(), ' [!')[1]"/>
<a href="dactyl://help-tag/{$tag}" style="color: inherit;">
<xsl:if test="contains($tags, concat(' ', $tag, ' '))">
<xsl:if test="contains(ancestor::*/@document-tags, concat(' ', $tag, ' '))">
<xsl:attribute name="href">#<xsl:value-of select="$tag"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
<xsl:apply-templates mode="help-1"/>
</a>
</span>
</xsl:template>
<xsl:template match="dactyl:description | dactyl:example | dactyl:spec" mode="pass-2">
<xsl:template match="dactyl:description | dactyl:example | dactyl:spec" mode="help-2">
<div>
<xsl:if test="self::dactyl:description"><xsl:attribute name="dactyl:highlight">HelpDescription</xsl:attribute></xsl:if>
<xsl:if test="self::dactyl:example"><xsl:attribute name="dactyl:highlight">HelpExample</xsl:attribute></xsl:if>
<xsl:if test="self::dactyl:spec"><xsl:attribute name="dactyl:highlight">HelpSpec</xsl:attribute></xsl:if>
<xsl:apply-templates select="@*|node()"/>
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</div>
</xsl:template>
<xsl:template match="dactyl:str | dactyl:t | dactyl:type" mode="pass-2">
<xsl:template match="dactyl:str | dactyl:type" mode="help-2">
<span>
<xsl:if test="self::dactyl:str"><xsl:attribute name="dactyl:highlight">HelpString</xsl:attribute></xsl:if>
<xsl:if test="self::dactyl:t"><xsl:attribute name="dactyl:highlight">HelpTopic</xsl:attribute></xsl:if>
<xsl:if test="self::dactyl:type"><xsl:attribute name="dactyl:highlight">HelpType</xsl:attribute></xsl:if>
<xsl:apply-templates select="@*|node()"/>
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</span>
</xsl:template>
<xsl:template match="dactyl:xml-block" mode="help-2">
<div dactyl:highlight="HelpXMLBlock">
<xsl:call-template name="xml-highlight"/>
</div>
</xsl:template>
<xsl:template match="dactyl:xml-highlight" mode="help-2">
<xsl:call-template name="xml-highlight"/>
</xsl:template>
<!-- Plugins {{{1 -->
@@ -400,59 +465,133 @@
</span>
</div>
</xsl:template>
<xsl:template match="dactyl:author[@email]" mode="pass-2">
<xsl:template match="dactyl:author[@email]" mode="help-2">
<xsl:call-template name="info">
<xsl:with-param name="label" select="'Author'"/>
<xsl:with-param name="extra">
<xsl:text> </xsl:text><a href="mailto:{@email}"></a>
<xsl:text> </xsl:text><a href="mailto:{@email}"></a>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="dactyl:author" mode="pass-2">
<xsl:template match="dactyl:author" mode="help-2">
<xsl:call-template name="info">
<xsl:with-param name="label" select="'Author'"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="dactyl:license" mode="pass-2">
<xsl:template match="dactyl:license" mode="help-2">
<xsl:call-template name="info">
<xsl:with-param name="label" select="'License'"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="dactyl:plugin" mode="pass-2">
<xsl:template match="dactyl:plugin" mode="help-2">
<xsl:call-template name="info">
<xsl:with-param name="label" select="'Plugin'"/>
<xsl:with-param name="nodes">
<span><xsl:value-of select="@name"/></span>
</xsl:with-param>
</xsl:call-template>
<xsl:apply-templates/>
<xsl:if test="@version">
<xsl:call-template name="info">
<xsl:with-param name="label" select="'Version'"/>
<xsl:with-param name="link" select="''"/>
<xsl:with-param name="nodes">
<span><xsl:value-of select="@version"/></span>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates mode="help-1"/>
</xsl:template>
<!-- Special Element Templates {{{1 -->
<xsl:template match="dactyl:logo">
<xsl:template match="dactyl:logo" mode="help-1">
<span dactyl:highlight="Logo"/>
</xsl:template>
<xsl:template match="dactyl:pan[dactyl:handle]">
<form style="text-align:center" xmlns="http://www.w3.org/1999/xhtml"
action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="image" src="chrome://dactyl/content/x-click-but21.png" border="0" name="submit" alt="Donate with PayPal"/>
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHPwYJKoZIhvcNAQcEoIIHMDCCBywCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYAUOJADCwiik68MpIUKcMAtNfs4Cx6RY7604ZujgKj7WVaiELWyhUUDSaq8+iLYaNkRUq+dDld96KwhfodqP3MEmIzpQ/qKvh5+4JzTWSBU5G1lHzc4NJQw6TpXKloPxxXhuGKzZ84/asKZIZpLfkP5i8VtqVFecu7qYc0q1U2KoDELMAkGBSsOAwIaBQAwgbwGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIWR7nX4WwgcqAgZgO41g/NtgfBwI14LlJx3p5Hc4nHsQD2wyu5l4BMndkc3mc0uRTXvzutcfPBxYC4aGV5UDn6c+XPzsne+OAdSs4/0a2DJe85SBDOlVyOekz3rRhy5+6XKpKQ7qfiMpKROladi4opfMac/aDUPhGeVsY0jtQCtelIE199iaVKhlbiDvfE7nzV5dLU4d3VZwSDuWBIrIIi9GMtKCCA4cwggODMIIC7KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTAeFw0wNDAyMTMxMDEzMTVaFw0zNTAyMTMxMDEzMTVaMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwUdO3fxEzEtcnI7ZKZL412XvZPugoni7i7D7prCe0AtaHTc97CYgm7NsAtJyxNLixmhLV8pyIEaiHXWAh8fPKW+R017+EmXrr9EaquPmsVvTywAAE1PMNOKqo2kl4Gxiz9zZqIajOm1fZGWcGS0f5JQ2kBqNbvbg2/Za+GJ/qwUCAwEAAaOB7jCB6zAdBgNVHQ4EFgQUlp98u8ZvF71ZP1LXChvsENZklGswgbsGA1UdIwSBszCBsIAUlp98u8ZvF71ZP1LXChvsENZklGuhgZSkgZEwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAgV86VpqAWuXvX6Oro4qJ1tYVIT5DgWpE692Ag422H7yRIr/9j/iKG4Thia/Oflx4TdL+IFJBAyPK9v6zZNZtBgPBynXb048hsP16l2vi0k5Q2JKiPDsEfBhGI+HnxLXEaUWAcVfCsQFvd2A1sxRr67ip5y2wwBelUecP3AjJ+YcxggGaMIIBlgIBATCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTA4MDYwNTE0NDk1OFowIwYJKoZIhvcNAQkEMRYEFBpY8FafLq7i3V0czWS9TbR/RjyQMA0GCSqGSIb3DQEBAQUABIGAPvYR9EC2ynooWAvX0iw9aZYTrpX2XrTl6lYkZaLrhM1zKn4RuaiL33sPtq0o0uSKm98gQHzh4P6wmzES0jzHucZjCU4VlpW0fC+/pJxswbW7Qux+ObsNx3f45OcvprqMMZyJiEOULcNhxkm9pCeXQMUGwlHoRRtAxYK2T8L/rQQ=-----END PKCS7-----
"/>
</form>
</xsl:template>
<!-- Process Tree {{{1 -->
<xsl:template match="@*|node()" mode="pass-2">
<xsl:template match="@*|node()" mode="help-2">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:apply-templates select="@*|node()" mode="help-1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:apply-templates select="." mode="pass-2"/>
<xsl:template match="@*|node()" mode="help-1">
<xsl:apply-templates select="." mode="help-2"/>
</xsl:template>
<!-- XML Highlighting (xsl:import doesn't work in Firefox 3.x) {{{1 -->
<xsl:template name="xml-highlight">
<div dactyl:highlight="HelpXML">
<xsl:apply-templates mode="xml-highlight"/>
</div>
</xsl:template>
<xsl:template name="xml-namespace">
<xsl:param name="node" select="."/>
<xsl:if test="name($node) != local-name($node)">
<span dactyl:highlight="HelpXMLNamespace">
<xsl:value-of select="substring-before(name($node), ':')"/>
</span>
</xsl:if>
<xsl:value-of select="local-name($node)"/>
</xsl:template>
<xsl:template match="*" mode="xml-highlight">
<span dactyl:highlight="HelpXMLTagStart">
<xsl:text>&lt;</xsl:text>
<xsl:call-template name="xml-namespace"/>
<xsl:apply-templates select="@*" mode="xml-highlight"/>
<xsl:text>/></xsl:text>
</span>
</xsl:template>
<xsl:template match="*[node()]" mode="xml-highlight">
<span dactyl:highlight="HelpXMLTagStart">
<xsl:text>&lt;</xsl:text>
<xsl:call-template name="xml-namespace"/>
<xsl:apply-templates select="@*" mode="xml-highlight"/>
<xsl:text>></xsl:text>
</span>
<xsl:apply-templates select="node()" mode="xml-highlight"/>
<span dactyl:highlight="HelpXMLTagEnd">
<xsl:text>&lt;/</xsl:text>
<xsl:call-template name="xml-namespace"/>
<xsl:text>></xsl:text>
</span>
</xsl:template>
<xsl:template match="dactyl:escape | dactyl:escape[node()]" mode="xml-highlight">
<span dactyl:highlight="HelpXMLText">
<xsl:apply-templates mode="help-1"/>
</span>
</xsl:template>
<xsl:template match="@*" mode="xml-highlight">
<xsl:text> </xsl:text>
<span dactyl:highlight="HelpXMLAttribute">
<xsl:call-template name="xml-namespace"/>
</span>
<span dactyl:highlight="HelpXMLString">
<xsl:value-of select="regexp:replace(., '&quot;', 'g', '&amp;quot;')"/>
</span>
</xsl:template>
<xsl:template match="comment()" mode="xml-highlight">
<span dactyl:highlight="HelpXMLComment">
<xsl:value-of select="."/>
</span>
</xsl:template>
<xsl:template match="processing-instruction()" mode="xml-highlight">
<span dactyl:highlight="HelpXMLProcessing">
<xsl:value-of select="."/>
</span>
</xsl:template>
<xsl:template match="text()" mode="xml-highlight">
<span dactyl:highlight="HelpXMLText">
<xsl:value-of select="regexp:replace(., '&lt;', 'g', '&amp;lt;')"/>
</span>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -36,26 +36,26 @@ const Hints = Module("hints", {
function images() util.makeXPath(["img"]);
this._hintModes = {
";": Mode("Focus hint", function (elem) buffer.focusElement(elem), extended),
"?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended),
";": Mode("Focus hint", function (elem) buffer.focusElement(elem), extended),
"?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended),
s: Mode("Save hint", function (elem) buffer.saveLink(elem, true)),
a: Mode("Save hint with prompt", function (elem) buffer.saveLink(elem, false)),
f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () ["body"]),
o: Mode("Follow hint", function (elem) buffer.followLink(elem, dactyl.CURRENT_TAB)),
t: Mode("Follow hint in a new tab", function (elem) buffer.followLink(elem, dactyl.NEW_TAB)),
b: Mode("Follow hint in a background tab", function (elem) buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB)),
w: Mode("Follow hint in a new window", function (elem) buffer.followLink(elem, dactyl.NEW_WINDOW), extended),
w: Mode("Follow hint in a new window", function (elem) buffer.followLink(elem, dactyl.NEW_WINDOW), extended),
F: Mode("Open multiple hints in tabs", function (elem) { buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB); hints.show("F") }),
O: Mode("Generate an ':open URL' using hint", function (elem, loc) commandline.open(":", "open " + loc, modes.EX)),
T: Mode("Generate a ':tabopen URL' using hint", function (elem, loc) commandline.open(":", "tabopen " + loc, modes.EX)),
W: Mode("Generate a ':winopen URL' using hint", function (elem, loc) commandline.open(":", "winopen " + loc, modes.EX)),
v: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, false), extended),
V: Mode("View hint source in external editor", function (elem, loc) buffer.viewSource(loc, true), extended),
v: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, false), extended),
V: Mode("View hint source in external editor", function (elem, loc) buffer.viewSource(loc, true), extended),
y: Mode("Yank hint location", function (elem, loc) dactyl.clipboardWrite(loc, true)),
Y: Mode("Yank hint description", function (elem) dactyl.clipboardWrite(elem.textContent || "", true), extended),
c: Mode("Open context menu", function (elem) buffer.openContextMenu(elem), extended),
i: Mode("Show image", function (elem) dactyl.open(elem.src), images),
I: Mode("Show image in a new tab", function (elem) dactyl.open(elem.src, dactyl.NEW_TAB), images)
c: Mode("Open context menu", function (elem) buffer.openContextMenu(elem), extended),
i: Mode("Show image", function (elem) dactyl.open(elem.src), images),
I: Mode("Show image in a new tab", function (elem) dactyl.open(elem.src, dactyl.NEW_TAB), images)
};
},
@@ -351,7 +351,7 @@ const Hints = Module("hints", {
if (hint.text == "" && hint.elem.firstChild && hint.elem.firstChild instanceof HTMLImageElement) {
if (!hint.imgSpan) {
var rect = hint.elem.firstChild.getBoundingClientRect();
let rect = hint.elem.firstChild.getBoundingClientRect();
if (!rect)
continue;
@@ -1054,7 +1054,7 @@ const Hints = Module("hints", {
["wordstartswith", "The typed characters are split on whitespace. The resulting groups must all match the beginings of words, in order."],
["firstletters", "Behaves like wordstartswith, but all groups much match a sequence of words."],
["custom", "Delegate to a custom function: dactyl.plugins.customHintMatcher(hintString)"],
["transliterated", "When true, special latin characters are translated to their ascii equivalent (e.g., \u00e9 -> e)"]
["transliterated", UTF8("When true, special latin characters are translated to their ascii equivalent (e.g., é -> e)")]
]
});

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -45,11 +45,12 @@ const History = Module("history", {
let sh = window.getWebNavigation().sessionHistory;
let obj = [];
obj.index = sh.index;
obj.__iterator__ = function () util.Array.iteritems(this);
obj.__iterator__ = function () array.iteritems(this);
for (let i in util.range(0, sh.count)) {
obj[i] = { index: i, __proto__: sh.getEntryAtIndex(i, false) };
util.memoize(obj[i], "icon",
function (obj) services.get("favicon").getFaviconImageForPage(obj.URI).spec);
obj[i] = update(Object.create(sh.getEntryAtIndex(i, false)),
{ index: i });
memoize(obj[i], "icon",
function () services.get("favicon").getFaviconImageForPage(this.URI).spec);
}
return obj;
},
@@ -64,7 +65,10 @@ const History = Module("history", {
dactyl.beep();
else {
let index = Math.constrain(current + steps, start, end);
window.getWebNavigation().gotoIndex(index);
try {
window.getWebNavigation().gotoIndex(index);
}
catch (e) {} // We get NS_ERROR_FILE_NOT_FOUND if files in history don't exist
}
},

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k@gmail.com>
// Some code based on Venkman
//
// This work is licensed for reuse under an MIT license. Details are
@@ -19,16 +19,15 @@ function Script(file) {
}
self = { __proto__: plugins };
plugins.contexts[file.path] = self;
plugins[file.path] = self;
self.NAME = file.leafName.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase());
self.PATH = file.path;
self.__context__ = self;
self.__proto__ = plugins;
// This belongs elsewhere
for (let [, dir] in Iterator(io.getRuntimeDirectories("plugin"))) {
if (dir.contains(file, false))
plugins[self.NAME] = self;
}
if (io.getRuntimeDirectories("plugins").some(
function (dir) dir.contains(file, false)))
plugins[self.NAME] = self;
return self;
}
@@ -54,7 +53,8 @@ const IO = Module("io", {
let file = download.targetFile.path;
let size = download.size;
dactyl.echomsg("Download of " + title + " to " + file + " finished", 1, commandline.ACTIVE_WINDOW);
dactyl.echomsg({ domains: [util.getHost(url)], message: "Download of " + title + " to " + file + " finished" },
1, commandline.ACTIVE_WINDOW);
autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size });
}
},
@@ -179,8 +179,8 @@ const IO = Module("io", {
getRCFile: function (dir, always) {
dir = dir || "~";
let rcFile1 = File.joinPaths(dir, "." + config.name.toLowerCase() + "rc");
let rcFile2 = File.joinPaths(dir, "_" + config.name.toLowerCase() + "rc");
let rcFile1 = File.joinPaths(dir, "." + config.name + "rc");
let rcFile2 = File.joinPaths(dir, "_" + config.name + "rc");
if (dactyl.has("Win32"))
[rcFile1, rcFile2] = [rcFile2, rcFile1];
@@ -346,6 +346,8 @@ lookup:
dactyl.helpInitialized = false;
}
catch (e) {
if (isstring(e))
e = { message: e };
let err = new Error();
for (let [k, v] in Iterator(e))
err[k] = v;
@@ -508,10 +510,10 @@ lookup:
* variable.
*/
get runtimePath() {
const rtpvar = config.name.toUpperCase() + "_RUNTIME";
const rtpvar = config.idname + "_RUNTIME";
let rtp = services.get("environment").get(rtpvar);
if (!rtp) {
rtp = "~/" + (dactyl.has("Win32") ? "" : ".") + config.name.toLowerCase();
rtp = "~/" + (dactyl.has("Win32") ? "" : ".") + config.name;
services.get("environment").set(rtpvar, rtp);
}
return rtp;
@@ -582,7 +584,7 @@ lookup:
{ argCount: "0" });
// "mkv[imperatorrc]" or "mkm[uttatorrc]"
commands.add([config.name.toLowerCase().replace(/(.)(.*)/, "mk$1[$2rc]")],
commands.add([config.name.replace(/(.)(.*)/, "mk$1[$2rc]")],
"Write current key mappings and changed options to the config file",
function (args) {
dactyl.assert(args.length <= 1, "E172: Only one file name allowed");
@@ -595,7 +597,7 @@ lookup:
// TODO: Use a set/specifiable list here:
let lines = [cmd.serialize().map(commands.commandToString) for (cmd in commands) if (cmd.serialize)];
lines = util.Array.flatten(lines);
lines = array.flatten(lines);
// source a user .pentadactylrc file
lines.unshift('"' + dactyl.version + "\n");
@@ -608,7 +610,7 @@ lookup:
arguments: [filename + ".local"]
}));
lines.push("\n\" vim: set ft=" + config.name.toLowerCase() + ":");
lines.push("\n\" vim: set ft=" + config.name + ":");
try {
file.write(lines.join("\n"));
@@ -669,9 +671,13 @@ lookup:
// NOTE: Vim doesn't replace ! preceded by 2 or more backslashes and documents it - desirable?
// pass through a raw bang when escaped or substitute the last command
arg = arg.replace(/(\\)*!/g,
function (m) /^\\(\\\\)*!$/.test(m) ? m.replace("\\!", "!") : m.replace("!", io._lastRunCommand)
);
// This is an asinine and irritating feature when we have searchable
// command-line history. --Kris
if (options["banghist"])
arg = arg.replace(/(\\)*!/g,
function (m) /^\\(\\\\)*!$/.test(m) ? m.replace("\\!", "!") : m.replace("!", io._lastRunCommand)
);
io._lastRunCommand = arg;
@@ -691,19 +697,20 @@ lookup:
completion: function () {
completion.charset = function (context) {
context.anchored = false;
context.generate = function () {
let names = util.Array(
"more1 more2 more3 more4 more5 unicode".split(" ").map(function (key)
options.getPref("intl.charsetmenu.browser." + key).split(', '))
).flatten().uniq();
let bundle = document.getElementById("dactyl-charset-bundle");
return names.map(function (name) [name, bundle.getString(name.toLowerCase() + ".title")]);
let bundle = services.get("stringBundle").createBundle(
"chrome://global/locale/charsetTitles.properties");
context.keys = {
text: util.identity,
description: function (charset) bundle.GetStringFromName(charset.toLowerCase() + ".title")
};
context.generate = function () array("more1 more2 more3 more4 more5 unicode".split(" "))
.map(function (key) options.getPref("intl.charsetmenu.browser." + key).split(', '))
.flatten().uniq().array;
};
completion.directory = function directory(context, full) {
this.file(context, full);
context.filters.push(function ({ item: f }) f.isDirectory());
context.filters.push(function ({ item }) item.isDirectory());
};
completion.environment = function environment(context) {
@@ -737,7 +744,7 @@ lookup:
if (options["wildignore"]) {
let wig = options.get("wildignore");
context.filters.push(function ({item: f}) f.isDirectory() || !wig.getKey(this.name));
context.filters.push(function ({ item }) item.isDirectory() || !wig.getKey(this.name));
}
// context.background = true;
@@ -765,7 +772,7 @@ lookup:
}
}
return util.Array.flatten(commands);
return array.flatten(commands);
};
};
@@ -795,6 +802,10 @@ lookup:
shellcmdflag = "-c";
}
options.add(["banghist", "bh"],
"Replace occurances of ! with the previous command when executing external commands",
"banghist", true);
options.add(["fileencoding", "fenc"],
"Sets the character encoding of read and written files",
"string", "UTF-8", {

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -41,11 +41,13 @@ const JavaScript = Module("javascript", {
let seen = {};
for (let key in properties(obj, !toplevel)) {
set.add(seen, key);
yield [key, this.getKey(obj, key)];
yield key;
}
// Properties aren't visible in an XPCNativeWrapper until
// they're accessed.
for (let key in properties(this.getKey(obj, "wrappedJSObject"), !toplevel))
if (!set.has(seen, key))
yield [key, this.getKey(obj, key)];
if (key in obj && !set.has(seen, key))
yield key;
},
objectKeys: function objectKeys(obj, toplevel) {
@@ -110,15 +112,14 @@ const JavaScript = Module("javascript", {
},
_pop: function pop(arg) {
if (this._top.char != arg) {
this.context.highlight(this._top.offset, this._i - this._top.offset, "SPELLCHECK");
this.context.highlight(this._top.offset, 1, "FIND");
throw new Error("Invalid JS");
}
if (this._i == this.context.caret - 1)
this.context.highlight(this._top.offset, 1, "FIND");
if (this._top.char != arg) {
this.context.highlight(this._top.offset, this._i - this._top.offset, "SPELLCHECK");
throw Error("Invalid JS");
}
// The closing character of this stack frame will have pushed a new
// statement, leaving us with an empty statement. This doesn't matter,
// now, as we simply throw away the frame when we pop it, but it may later.
@@ -139,11 +140,13 @@ const JavaScript = Module("javascript", {
// Reuse the old stack.
if (this._str && filter.substr(0, this._str.length) == this._str) {
this.context.highlight(0, 0, "FIND");
this._i = this._str.length;
if (this.popStatement)
this._top.statements.pop();
}
else {
this.context.highlight();
this._stack = [];
this._functions = [];
this._push("#root");
@@ -239,7 +242,7 @@ const JavaScript = Module("javascript", {
_getObj: function (frame, stop) {
let statement = this._get(frame, 0, "statements") || 0; // Current statement.
let prev = statement;
let obj;
let obj = null;
let cacheKey;
for (let [, dot] in Iterator(this._get(frame).dots.concat(stop))) {
if (dot < statement)
@@ -285,19 +288,19 @@ const JavaScript = Module("javascript", {
return [dot + 1 + space.length, obj, key];
},
_fill: function (context, obj, name, compl, anchored, key, last, offset) {
context.title = [name];
context.anchored = anchored;
context.filter = key;
_fill: function (context, args) {
context.title = [args.name];
context.anchored = args.anchored;
context.filter = args.filter;
context.itemCache = context.parent.itemCache;
context.key = name + last;
context.key = args.name + args.last;
if (last != null)
context.quote = [last, function (text) util.escapeString(text.substr(offset), ""), last];
if (args.last != null)
context.quote = [args.last, function (text) util.escapeString(text.substr(args.offset), ""), args.last];
else // We're not looking for a quoted string, so filter out anything that's not a valid identifier
context.filters.push(function (item) /^[a-zA-Z_$][\w$]*$/.test(item.text));
compl.call(self, context, obj);
args.completer.call(self, context, args.obj);
},
_complete: function (objects, key, compl, string, last) {
@@ -309,7 +312,10 @@ const JavaScript = Module("javascript", {
let orig = compl;
if (!compl) {
compl = function (context, obj, recurse) {
context.process = [null, function highlight(item, v) template.highlight(typeof v == "xml" ? new String(v.toXMLString()) : v, true)];
context.process[1] = function highlight(item, v)
template.highlight(typeof v == "xml" ? new String(v.toXMLString()) : v, true);
// Sort in a logical fashion for object keys:
// Numbers are sorted as numbers, rather than strings, and appear first.
// Constants are unsorted, and appear before other non-null strings.
@@ -321,14 +327,15 @@ const JavaScript = Module("javascript", {
return a.key - b.key;
return isnan(b.key) - isnan(a.key) || compare(a, b);
};
context.keys = { text: 0, description: 1,
context.keys = {
text: util.identity,
description: function (item) self.getKey(obj, item),
key: function (item) {
let key = item[0];
if (!isNaN(key))
return parseInt(key);
else if (/^[A-Z_][A-Z0-9_]*$/.test(key))
if (/^[A-Z_][A-Z0-9_]*$/.test(key))
return ""
return key;
return item;
}
};
@@ -339,37 +346,51 @@ const JavaScript = Module("javascript", {
context.generate = function () self.objectKeys(obj, !recurse);
};
}
let args = {
completer: compl,
anchored: true,
filter: key + (string || ""),
last: last,
offset: key.length
};
// TODO: Make this a generic completion helper function.
let filter = key + (string || "");
for (let [, obj] in Iterator(objects)) {
for (let [, obj] in Iterator(objects))
this.context.fork(obj[1], this._top.offset, this, this._fill,
obj[0], obj[1], compl,
true, filter, last, key.length);
}
update(args, {
obj: obj[0],
name: obj[1],
}));
if (orig)
return;
for (let [, obj] in Iterator(objects)) {
let name = obj[1] + " (prototypes)";
this.context.fork(name, this._top.offset, this, this._fill,
obj[0], name, function (a, b) compl(a, b, true),
true, filter, last, key.length);
}
for (let [, obj] in Iterator(objects))
this.context.fork(obj[1] + "/prototypes", this._top.offset, this, this._fill,
update(args, {
obj: obj[0],
name: obj[1] + " (prototypes)",
completer: function (a, b) compl(a, b, true)
}));
for (let [, obj] in Iterator(objects)) {
let name = obj[1] + " (substrings)";
this.context.fork(name, this._top.offset, this, this._fill,
obj[0], name, compl,
false, filter, last, key.length);
}
for (let [, obj] in Iterator(objects))
this.context.fork(obj[1] + "/substrings", this._top.offset, this, this._fill,
update(args, {
obj: obj[0],
name: obj[1] + " (substrings)",
anchored: false,
completer: compl
}));
for (let [, obj] in Iterator(objects)) {
let name = obj[1] + " (prototype substrings)";
this.context.fork(name, this._top.offset, this, this._fill,
obj[0], name, function (a, b) compl(a, b, true),
false, filter, last, key.length);
}
for (let [, obj] in Iterator(objects))
this.context.fork(obj[1] + "/prototypes/substrings", this._top.offset, this, this._fill,
update(args, {
obj: obj[0],
name: obj[1] + " (prototype substrings)",
anchored: false,
completer: function (a, b) compl(a, b, true)
}));
},
_getKey: function () {
@@ -398,7 +419,7 @@ const JavaScript = Module("javascript", {
}
this.context.getCache("evalled", Object);
this.context.getCache("evalContext", function () ({ __proto__: userContext }));;
this.context.getCache("evalContext", function () ({ __proto__: userContext }));
// Okay, have parse stack. Figure out what we're completing.
@@ -478,7 +499,7 @@ const JavaScript = Module("javascript", {
for (let [i, idx] in Iterator(this._get(-2).comma)) {
let arg = this._str.substring(prev + 1, idx);
prev = idx;
util.memoize(args, i, function () self.evalled(arg));
memoize(args, i, function () self.evalled(arg));
}
let key = this._getKey();
args.push(key + string);

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -383,7 +383,7 @@ const Mappings = Module("mappings", {
let [lhs, rhs] = args;
if (!rhs) // list the mapping
mappings.list(modes, this._expandLeader(lhs));
mappings.list(modes, mappings._expandLeader(lhs));
else {
// this matches Vim's behaviour
if (/^<Nop>$/i.test(rhs))
@@ -467,7 +467,7 @@ const Mappings = Module("mappings", {
addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
for (let mode in modes.mainModes)
if (mode.char && !commands.get(mode.char + "map"))
if (mode.char && !commands.get(mode.char + "map", true))
addMapCommands(mode.char,
[m.mask for (m in modes.mainModes) if (m.char == mode.char)],
[mode.disp.toLowerCase()]);
@@ -489,8 +489,7 @@ const Mappings = Module("mappings", {
null,
function (context, obj, args) {
let mode = args[0];
return util.Array.flatten(
[
return array.flatten([
[[name, map.description] for ([i, name] in Iterator(map.names))]
for ([i, map] in Iterator(mappings._user[mode].concat(mappings._main[mode])))
]);

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -17,7 +17,7 @@ const Marks = Module("marks", {
this._urlMarks = storage.newMap("url-marks", { privateData: true, replacer: replacer, store: true });
try {
if(isarray(Iterator(this._localMarks).next()));
if(isarray(Iterator(this._localMarks).next()[1]))
this._localMarks.clear();
}
catch(e) {}
@@ -31,7 +31,7 @@ const Marks = Module("marks", {
*/
get all() {
let lmarks = array(Iterator(this._localMarks.get(this.localURI) || {}));
let umarks = array(Iterator(this._urlMarks)).__proto__;
let umarks = array(Iterator(this._urlMarks)).array;
return lmarks.concat(umarks).sort(function (a, b) String.localeCompare(a[0], b[0]));
},
@@ -54,9 +54,7 @@ const Marks = Module("marks", {
let win = window.content;
let doc = win.document;
if (!doc.body)
return;
if (doc.body instanceof HTMLFrameSetElement) {
if (doc.body && doc.body instanceof HTMLFrameSetElement) {
if (!silent)
dactyl.echoerr("Marks support for frameset pages not implemented yet");
return;
@@ -72,8 +70,8 @@ const Marks = Module("marks", {
dactyl.log("Adding URL mark: " + Marks.markToString(mark, res), 5);
}
else if (Marks.isLocalMark(mark)) {
let marks = this._localMarks.get(doc.URL, {});
marks[mark] = { location: doc.URL, position: position, timestamp: Date.now()*1000 };
let marks = this._localMarks.get(doc.documentURI, {});
marks[mark] = { location: doc.documentURI, position: position, timestamp: Date.now()*1000 };
this._localMarks.changed();
if (!silent)
dactyl.log("Adding local mark: " + Marks.markToString(mark, marks[mark]), 5);
@@ -237,7 +235,7 @@ const Marks = Module("marks", {
// NOTE: this currently differs from Vim's behavior which
// deletes any valid marks in the arg list, up to the first
// invalid arg, as well as giving the error message.
dactyl.assert(!matches, "E475: Invalid argument: " + matches[0]);
dactyl.assert(!matches, "E475: Invalid argument: " + (matches && matches[0]));
// check for illegal ranges - only allow a-z A-Z 0-9
if ((matches = args.match(/[a-zA-Z0-9]-[a-zA-Z0-9]/g))) {

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -120,7 +120,7 @@ const Modes = Module("modes", {
NONE: 0,
__iterator__: function () util.Array.itervalues(this.all),
__iterator__: function () array.itervalues(this.all),
get all() this._mainModes.slice(),
@@ -155,7 +155,8 @@ const Modes = Module("modes", {
getCharModes: function (chr) [m for (m in values(this._modeMap)) if (m.char == chr)],
matchModes: function (obj) [m for (m in values(this._modeMap)) if (Object.keys(obj).every(function (k) obj[k] == (m[k] || false)))],
matchModes: function (obj)
[m for (m in values(this._modeMap)) if (Object.keys(obj).every(function (k) obj[k] == (m[k] || false)))],
// show the current mode string in the command line
show: function () {

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2009-2010 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.
@@ -15,7 +15,7 @@ const ModuleBase = Class("ModuleBase", {
*/
requires: [],
toString: function () "[module " + this.constructor.name + "]"
toString: function () "[module " + this.constructor.classname + "]"
});
/**
@@ -76,26 +76,29 @@ window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad, false);
Module.list.forEach(function(module) {
modules.__defineGetter__(module.name, function() {
delete modules[module.name];
return load(module.name, null, Components.stack.caller);
modules.__defineGetter__(module.classname, function() {
delete modules[module.classname];
return load(module.classname, null, Components.stack.caller);
});
});
function dump(str) window.dump(String.replace(str, /\n?$/, "\n").replace(/^/m, Config.prototype.name.toLowerCase() + ": "));
function dump(str) window.dump(String.replace(str, /\n?$/, "\n").replace(/^/m, services.get("dactyl:").name + ": "));
const start = Date.now();
const deferredInit = { load: [] };
const seen = set();
const loaded = set(["init"]);
modules.loaded = loaded;
function init(module) {
function init(func, mod)
function () defmodule.time(module.name || module.constructor.name, mod, func, module, dactyl, modules, window);
function () defmodule.time(module.classname || module.constructor.classname, mod,
func, module,
dactyl, modules, window);
set.add(loaded, module.constructor.name);
set.add(loaded, module.constructor.classname);
for (let [mod, func] in Iterator(module.INIT)) {
if (mod in loaded)
init(func)();
init(func, mod)();
else {
deferredInit[mod] = deferredInit[mod] || [];
deferredInit[mod].push(init(func, mod));
@@ -112,34 +115,35 @@ window.addEventListener("load", function onLoad() {
}
try {
if (module.name in loaded)
if (module.classname in loaded)
return;
if (module.name in seen)
if (module.classname in seen)
throw Error("Module dependency loop.");
set.add(seen, module.name);
set.add(seen, module.classname);
for (let dep in values(module.requires))
load(Module.constructors[dep], module.name);
load(Module.constructors[dep], module.classname);
defmodule.loadLog.push("Load" + (isstring(prereq) ? " " + prereq + " dependency: " : ": ") + module.name);
defmodule.loadLog.push("Load" + (isstring(prereq) ? " " + prereq + " dependency: " : ": ") + module.classname);
if (frame && frame.filename)
defmodule.loadLog.push(" from: " + frame.filename + ":" + frame.lineNumber);
delete modules[module.name];
modules[module.name] = defmodule.time(module.name, "init", module);
delete modules[module.classname];
modules[module.classname] = defmodule.time(module.classname, "init", module);
init(modules[module.name]);
for (let [, fn] in iter(deferredInit[module.name] || []))
init(modules[module.classname]);
for (let [, fn] in iter(deferredInit[module.classname] || []))
fn();
}
catch (e) {
dump("Loading " + (module && module.name) + ": " + e + "\n" + (e.stack || ""));
dump("Loading " + (module && module.classname) + ": " + e + "\n" + (e.stack || ""));
}
return modules[module.name];
return modules[module.classname];
}
Module.list.forEach(load);
deferredInit["load"].forEach(call);
modules.times = update({}, defmodule.times);
dump("Loaded in " + (Date.now() - start) + "ms");
}, false);

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -23,6 +23,8 @@
* completer - see {@link Option#completer}
* domains - see {@link Option#domains}
* getter - see {@link Option#getter}
* initialValue - Initial value is loaded from getter
* persist - see {@link Option#persist}
* privateData - see {@link Option#privateData}
* scope - see {@link Option#scope}
* setter - see {@link Option#setter}
@@ -56,9 +58,9 @@ const Option = Class("Option", {
// add no{option} variant of boolean {option} to this.names
if (this.type == "boolean")
this.names = array([name, "no" + name] for (name in values(names))).flatten().__proto__;
this.names = array([name, "no" + name] for (name in values(names))).flatten().array;
if (this.globalValue == undefined)
if (this.globalValue == undefined && !this.initialValue)
this.globalValue = this.parseValues(this.defaultValue);
},
@@ -295,6 +297,13 @@ const Option = Class("Option", {
*/
getter: null,
/**
* @property {boolean} When true, this options values will be saved
* when generating a configuration file.
* @default true
*/
persist: true,
/**
* @property {boolean|function(values)} When true, values of this
* option may contain private data which should be purged from
@@ -363,18 +372,23 @@ const Option = Class("Option", {
let re = RegExp(val);
re.bang = bang;
re.result = arguments.length == 2 ? result : !bang;
re.toString = function () Option.unparseRegex(this);
return re;
},
unparseRegex: function (re) re.bang + re.source + (typeof re.result == "string" ? ":" + re.result : ""),
unparseRegex: function (re) re.bang + re.source.replace(/\\(.)/g, function (m, n1) n1 == "/" ? n1 : m) +
(typeof re.result == "string" ? ":" + re.result : ""),
getKey: {
stringlist: function (k) this.values.indexOf(k) >= 0,
get charlist() this.stringlist,
regexlist: function (k) {
for (let re in values(this.values))
if (re.test(k))
return re.result;
return null;
}
},
get regexmap() this.regexlist
},
joinValues: {
@@ -382,6 +396,7 @@ const Option = Class("Option", {
stringlist: function (vals) vals.join(","),
stringmap: function (vals) [k + ":" + v for ([k, v] in Iterator(vals))].join(","),
regexlist: function (vals) vals.map(Option.unparseRegex).join(","),
get regexmap() this.regexlist
},
parseValues: {
@@ -430,10 +445,10 @@ const Option = Class("Option", {
switch (operator) {
case "+":
return util.Array.uniq(Array.concat(orig, values), true);
return array.uniq(Array.concat(orig, values), true);
case "^":
// NOTE: Vim doesn't prepend if there's a match in the current value
return util.Array.uniq(Array.concat(values, orig), true);
return array.uniq(Array.concat(values, orig), true);
case "-":
return orig.filter(function (item) values.indexOf(item) == -1);
case "=":
@@ -452,10 +467,10 @@ const Option = Class("Option", {
values = Array.concat(values);
switch (operator) {
case "+":
return util.Array.uniq(Array.concat(this.values, values), true);
return array.uniq(Array.concat(this.values, values), true);
case "^":
// NOTE: Vim doesn't prepend if there's a match in the current value
return util.Array.uniq(Array.concat(values, this.values), true);
return array.uniq(Array.concat(values, this.values), true);
case "-":
return this.values.filter(function (item) values.indexOf(item) == -1);
case "=":
@@ -468,6 +483,9 @@ const Option = Class("Option", {
}
return null;
},
get charlist() this.stringlist,
get regexlist() this.stringlist,
get regexmap() this.stringlist,
string: function (operator, values, scope, invert) {
switch (operator) {
@@ -503,21 +521,14 @@ const Option = Class("Option", {
}
});
Option.joinValues["regexmap"] = Option.joinValues["regexlist"];
Option.getKey["charlist"] = Option.getKey["stringlist"];
Option.getKey["regexmap"] = Option.getKey["regexlist"];
Option.ops["charlist"] = Option.ops["stringlist"];
Option.ops["regexlist"] = Option.ops["stringlist"];
Option.ops["regexmap"] = Option.ops["stringlist"];
/**
* @instance options
*/
const Options = Module("options", {
init: function () {
this._optionHash = {};
this.needInit = [];
this._options = [];
this._optionMap = {};
this._prefContexts = [];
for (let [, pref] in Iterator(this.allPrefs(Options.OLD_SAVED))) {
@@ -541,45 +552,28 @@ const Options = Module("options", {
});
}
function optionObserver(key, event, option) {
storage.newMap("options", { store: false });
storage.addObserver("options", function optionObserver(key, event, option) {
// Trigger any setters.
let opt = options.get(option);
if (event == "change" && opt)
opt.setValues(opt.globalValue, Option.SCOPE_GLOBAL, true);
}
}, window);
storage.newMap("options", { store: false });
storage.addObserver("options", optionObserver, window);
this.prefObserver.register();
this._branch = services.get("pref").getBranch("").QueryInterface(Ci.nsIPrefBranch2);
this._branch.addObserver("", this, false);
},
destroy: function () {
this.prefObserver.unregister();
this._branch.removeObserver("", this);
},
/** @property {Iterator(Option)} @private */
__iterator__: function ()
array(values(this._optionHash)).sort(function (a, b) String.localeCompare(a.name, b.name))
.itervalues(),
/** @property {Object} Observes preference value changes. */
prefObserver: {
register: function () {
// better way to monitor all changes?
this._branch = services.get("pref").getBranch("").QueryInterface(Ci.nsIPrefBranch2);
this._branch.addObserver("", this, false);
},
unregister: function () {
if (this._branch)
this._branch.removeObserver("", this);
},
observe: function (subject, topic, data) {
if (topic != "nsPref:changed")
return;
values(this._options.sort(function (a, b) String.localeCompare(a.name, b.name))),
observe: function (subject, topic, data) {
if (topic == "nsPref:changed") {
// subject is the nsIPrefBranch we're observing (after appropriate QI)
// data is the name of the pref that's been changed (relative to subject)
switch (data) {
@@ -588,7 +582,7 @@ const Options = Module("options", {
dactyl.mode = value ? modes.CARET : modes.NORMAL;
break;
}
}
}
},
/**
@@ -601,29 +595,28 @@ const Options = Module("options", {
* @param {Object} extra An optional extra configuration hash (see
* {@link Map#extraInfo}).
* @optional
* @returns {boolean} Whether the option was created.
*/
add: function (names, description, type, defaultValue, extraInfo) {
if (!extraInfo)
extraInfo = {};
let option = Option(names, description, type, defaultValue, extraInfo);
if (!option)
return false;
if (option.name in this._optionHash) {
// never replace for now
dactyl.log("Warning: " + names[0].quote() + " already exists, NOT replacing existing option.", 1);
return false;
let name = names[0];
if (name in this._optionMap) {
dactyl.log("Warning: " + name.quote() + " already exists: replacing existing option.", 1);
this.remove(name);
}
// quickly access options with options["wildmode"]:
this.__defineGetter__(option.name, function () option.value);
this.__defineSetter__(option.name, function (value) { option.value = value; });
let closure = function () options._optionMap[name];
memoize(this._options, this._options.length, closure);
memoize(this._optionMap, name, function () Option(names, description, type, defaultValue, extraInfo));
for (let alias in values(names.slice(1)))
memoize(this._optionMap, alias, closure);
if (extraInfo.setter)
memoize(this.needInit, this.needInit.length, closure);
this._optionHash[option.name] = option;
return true;
// quickly access options with options["wildmode"]:
this.__defineGetter__(name, function () this._optionMap[name].value);
this.__defineSetter__(name, function (value) { this._optionMap[name].value = value; });
},
/**
@@ -646,12 +639,8 @@ const Options = Module("options", {
if (!scope)
scope = Option.SCOPE_BOTH;
if (name in this._optionHash)
return (this._optionHash[name].scope & scope) && this._optionHash[name];
for (let opt in values(this._optionHash))
if (opt.hasName(name))
return (opt.scope & scope) && opt;
if (name in this._optionMap && (this._optionMap[name].scope & scope))
return this._optionMap[name];
return null;
},
@@ -734,7 +723,7 @@ const Options = Module("options", {
};
commandline.commandOutput(
template.options(config.hostApplication + " Options", prefs()));
template.options(config.host + " Options", prefs()));
},
/**
@@ -751,7 +740,7 @@ const Options = Module("options", {
let matches, prefix, postfix, valueGiven;
[matches, prefix, ret.name, postfix, valueGiven, ret.operator, ret.value] =
args.match(/^\s*(no|inv)?([a-z_]*)([?&!])?\s*(([-+^]?)=(.*))?\s*$/) || [];
args.match(/^\s*(no|inv)?([a-z_-]*?)([?&!])?\s*(([-+^]?)=(.*))?\s*$/) || [];
ret.args = args;
ret.onlyNonDefault = false; // used for :set to print non-default options
@@ -760,8 +749,13 @@ const Options = Module("options", {
ret.onlyNonDefault = true;
}
if (matches)
if (matches) {
ret.option = options.get(ret.name, ret.scope);
if (!ret.option && (ret.option = options.get(prefix + ret.name, ret.scope))) {
ret.name = prefix + ret.name;
prefix = "";
}
}
ret.prefix = prefix;
ret.postfix = postfix;
@@ -795,10 +789,10 @@ const Options = Module("options", {
* any of the options's names.
*/
remove: function (name) {
for each (let option in this._optionHash) {
if (option.hasName(name))
delete this._optionHash[option.name];
}
let opt = this.get(name);
for (let name in values(opt.names))
delete this._optionMap[name];
this._options = this._options.filter(function (o) o != opt);
},
/** @property {Object} The options store. */
@@ -1023,7 +1017,7 @@ const Options = Module("options", {
}
if (name == "all" && reset)
commandline.input("Warning: Resetting all preferences may make " + config.hostApplication + " unusable. Continue (yes/[no]): ",
commandline.input("Warning: Resetting all preferences may make " + config.host + " unusable. Continue (yes/[no]): ",
function (resp) {
if (resp == "yes")
for (let pref in values(options.allPrefs()))
@@ -1037,20 +1031,14 @@ const Options = Module("options", {
else if (invertBoolean)
options.invertPref(name);
else if (valueGiven) {
switch (value) {
case undefined:
if (value == undefined)
value = "";
break;
case "true":
else if (value == "true")
value = true;
break;
case "false":
value = false;
break;
default:
if (/^\d+$/.test(value))
value = parseInt(value, 10);
}
else if (value == "false")
value = true;
else if (/^\d+$/.test(value))
value = parseInt(value, 10);
options.setPref(name, value);
}
else
@@ -1123,7 +1111,7 @@ const Options = Module("options", {
context.completions = [
[options._loadPreference(filter, null, false), "Current Value"],
[options._loadPreference(filter, null, true), "Default Value"]
].filter(function ([k]) k != null);
].filter(function ([k]) k != null && k.length < 200);
return null;
}
@@ -1134,15 +1122,10 @@ const Options = Module("options", {
let prefix = opt.prefix;
if (context.filter.indexOf("=") == -1) {
if (prefix)
context.filters.push(function ({ item: opt }) opt.type == "boolean" || prefix == "inv" && opt.values instanceof Array);
return completion.option(context, opt.scope);
if (false && prefix)
context.filters.push(function ({ item }) item.type == "boolean" || prefix == "inv" && isarray(item.values));
return completion.option(context, opt.scope, prefix);
}
else if (prefix == "no")
return null;
if (prefix)
context.advance(prefix.length);
let option = opt.option;
context.advance(context.filter.indexOf("=") + 1);
@@ -1155,17 +1138,33 @@ const Options = Module("options", {
if (opt.get || opt.reset || !option || prefix)
return null;
if (!opt.value) {
if (!opt.value && !opt.operator && !opt.invert) {
context.fork("default", 0, this, function (context) {
context.title = ["Extra Completions"];
context.completions = [
[option.value, "Current value"],
[option.defaultValue, "Default value"]
].filter(function (f) f[0] != "");
].filter(function (f) f[0] != "" && f[0].length < 200);
});
}
return context.fork("values", 0, completion, "optionValue", opt.name, opt.operator);
let optcontext = context.fork("values");
completion.optionValue(optcontext, opt.name, opt.operator);
// Fill in the current values if we're removing
if (opt.operator == "-" && isarray(opt.values)) {
let have = set([i.text for (i in context.allItems)]);
context = context.fork("current-values", 0);
context.anchored = optcontext.anchored
context.maxItems = optcontext.maxItems
context.filters.push(function (i) !set.has(have, i.text));
completion.optionValue(context, opt.name, opt.operator, null,
function (context) {
context.generate = function () option.values.map(function (o) [o, ""])
});
context.title = ["Current values"];
}
}
commands.add(["let"],
@@ -1321,17 +1320,21 @@ const Options = Module("options", {
});
},
completion: function () {
completion.option = function option(context, scope) {
completion.option = function option(context, scope, prefix) {
context.title = ["Option"];
context.keys = { text: "names", description: "description" };
context.completions = options;
if (prefix == "inv")
context.keys.text = function (opt)
opt.type == "boolean" || isarray(opt.values) ? opt.names.map(function (n) "inv" + n)
: opt.names;
if (scope)
context.filters.push(function ({ item: opt }) opt.scope & scope);
context.filters.push(function ({ item }) item.scope & scope);
};
completion.optionValue = function (context, name, op, curValue) {
completion.optionValue = function (context, name, op, curValue, completer) {
let opt = options.get(name);
let completer = opt.completer;
completer = completer || opt.completer;
if (!completer)
return;
@@ -1372,31 +1375,24 @@ const Options = Module("options", {
context.advance(context.filter.length - len);
context.title = ["Option Value"];
let completions = completer(context);
if (!isarray(completions))
completions = array(completions).__proto__;
if (!completions)
return;
// Not Vim compatible, but is a significant enough improvement
// that it's worth breaking compatibility.
if (isarray(newValues)) {
completions = completions.filter(function (val) newValues.indexOf(val[0]) == -1);
switch (op) {
case "+":
completions = completions.filter(function (val) curValues.indexOf(val[0]) == -1);
break;
case "-":
completions = completions.filter(function (val) curValues.indexOf(val[0]) > -1);
break;
}
context.filters.push(function (i) newValues.indexOf(i.text) == -1);
if (op == "+")
context.filters.push(function (i) curValues.indexOf(i.text) == -1);
if (op == "-")
context.filters.push(function (i) curValues.indexOf(i.text) > -1);
}
context.completions = completions;
let res = completer.call(opt, context);
if (res)
context.completions = res;
};
completion.preference = function preference(context) {
context.anchored = false;
context.title = [config.hostApplication + " Preference", "Value"];
context.title = [config.host + " Preference", "Value"];
context.keys = { text: function (item) item, description: function (item) options.getPref(item) };
context.completions = options.allPrefs();
};
@@ -1404,14 +1400,14 @@ const Options = Module("options", {
javascript: function () {
JavaScript.setCompleter(this.get, [function () ([o.name, o.description] for (o in options))]);
JavaScript.setCompleter([this.getPref, this.safeSetPref, this.setPref, this.resetPref, this.invertPref],
[function () options.allPrefs().map(function (pref) [pref, ""])]);
[function (context) (context.anchored=false, options.allPrefs().map(function (pref) [pref, ""]))]);
},
sanitizer: function () {
sanitizer.addItem("options", {
description: "Options containing hostname data",
action: function (timespan, host) {
if (host)
for (let opt in values(options._optionHash))
for (let opt in values(options._options))
if (timespan.contains(opt.lastSet * 1000) && opt.domains)
try {
opt.values = opt.filterDomain(host, opt.values);
@@ -1421,12 +1417,12 @@ const Options = Module("options", {
}
},
privateEnter: function () {
for (let opt in values(options._optionHash))
for (let opt in values(options._options))
if (opt.privateData && (!callable(opt.privateData) || opt.privateData(opt.values)))
opt.oldValue = opt.value;
},
privateLeave: function () {
for (let opt in values(options._optionHash))
for (let opt in values(options._options))
if (opt.oldValue != null) {
opt.value = opt.oldValue;
opt.oldValue = null;

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -26,7 +26,7 @@ const QuickMarks = Module("quickmarks", {
*/
add: function add(qmark, location) {
this._qmarks.set(qmark, location);
dactyl.echomsg("Added Quick Mark '" + qmark + "': " + location, 1);
dactyl.echomsg({ domains: [util.getHost(location)], message: "Added Quick Mark '" + qmark + "': " + location }, 1);
},
/**

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
// Copyright (c) 2008-2010 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.
@@ -14,8 +14,9 @@ const StatusLine = Module("statusline", {
this._statusBar.collapsed = true; // it is later restored unless the user sets laststatus=0
// our status bar fields
this.widgets = dict(["status", "url", "inputbuffer", "progress", "tabcount", "bufferposition", "zoomlevel"].map(
function (field) [field, document.getElementById("dactyl-statusline-field-" + field)]));
this.widgets = array(["status", "url", "inputbuffer", "progress", "tabcount", "bufferposition", "zoomlevel"]
.map(function (field) [field, document.getElementById("dactyl-statusline-field-" + field)]))
.toObject();
},
/**
@@ -36,7 +37,7 @@ const StatusLine = Module("statusline", {
insecure: "StatusLine"
};
this._statusBar.setAttributeNS(NS.uri, "highlight", highlightGroup[type]);
highlight.highlightNode(this._statusBar, highlightGroup[type]);
},
// update all fields of the statusline
@@ -115,8 +116,8 @@ const StatusLine = Module("statusline", {
}
if (modules.bookmarks) {
if (bookmarks.isBookmarked(buffer.URL))
modified += "\u2764"; // a heart symbol: ❤
//modified += "\u2665"; // a heart symbol: ♥
modified += UTF8("❤");
//modified += UTF8("♥");
}
if (modified)
@@ -206,7 +207,9 @@ const StatusLine = Module("statusline", {
let win = document.commandDispatcher.focusedWindow;
if (!win)
return;
percent = win.scrollMaxY == 0 ? -1 : win.scrollY / win.scrollMaxY;
win.scrollY;
percent = win.scrollY == 0 ? 0 : // This prevents a forced rendering
win.scrollMaxY == 0 ? -1 : win.scrollY / win.scrollMaxY;
}
let bufferPositionStr = "";
@@ -255,11 +258,11 @@ const StatusLine = Module("statusline", {
{
setter: function setter(value) {
if (value == 0)
document.getElementById("status-bar").collapsed = true;
statusline._statusBar.collapsed = true;
else if (value == 1)
dactyl.echoerr("show status line only with > 1 window not implemented yet");
else
document.getElementById("status-bar").collapsed = false;
statusline._statusBar.collapsed = false;
return value;
},

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k at Gmail>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
@@ -21,6 +21,13 @@ const Tabs = Module("tabs", {
this._lastBufferSwitchArgs = "";
this._lastBufferSwitchSpecial = true;
let fragment = dactyl.has("MacUnix") ? "tab-mac" : "tab";
this.tabBinding = styles.addSheet(true, "tab-binding", "chrome://browser/content/browser.xul",
".tabbrowser-tab { -moz-binding: url(chrome://dactyl/content/bindings.xml#" + fragment + ") !important; }" +
// FIXME: better solution for themes?
".tabbrowser-tab[busy] > .tab-icon > .tab-icon-image { list-style-image: url('chrome://global/skin/icons/loading_16.png') !important; }",
false, true);
// hide tabs initially to prevent flickering when 'stal' would hide them
// on startup
if (config.hasTabbrowser)
@@ -74,22 +81,6 @@ const Tabs = Module("tabs", {
return store.options;
},
/**
* @property {boolean} Whether the tab numbering XBL binding has been
* applied.
*/
get tabsBound() Boolean(styles.get(true, "tab-binding")),
set tabsBound(val) {
let fragment = dactyl.has("MacUnix") ? "tab-mac" : "tab";
if (!val)
styles.removeSheet(true, "tab-binding");
else if (!this.tabsBound)
styles.addSheet(true, "tab-binding", "chrome://browser/content/browser.xul",
".tabbrowser-tab { -moz-binding: url(chrome://dactyl/content/bindings.xml#" + fragment + ") !important; }" +
// FIXME: better solution for themes?
".tabbrowser-tab[busy] > .tab-icon > .tab-icon-image { list-style-image: url('chrome://global/skin/icons/loading_16.png') !important; }");
},
get visibleTabs() config.tabbrowser.visibleTabs || this.allTabs.filter(function (tab) !tab.hidden),
/**
@@ -721,7 +712,7 @@ const Tabs = Module("tabs", {
});
commands.add(["quita[ll]", "qa[ll]"],
"Quit " + config.name,
"Quit " + config.appname,
function (args) { dactyl.quit(false, args.bang); }, {
argCount: "0",
bang: true
@@ -831,7 +822,7 @@ const Tabs = Module("tabs", {
argCount: "+",
completer: function (context, args) {
if (args.completeArg == 0) {
context.filters.push(function ({ item: win }) win != window);
context.filters.push(function ({ item }) item != window);
completion.window(context);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB