2 (c) 2009 by Leon Winter
3 (c) 2009, 2010 by Hannes Schueller
4 (c) 2010 by Hans-Peter Deifel
10 var currentFocusNum = 1;
12 /* hints[] = [elem, number, text, span, backgroundColor, color] */
15 this.createHints = function(inputText)
17 if (document.getElementsByTagName("body")[0] === null || typeof(document.getElementsByTagName("body")[0]) != "object")
20 var height = window.innerHeight;
21 var width = window.innerWidth;
22 var scrollX = document.defaultView.scrollX;
23 var scrollY = document.defaultView.scrollY;
24 _generateHintContainer();
26 /* prefixing html: will result in namespace error */
28 if (typeof(inputText) == "undefined" || inputText == "") {
29 hinttags = "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href] | //input[not(@type='hidden')] | //a | //area | //iframe | //textarea | //button | //select";
31 /* only elements which match the text entered so far */
32 hinttags = "//*[(@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href) and contains(., '" + inputText + "')] | //input[not(@type='hidden') and contains(., '" + inputText + "')] | //a[contains(., '" + inputText + "')] | //area[contains(., '" + inputText + "')] | //iframe[contains(@name, '" + inputText + "')] | //textarea[contains(., '" + inputText + "')] | //button[contains(@value, '" + inputText + "')] | //select[contains(., '" + inputText + "')]";
36 iterator type isn't suitable here, because: "DOMException NVALID_STATE_ERR:
37 The document has been mutated since the result was returned."
39 var r = document.evaluate(hinttags, document,
41 return "http://www.w3.org/1999/xhtml";
42 }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
44 /* generate basic hint element which will be cloned and updated later */
45 var hintSpan = document.createElement("span");
46 hintSpan.setAttribute("class", "hinting_mode_hint");
47 hintSpan.style.position = "absolute";
48 hintSpan.style.background = "red";
49 hintSpan.style.color = "#fff";
50 hintSpan.style.font = "bold 10px monospace";
51 hintSpan.style.zIndex = "10000000";
53 /* due to the different XPath result type, we will need two counter variables */
57 for (i = 0; i < r.snapshotLength; i++)
59 var elem = r.snapshotItem(i);
60 var rect = elem.getBoundingClientRect();
61 if (!rect || rect.top > height || rect.bottom < 0 || rect.left > width || rect.right < 0 || !(elem.getClientRects()[0]))
63 var computedStyle = document.defaultView.getComputedStyle(elem, null);
64 if (computedStyle.getPropertyValue("visibility") != "visible" || computedStyle.getPropertyValue("display") == "none")
66 var leftpos = Math.max((rect.left + scrollX), scrollX);
67 var toppos = Math.max((rect.top + scrollY), scrollY);
69 /* process elements text */
70 var text = _getTextFromElement(elem);
72 /* making this block DOM compliant */
73 var hint = hintSpan.cloneNode(false);
74 hint.setAttribute("id", "vimprobablehint" + this.hintCount);
75 hint.style.left = leftpos + "px";
76 hint.style.top = toppos + "px";
77 var text = document.createTextNode(this.hintCount + 1);
78 hint.appendChild(text);
79 hintContainer.appendChild(hint);
81 hints.push([elem, this.hintCount, text, hint, elem.style.background, elem.style.color]);
82 /* make the link black to ensure it's readable */
83 elem.style.color = "#000";
84 elem.style.background = "#ff0";
88 if (this.hintCount == 1) {
89 /* just one hinted element - might as well follow it */
94 /* set focus on hint with given number */
95 this.focusHint = function(n)
97 /* reset previous focused hint */
98 var hint = _getHintByNumber(currentFocusNum);
100 hint[0].className = hint[0].className.replace("hinting_mode_hint_focus", "hinting_mode_hint");
101 hint[0].style.background = "#ff0";
106 /* mark new hint as focused */
107 var hint = _getHintByNumber(currentFocusNum);
109 hint[0].className = hint[0].className.replace("hinting_mode_hint", "hinting_mode_hint_focus");
110 hint[0].style.background = "#8f0";
114 this.focusNextHint = function()
116 var index = _getHintIdByNumber(currentFocusNum);
118 if (typeof(hints[index + 1]) != "undefined") {
119 this.focusHint(hints[index + 1][1]);
121 this.focusHint(hints[0][1]);
125 this.focusPreviousHint = function()
127 var index = _getHintIdByNumber(currentFocusNum);
129 if (typeof(hints[index - 1][1]) != "undefined") {
130 this.focusHint(hints[index - 1][1]);
132 this.focusHint(hints[hints.length - 1][1]);
136 this.updateHints = function(n)
138 /* remove none matching hints */
142 if (0 != hint[1].toString().indexOf(n.toString())) {
143 remove.push(hint[1]);
147 for (var i = 0; i < remove.length; ++i) {
148 _removeHint(remove[i]);
151 if (hints.length === 1) {
152 return "fire;" + hints[0][1];
158 this.clearFocus = function()
160 if (document.activeElement && document.activeElement.blur) {
161 document.activeElement.blur();
165 this.clearHints = function()
169 if (typeof(hint[0]) != "undefined") {
170 hint[0].style.background = hint[4];
171 hint[0].style.color = hint[5];
174 hintContainer.parentNode.removeChild(hintContainer);
175 window.onkeyup = null;
178 this.fire = function(n)
181 var n = this.currentFocusNum;
183 var hint = _getHintByNumber(n);
184 if (typeof(hint[0]) == "undefined")
188 var tag = el.nodeName.toLowerCase();
190 if (tag == "iframe" || tag == "frame" || tag == "textarea" || tag == "input" && (el.type == "text" || el.type == "password" || el.type == "checkbox" || el.type == "radio") || tag == "select") {
194 if (!el.onclick && el.href && !el.href.match("/^javascript:/")) {
195 /* send signal to open link */
196 return "open;" + el.href;
198 var evObj = document.createEvent("MouseEvents");
199 evObj.initMouseEvent("click", true, true, window, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null);
200 el.dispatchEvent(evObj);
203 this.focusInput = function()
205 if (document.getElementsByTagName("body")[0] === null || typeof(document.getElementsByTagName("body")[0]) != "object")
208 /* prefixing html: will result in namespace error */
209 var hinttags = "//input[@type='text'] | //input[@type='password'] | //textarea";
210 var r = document.evaluate(hinttags, document,
212 return "http://www.w3.org/1999/xhtml";
213 }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
218 for (i = 0; i < r.snapshotLength; i++) {
219 var elem = r.snapshotItem(i);
221 if (elem.style.display != "none" && elem.style.visibility != "hidden") {
227 if (j == 1 && elem.style.display != "none" && elem.style.visibility != "hidden") {
231 if (elem == document.activeElement) {
236 /* no appropriate field found focused - focus the first one */
237 if (j == 0 && first !== null)
241 function _generateHintContainer()
243 var body = document.getElementsByTagName("body")[0];
244 if (document.getElementById("hint_container")) {
248 hintContainer = document.createElement("div");
249 hintContainer.id = "hint_container";
252 body.appendChild(hintContainer);
255 function _getTextFromElement(el)
257 var tagname = el.tagName.toLowerCase();
258 if (tagname == "input" || tagname == "textarea") {
260 } else if (tagname == "select") {
261 if (el.selectedIndex >= 0) {
262 text = el.item(el.selectedIndex).text;
267 text = el.textContent;
269 return text.toLowerCase();;
272 function _getHintByNumber(n)
274 var index = _getHintIdByNumber(n);
275 if (index !== null) {
281 function _getHintIdByNumber(n)
283 for (var i = 0; i < hints.length; ++i) {
292 function _removeHint(n)
294 var index = _getHintIdByNumber(n);
295 if (index === null) {
298 var hint = hints[index];
300 hint[0].style.background = hint[4];
301 hint[0].style.color = hint[5];
302 hintContainer.removeChild(hint[3]);
304 /* remove hints from all hints */
305 hints.splice(index, 1);