summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKris Maglione <maglione.k@gmail.com>2011-02-17 15:23:17 -0500
committerKris Maglione <maglione.k@gmail.com>2011-02-17 15:23:17 -0500
commita54573522acb5afcdaacf370ec19aec2d858d236 (patch)
tree82372893e3b0da8d03de61f421e0bdaac4982069 /common
parent589849c06d4a7b6751f685a38debf3d7f882c619 (diff)
downloadpentadactyl-a54573522acb5afcdaacf370ec19aec2d858d236.tar.gz
Replace XPath-based hint paths with CSS selectors. Needs cleanup and validation.
--HG-- extra : rebase_source : 83035481bf697b7b57e17e516b0dfc61329164c6
Diffstat (limited to 'common')
-rw-r--r--common/content/dactyl.js3
-rw-r--r--common/content/help.xsl4
-rw-r--r--common/content/hints.js71
-rw-r--r--common/locale/en-US/options.xml50
-rw-r--r--common/modules/io.jsm3
-rw-r--r--common/modules/options.jsm50
-rw-r--r--common/modules/util.jsm20
7 files changed, 129 insertions, 72 deletions
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index 763e07d5..cfca1372 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -1382,6 +1382,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
dactyl.echoerr(template.linkifyHelp(error.message));
else
dactyl.beep();
+
+ if (!error.noTrace)
+ util.reportError(error);
return;
}
if (error.result == Cr.NS_BINDING_ABORTED)
diff --git a/common/content/help.xsl b/common/content/help.xsl
index ac166064..0f5ae9b5 100644
--- a/common/content/help.xsl
+++ b/common/content/help.xsl
@@ -231,7 +231,9 @@
<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>
+ <span dactyl:highlight="HelpDefault">
+ <xsl:copy-of select="@*"/>
+ <xsl:text>(default: </xsl:text>
<xsl:choose>
<xsl:when test="$type = 'string'">
<span dactyl:highlight="HelpString" delim="'"><xsl:apply-templates mode="help-1"/></span>
diff --git a/common/content/hints.js b/common/content/hints.js
index a473e6c9..9538f4fe 100644
--- a/common/content/hints.js
+++ b/common/content/hints.js
@@ -271,7 +271,7 @@ var HintSession = Class("HintSession", CommandMode, {
return true;
}
- let body = doc.body || util.evaluateXPath(["body"], doc).snapshotItem(0);
+ let body = doc.body || doc.querySelector("body");
if (body) {
let fragment = util.xmlToDom(<div highlight="hints"/>, doc);
body.appendChild(fragment);
@@ -281,7 +281,7 @@ var HintSession = Class("HintSession", CommandMode, {
let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint" style="display: none"/>, doc);
let mode = this.hintMode;
- let res = util.evaluateXPath(mode.xpath, doc, true);
+ let res = mode.matcher(doc);
let start = this.pageHints.length;
for (let elem in res) {
@@ -670,6 +670,29 @@ var HintSession = Class("HintSession", CommandMode, {
},
});
+function compileMatcher(list) {
+ let xpath = [], css = [];
+ for (let elem in values(list))
+ if (/^xpath:/.test(elem))
+ xpath.push(elem.substr(6));
+ else
+ css.push(elem);
+
+ return update(
+ function matcher(node) {
+ if (matcher.xpath)
+ for (let elem in util.evaluateXPath(matcher.xpath, node))
+ yield elem;
+
+ if (matcher.css)
+ for (let [, elem] in iter(node.querySelectorAll(matcher.css)))
+ yield elem;
+ }, {
+ css: css.join(", "),
+ xpath: xpath.join(" | ")
+ });
+}
+
var Hints = Module("hints", {
init: function init() {
this.resizeTimer = Timer(100, 500, function () {
@@ -682,8 +705,8 @@ var Hints = Module("hints", {
events.listen(appContent, "scroll", this.resizeTimer.closure.tell, false);
const Mode = Hints.Mode;
- Mode.defaultValue("tags", function () function () options["hinttags"]);
- Mode.prototype.__defineGetter__("xpath", function ()
+ Mode.defaultValue("tags", function () function () options.get("hinttags").matcher);
+ Mode.prototype.__defineGetter__("matcher", function ()
options.get("extendedhinttags").getKey(this.name, this.tags()));
this.modes = {};
@@ -1162,23 +1185,41 @@ var Hints = Module("hints", {
},
options: function () {
function xpath(arg) util.makeXPath(arg);
+
options.add(["extendedhinttags", "eht"],
- "XPath strings of hintable elements for extended hint modes",
+ "XPath or CSS selector strings of hintable elements for extended hint modes",
"regexpmap", {
- "[iI]": xpath(["img"]),
- "[asOTivVWy]": xpath(["{a,area}[@href]", "{img,iframe}[@src]"]),
- "[f]": xpath(["body"]),
- "[F]": xpath(["body", "code", "div", "html", "p", "pre", "span"]),
- "[S]": xpath(["input[not(@type='hidden')]", "textarea", "button", "select"])
+ "[iI]": "img",
+ "[asOTivVWy]": ["a[href]", "area[href]", "img[src]", "iframe[src]"],
+ "[f]": "body",
+ "[F]": ["body", "code", "div", "html", "p", "pre", "span"],
+ "[S]": ["input:not([type=hidden])", "textarea", "button", "select"]
},
- { validator: Option.validateXPath });
+ {
+ keepQuotes: true,
+ getKey: function (val, default_)
+ let (res = array.nth(this.value, function (re) re.test(val), 0))
+ res ? res.matcher : default_,
+ setter: function (vals) {
+ for (let value in values(vals))
+ value.matcher = compileMatcher(Option.splitList(value.result));
+ return vals;
+ },
+ validator: Option.validateXPath
+ });
options.add(["hinttags", "ht"],
"XPath string of hintable elements activated by 'f' and 'F'",
- "string", xpath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select",
- "*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or " +
- "@tabindex or @role='link' or @role='button']"]),
- { validator: Option.validateXPath });
+ "stringlist", "input:not([type=hidden]),a,area,iframe,textarea,button,select," +
+ "[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," +
+ "[tabindex],[role=link],[role=button]",
+ {
+ setter: function (values) {
+ this.matcher = compileMatcher(values);
+ return values;
+ },
+ validator: Option.validateXPath
+ });
options.add(["hintkeys", "hk"],
"The keys used to label and select hints",
diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml
index 5e98816e..9247a5c2 100644
--- a/common/locale/en-US/options.xml
+++ b/common/locale/en-US/options.xml
@@ -1,14 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>
-<!DOCTYPE document SYSTEM "dactyl://content/dtd" [
- <!ENTITY hinttags "//*[@onclick or @onmouseover or @onmousedown or
- @onmouseup or @oncommand or @role='link'] |
- //input[not(@type='hidden')] | //xhtml:input[not(@type='hidden')] |
- //a | //xhtml:a | //area | //xhtml:area |
- //button | //xhtml:button | //iframe | //xhtml:iframe |
- //select | //xhtml:select | //textarea | //xhtml:textarea">
-]>
+<!DOCTYPE document SYSTEM "dactyl://content/dtd">
<document
name="options"
@@ -631,26 +624,19 @@
<item>
<tags>'eht' 'extendedhinttags'</tags>
- <spec>'extendedhinttags' 'eht'</spec>
<strut/>
+ <spec>'extendedhinttags'</spec>
<type>regexpmap</type>
- <default>[iI]:'//img | //xhtml:img',
- [OTivVWy]:'//a[@href] | //xhtml:a[@href] |
- //area[@href] | //xhtml:area[@href] |
- //img[@src] | //xhtml:img[@src] |
- //iframe[@src] | //xhtml:iframe[@src]',
- [F]:'//div | //xhtml:div | //span | //xhtml:span |
- //p | //xhtml:p | //body | //xhtml:body |
- //html | //xhtml:html'
- [S]:'//input[not(@type=''hidden'')] |
- //xhtml:input[not(@type=''hidden'')] |
- //textarea | //xhtml:textarea |
- //button | //xhtml:button |
- //select | //xhtml:select'</default>
- <description>
- <p>
- Defines specialized XPath expressions for arbitrary
- <t>extended-hints</t> modes. If no matches are found, the value of
+ <default style="display: block;">[asOTivVWy]:a[href],area[href],img[src],iframe[src],
+ [f]:body,
+ [F]:body,code,div,html,p,pre,span,
+ [iI]:img,
+ [S]:button,'input:not([type=hidden])',select,textarea</default>
+ <description>
+ <p>
+ Defines specialized CSS selectors or XPath expressions for arbitrary
+ <t>extended-hints</t> modes. The syntax is the same as for
+ <o>hinttags</o>. If no matches are found, the value of
<o>hinttags</o> is used.
</p>
</description>
@@ -855,12 +841,16 @@
<tags>'ht' 'hinttags'</tags>
<strut/>
<spec>'hinttags' 'ht'</spec>
- <type>string</type>
- <default>&hinttags;</default>
+ <type>stringlist</type>
+ <default style="display: block;">a,area,button,iframe,input:not([type=hidden]),select,textarea,
+ [onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand],
+ [tabindex],[role=link],[role=button]</default>
<description>
<p>
- The XPath string used to select elements for
- <link topic="hints">hinting</link>. Can be overridden for
+ A list of CSS selectors or XPath expressions used to select elements
+ for <link topic="hints">hinting</link>. Values beginning with the
+ string <str>xpath:</str> are treated as XPath expressions, while any
+ other values are treated as CSS selectors. Can be overridden for
individual <t>extended-hints</t> modes with the
<o>extendedhinttags</o> option.
</p>
diff --git a/common/modules/io.jsm b/common/modules/io.jsm
index 71c72ba2..ef18759f 100644
--- a/common/modules/io.jsm
+++ b/common/modules/io.jsm
@@ -205,8 +205,7 @@ var IO = Module("io", {
return context;
}
catch (e) {
- if (!(e instanceof FailedAssertion))
- dactyl.reportError(e);
+ dactyl.reportError(e);
let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e);
if (!params.silent)
dactyl.echoerr(message);
diff --git a/common/modules/options.jsm b/common/modules/options.jsm
index 74e1347f..7a9452fc 100644
--- a/common/modules/options.jsm
+++ b/common/modules/options.jsm
@@ -415,18 +415,23 @@ var Option = Class("Option", {
},
parseRegexp: function (value, result, flags) {
+ let keepQuotes = this && this.keepQuotes;
if (isArray(flags)) // Called by Array.map
result = flags = undefined;
+ if (flags == null)
+ flags = this && this.regexpFlags || "";
+
let [, bang, val] = /^(!?)(.*)/.exec(value);
let re = RegExp(Option.dequote(val), flags);
re.bang = bang;
re.result = result !== undefined ? result : !bang;
- re.toString = function () Option.unparseRegexp(this);
+ re.toString = function () Option.unparseRegexp(this, keepQuotes);
return re;
},
- unparseRegexp: function (re) re.bang + Option.quote(util.regexp.getSource(re), /^!|:/) +
- (typeof re.result === "boolean" ? "" : ":" + Option.quote(re.result)),
+
+ unparseRegexp: function (re, quoted) re.bang + Option.quote(util.regexp.getSource(re), /^!|:/) +
+ (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result))),
parseSite: function parseSite(pattern, result, rest) {
if (isArray(rest)) // Called by Array.map
@@ -500,23 +505,25 @@ var Option = Class("Option", {
return [key, Option.dequote(v.substr(count + 1))];
})),
- regexpmap: function (value)
- Option.splitList(value, true).map(function (v) {
- let [count, re, quote] = Commands.parseArg(v, /:/, true);
- let val = Option.dequote(v.substr(count + 1));
- if (count === v.length)
- [val, re] = [re, ".?"];
- return Option.parseRegexp(re, val, this.regexpFlags);
- }, this),
-
- sitemap: function (value)
- Option.splitList(value, true).map(function (v) {
- let [count, re, quote] = Commands.parseArg(v, /:/, true);
- let val = Option.dequote(v.substr(count + 1));
- if (count === v.length)
- [val, re] = [re, "*"];
- return Option.parseSite(re, val);
- }, this)
+ regexpmap: function (value) Option.parse.list.call(this, value, Option.parseRegexp),
+
+ sitemap: function (value) Option.parse.list.call(this, value, Option.parseSite),
+
+ list: function (value, parse) let (prev = null)
+ array.compact(Option.splitList(value, true).map(function (v) {
+ let [count, filter, quote] = Commands.parseArg(v, /:/, true);
+
+ let val = v.substr(count + 1);
+ if (!this.keepQuotes)
+ val = Option.dequote(val);
+
+ if (v.length > count)
+ return prev = parse.call(this, filter, val);
+ else {
+ util.assert(prev, "Syntax error", false);
+ prev.result += "," + v;
+ }
+ }, this))
},
testValues: {
@@ -549,7 +556,7 @@ var Option = Class("Option", {
return res;
},
- quote: function quote(str, re)
+ quote: function quote(str, re) isArray(str) ? str.map(function (s) quote(s, re)).join(",") :
Commands.quoteArg[/[\s|"'\\,]|^$/.test(str) || re && re.test && re.test(str)
? (/[\b\f\n\r\t]/.test(str) ? '"' : "'")
: ""](str, re),
@@ -683,6 +690,7 @@ var Option = Class("Option", {
},
validateXPath: function (values) {
+ return true; // For now.
let evaluator = services.XPathEvaluator();
return this.testValues(values,
function (value) evaluator.createExpression(value, util.evaluateXPath.resolver));
diff --git a/common/modules/util.jsm b/common/modules/util.jsm
index 32fe8af7..d41024b6 100644
--- a/common/modules/util.jsm
+++ b/common/modules/util.jsm
@@ -22,7 +22,18 @@ var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is
var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator");
default xml namespace = XHTML;
-var FailedAssertion = Class("FailedAssertion", ErrorBase);
+var FailedAssertion = Class("FailedAssertion", ErrorBase, {
+ init: function init(message, level, noTrace) {
+ if (noTrace !== undefined)
+ this.noTrace = noTrace;
+ init.supercall(this, message, level);
+ },
+
+ level: 3,
+
+ noTrace: true
+});
+
var Point = Struct("x", "y");
var wrapCallback = function wrapCallback(fn) {
@@ -144,9 +155,9 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
* @param {string} message The message to present to the
* user on failure.
*/
- assert: function (condition, message) {
+ assert: function (condition, message, quiet) {
if (!condition)
- throw FailedAssertion(message, 1);
+ throw FailedAssertion(message, 1, quiet === undefined ? true : quiet);
},
/**
@@ -1417,6 +1428,9 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
errors: Class.memoize(function () []),
maxErrors: 15,
reportError: function (error) {
+ if (error.noTrace)
+ return;
+
if (Cu.reportError)
Cu.reportError(error);