From: Daniel Carl Date: Tue, 2 Jul 2013 21:27:56 +0000 (+0200) Subject: Use only single hint instance per page. X-Git-Url: https://git.owens.tech/assets/lich_lifts_title_slice.png/assets/lich_lifts_title_slice.png/git?a=commitdiff_plain;h=fbfb25ca5f3ddd85c71916aa16fb24716a66780a;p=vimb.git Use only single hint instance per page. --- diff --git a/src/hints.c b/src/hints.c index 2163120..54100c5 100644 --- a/src/hints.c +++ b/src/hints.c @@ -24,7 +24,7 @@ #include "command.h" #include "hints.js.h" -#define HINT_VAR "VpHint" +#define HINT_VAR "VbHint" #define HINT_FILE "hints.js" static struct { @@ -96,7 +96,7 @@ void hints_create(const char *input, guint mode, const guint prefixLength) } js = g_strdup_printf( - "%s = new VimbHints('%c', '%c', '%s', '%s', '%s', '%s', %d);", + "%s.init('%c', '%c', '%s', '%s', '%s', '%s', %d);", HINT_VAR, type, usage, style->hint_bg, style->hint_bg_focus, @@ -126,7 +126,7 @@ void hints_update(const gulong num) void hints_focus_next(const gboolean back) { - char *js = g_strdup_printf(back ? "%s.focusPrev()" : "%s.focusNext();", HINT_VAR); + char *js = g_strdup_printf("%s.focus(%d);", HINT_VAR, back); run_script(js); g_free(js); } diff --git a/src/hints.js b/src/hints.js index e3f268e..726720f 100644 --- a/src/hints.js +++ b/src/hints.js @@ -1,56 +1,65 @@ -/* mode: l - links, i - images, e - editables */ -/* usage: O - open, T - open in new window, U - use source */ -function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) -{ - "use strict"; - var hClass = "__hint"; - var hClassFocus = "__hintFocus"; - var hConts = []; - var hints = []; - var focusNum = 1; - - this.create = function(inputText) - { - var topwin = window; - var top_height = topwin.innerHeight; - var top_width = topwin.innerWidth; - var hCount = 0; +var VbHint = (function(){ + 'use strict'; + + var hClass = "__hint", + hClassFocus = "__hintFocus", + hConts = [], + hints = [], + focusNum = 1, + config = {}; + + /* mode: l - links, i - images, e - editables */ + /* usage: O - open, T - open in new window, U - use source */ + function init(mode, usage, bg, bgf, fg, style, maxHints) { + config = { + mode: mode, + usage: usage, + bg: bg, + bgf: bgf, + fg: fg, + style: style, + maxHints: maxHints + }; + } - this.clear(); + function create(inputText) { + var topwin = window, + top_height = topwin.innerHeight, + top_width = topwin.innerWidth, + hCount = 0; - function _helper(win, offsetX, offsetY) - { + clear(); + + function helper(win, offsetX, offsetY) { /* document may be undefined for frames out of the same origin */ /* policy and will break the whole code - so we check this before */ if (win.document === undefined) { return; } - var doc = win.document; + var doc = win.document, + fragment = doc.createDocumentFragment(), + xpath = getXpath(inputText), + res = doc.evaluate( + xpath, doc, + function (p) {return "http://www.w3.org/1999/xhtml";}, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null + ), + + /* generate basic hint element which will be cloned and updated later */ + hintSpan = doc.createElement("span"), + /* Bounds */ + minX = offsetX < 0 ? -offsetX : 0, + minY = offsetY < 0 ? -offsetY : 0, + maxX = offsetX + win.width > top_width ? top_width - offsetX : top_width, + maxY = offsetY + win.height > top_height ? top_height - offsetY : top_height, + rect, e, i; - var fragment = doc.createDocumentFragment(); - var xpath = _getXpath(inputText); - - var res = doc.evaluate( - xpath, doc, - function (p) {return "http://www.w3.org/1999/xhtml";}, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null - ); - - /* generate basic hint element which will be cloned and updated later */ - var hintSpan = doc.createElement("span"); hintSpan.setAttribute("class", hClass); - hintSpan.style.cssText = style; - - /* Bounds */ - var minX = offsetX < 0 ? -offsetX : 0; - var minY = offsetY < 0 ? -offsetY : 0; - var maxX = offsetX + win.width > top_width ? top_width - offsetX : top_width; - var maxY = offsetY + win.height > top_height ? top_height - offsetY : top_height; + hintSpan.style.cssText = config.style; /* due to the different XPath result type, we will need two counter variables */ - var rect, e; for (i = 0; i < res.snapshotLength; i++) { - if (hCount >= maxHints) { + if (hCount >= config.maxHints) { break; } @@ -80,12 +89,12 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) num: hCount, span: hint, bg: e.style.background, - fg: e.style.color} - ); + fg: e.style.color + }); /* change the foreground and background colors of the hinted items */ - e.style.color = fg; - e.style.background = bg; + e.style.color = config.fg; + e.style.background = config.bg; } var hDiv = doc.createElement("div"); @@ -97,83 +106,74 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) hConts.push(hDiv); /* recurse into any iframe or frame element */ - var frameTags = ["frame","iframe"]; - for (var f = 0; f < frameTags.length; ++f) { - var frames = doc.getElementsByTagName(frameTags[f]); - for (var i = 0, nframes = frames.length; i < nframes; ++i) { - e = frames[i]; + var frameTags = ["frame","iframe"], + frames, n, f, i; + for (f = 0; f < frameTags.length; ++f) { + frames = doc.getElementsByTagName(frameTags[f]); + for (i = 0, n = frames.length; i < n; ++i) { + e = frames[i]; rect = e.getBoundingClientRect(); if (!e.contentWindow || !rect || rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY) { continue; } - _helper(e.contentWindow, offsetX + rect.left, offsetY + rect.top); + helper(e.contentWindow, offsetX + rect.left, offsetY + rect.top); } } } - _helper(topwin, 0, 0); + helper(topwin, 0, 0); if (hCount <= 1) { - return this.fire(1); + return fire(1); } - return _focus(1); - }; - - /* set focus to next avaiable hint */ - this.focusNext = function() - { - var i = _getHintIdByNum(focusNum); - - if (hints[i + 1] !== undefined) { - return _focus(hints[i + 1].num); - } - return _focus(hints[0].num); - }; + return focusHint(1); + } - /* set focus to previous avaiable hint */ - this.focusPrev = function() - { - var i = _getHintIdByNum(focusNum); - if (i !== 0 && hints[i - 1].num !== undefined) { - return _focus(hints[i - 1].num); + function focus(back) { + var num, + i = getHintId(focusNum); + if (back) { + num = (i !== 0 && hints[i - 1].num !== undefined) + ? (hints[i - 1].num) + : (hints[hints.length - 1].num); + } else { + num = (hints[i + 1] !== undefined) + ? (hints[i + 1].num) + : (hints[0].num); } - return _focus(hints[hints.length - 1].num); - }; + return focusHint(num); + } - /* filters hints matching given number */ - this.update = function(n) - { + function update(n) { + var remove = [], i, hint; if (n === 0) { - return this.create(); + return create(); } /* remove none matching hints */ - var remove = []; - var i; for (i = 0; i < hints.length; ++i) { - var hint = hints[i]; + hint = hints[i]; if (0 !== hint.num.toString().indexOf(n.toString())) { remove.push(hint.num); } } for (i = 0; i < remove.length; ++i) { - _removeHint(remove[i]); + removeHint(remove[i]); } if (hints.length === 1) { - return this.fire(hints[0].num); + return fire(hints[0].num); } - return _focus(n); - }; + return focusHint(n); + } - /* remove all hints and set previous style to them */ - this.clear = function() - { + function clear() { + var i, hint; if (hints.length === 0) { return; } - for (var i = 0; i < hints.length; ++i) { - var hint = hints[i]; + for (i = 0; i < hints.length; ++i) { + hint = hints[i]; if (hint.e) { hint.e.style.background = hint.bg; hint.e.style.color = hint.fg; @@ -183,34 +183,69 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) } } hints = []; - for (var i = 0; i < hConts.length; ++i) { + for (i = 0; i < hConts.length; ++i) { hConts[i].parentNode.removeChild(hConts[i]); } hConts = []; - }; + } + + function fire(n) { + var hint = getHint(n ? n : focusNum); + if (!hint) { + return "DONE:"; + } + + var e = hint.e, + tag = e.nodeName.toLowerCase(), + type = e.type ? e.type : ""; + + clear(); + if (tag === "input" || tag === "textarea" || tag === "select") { + if (type === "radio" || type === "checkbox") { + e.focus(); + click(e); + return "DONE:"; + } + if (type === "submit" || type === "reset" || type === "button" || type === "image") { + click(e); + return "DONE:"; + } + e.focus(); + return "INSERT:"; + } + if (tag === "iframe" || tag === "frame") { + e.focus(); + return "DONE:"; + } + + switch (usage) { + case "T": open(e, true); return "DONE:"; + case "O": open(e, false); return "DONE:"; + default: return "DATA:" + getSrc(e); + } + } /* fires the modeevent on hint with given number */ - this.fire = function(n) - { - var hint = _getHintByNum(n ? n : focusNum); + function fire(n) { + var hint = getHint(n ? n : focusNum); if (!hint) { return "DONE:"; } - var e = hint.e; - var tag = e.nodeName.toLowerCase(); - var type = e.type ? e.type : ""; + var e = hint.e, + tag = e.nodeName.toLowerCase(), + type = e.type ? e.type : ""; - this.clear(); + clear(); if (tag === "input" || tag === "textarea" || tag === "select") { if (type === "radio" || type === "checkbox") { e.focus(); - _click(e); + click(e); return "DONE:"; } if (type === "submit" || type === "reset" || type === "button" || type === "image") { - _click(e); + click(e); return "DONE:"; } e.focus(); @@ -220,15 +255,15 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) return "DONE:"; } - switch (usage) { - case "T": _open(e, true); return "DONE:"; - case "O": _open(e, false); return "DONE:"; - default: return "DATA:" + _getSrc(e); + switch (config.usage) { + case "T": open(e, true); return "DONE:"; + case "O": open(e, false); return "DONE:"; + default: return "DATA:" + getSrc(e); } - }; + } - /* opens given element */ - function _open(e, newWin) + /* internal used methods */ + function open(e, newWin) { var oldTarget = e.target; if (newWin) { @@ -237,44 +272,44 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) } else if (e.target === "_blank") { e.removeAttribute("target"); } - _click(e); + click(e); e.target = oldTarget; } /* set focus on hint with given number */ - function _focus(n) + function focusHint(n) { /* reset previous focused hint */ - var hint = _getHintByNum(focusNum); + var hint = getHint(focusNum); if (hint) { hint.e.classList.remove(hClassFocus); - hint.e.style.background = bg; - _mouseEvent(hint.e, "mouseout"); + hint.e.style.background = config.bg; + mouseEvent(hint.e, "mouseout"); } focusNum = n; /* mark new hint as focused */ - hint = _getHintByNum(focusNum); + hint = getHint(focusNum); if (hint) { hint.e.classList.add(hClassFocus); - hint.e.style.background = bgf; - var source = _getSrc(hint.e); - _mouseEvent(hint.e, "mouseover"); + hint.e.style.background = config.bgf; + var source = getSrc(hint.e); + mouseEvent(hint.e, "mouseover"); return "OVER:" + (source ? source : ""); } } /* retrieves the hint for given hint number */ - function _getHintByNum(n) + function getHint(n) { - var i = _getHintIdByNum(n); + var i = getHintId(n); return i !== null ? hints[i] : null; } /* retrieves the id of hint with given number */ - function _getHintIdByNum(n) + function getHintId(n) { for (var i = 0; i < hints.length; ++i) { if (hints[i].num === n) { @@ -285,9 +320,9 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) } /* removes hint with given number from hints array */ - function _removeHint(n) + function removeHint(n) { - var i = _getHintIdByNum(n); + var i = getHintId(n); if (i === null) { return; } @@ -300,15 +335,15 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) hints.splice(i, 1); } - function _click(e) + function click(e) { - _mouseEvent(e, "mouseover"); - _mouseEvent(e, "mousedown"); - _mouseEvent(e, "mouseup"); - _mouseEvent(e, "click"); + mouseEvent(e, "mouseover"); + mouseEvent(e, "mousedown"); + mouseEvent(e, "mouseup"); + mouseEvent(e, "click"); } - function _mouseEvent(e, name) + function mouseEvent(e, name) { var evObj = e.ownerDocument.createEvent("MouseEvents"); evObj.initMouseEvent(name, true, true, e.contentWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null); @@ -316,19 +351,19 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) } /* retrieves the url of given element */ - function _getSrc(e) + function getSrc(e) { return e.href || e.src; } /* retrieves the xpath expression according to mode */ - function _getXpath(s) + function getXpath(s) { if (s === undefined) { s = ""; } /* replace $WHAT in xpath to contains(translate(WHAT, 'SEARCH', 'search'), 'search') */ - function _buildQuery(what, x, s) + function buildQuery(what, x, s) { var l, i, parts; l = s.toLowerCase(); @@ -342,12 +377,12 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) return x; } - switch (mode) { + switch (config.mode) { case "l": if (!s) { return "//a[@href] | //*[@onclick or @tabindex or @class='lk' or @role='link' or @role='button'] | //input[not(@type='hidden' or @disabled or @readonly)] | //area[@href] | //textarea[not(@disabled or @readonly)] | //button | //select"; } - return _buildQuery( + return buildQuery( ["@value", ".", "@placeholder"], "//a[@href and $.] | //*[(@onclick or @class='lk' or @role='link' or role='button') and $.] | //input[not(@type='hidden' or @disabled or @readonly) and ($@value or $@placeholder)] | //area[$.] | //textarea[not(@disabled or @readonly) and $.] | //button[$.] | //select[$.]", s @@ -356,7 +391,7 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) if (!s) { return "//input[@type='text'] | //textarea"; } - return _buildQuery( + return buildQuery( ["@value", ".", "@placeholder"], "//input[@type='text' and ($@value or $@placeholder)] | //textarea[$.]", s @@ -365,7 +400,17 @@ function VimbHints(mode, usage, bg, bgf, fg, style, maxHints) if (!s) { return "//img[@src]"; } - return _buildQuery(["@title", "@alt"], "//img[@src and ($@title or $@alt)]", s); + return buildQuery(["@title", "@alt"], "//img[@src and ($@title or $@alt)]", s); } } -}; + + /* the api */ + return { + init: init, + create: create, + update: update, + clear: clear, + fire: fire, + focus: focus + }; +})();