From 10b53e44535a94aa649c6c552d976c743900e348 Mon Sep 17 00:00:00 2001 From: Marco Peereboom Date: Thu, 27 Oct 2011 13:57:39 -0500 Subject: [PATCH] rewrite hinting code completely Import new vimprobable JavaScript code. Make hinting mode now print what has been typed. It works along the same lines as '/' and '?' in search mode but it uses '.' and ',' instead. Focus on default input box and unfocus on default input box when ESC is hit. This also adds i and ESC to switch between input/command mode. --- hinting.js | 663 ++++++++++++++++++++++++++++++++++++++------------------- input-focus.js | 103 +++++---- xxxterm.c | 334 ++++++++++++++--------------- 3 files changed, 650 insertions(+), 450 deletions(-) rewrite hinting.js (87%) rewrite input-focus.js (63%) diff --git a/hinting.js b/hinting.js dissimilarity index 87% index f5a5f52..135c4dd 100644 --- a/hinting.js +++ b/hinting.js @@ -1,223 +1,440 @@ -/* - (c) 2009 by Leon Winter - (c) 2009, 2010 by Hannes Schueller - (c) 2010 by Hans-Peter Deifel - - Copyright (c) 2009 Leon Winter - Copyright (c) 2009, 2010 Hannes Schueller - Copyright (c) 2009, 2010 Matto Fransen - Copyright (c) 2010 Hans-Peter Deifel - Copyright (c) 2010 Thomas Adam - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -function vimprobable_clearfocus() { - if(document.activeElement && document.activeElement.blur) - document.activeElement.blur(); -} - -function vimprobable_show_hints(inputText) { - if (document.getElementsByTagName("body")[0] !== null && typeof(document.getElementsByTagName("body")[0]) == "object") { - var height = window.innerHeight; - var width = window.innerWidth; - var scrollX = document.defaultView.scrollX; - var scrollY = document.defaultView.scrollY; - /* prefixing html: will result in namespace error */ - var hinttags; - if (typeof(inputText) == "undefined" || inputText == "") { - 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"; - } else { - /* only elements which match the text entered so far */ - 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 + "')]"; - } - - /* iterator type isn't suitable here, because: "DOMException NVALID_STATE_ERR: The document has been mutated since the result was returned." */ - var r = document.evaluate(hinttags, document, - function(p) { - return 'http://www.w3.org/1999/xhtml'; - }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - div = document.createElement("div"); - /* due to the different XPath result type, we will need two counter variables */ - vimprobable_j = 0; - var i; - vimprobable_a = []; - vimprobable_colors = []; - vimprobable_backgrounds = []; - for (i = 0; i < r.snapshotLength; i++) - { - var elem = r.snapshotItem(i); - rect = elem.getBoundingClientRect(); - if (!rect || rect.top > height || rect.bottom < 0 || rect.left > width || rect.right < 0 || !(elem.getClientRects()[0])) - continue; - var computedStyle = document.defaultView.getComputedStyle(elem, null); - if (computedStyle.getPropertyValue("visibility") != "visible" || computedStyle.getPropertyValue("display") == "none") - continue; - var leftpos = Math.max((rect.left + scrollX), scrollX); - var toppos = Math.max((rect.top + scrollY), scrollY); - vimprobable_a.push(elem); - /* making this block DOM compliant */ - var hint = document.createElement("span"); - hint.setAttribute("class", "hinting_mode_hint"); - hint.setAttribute("id", "vimprobablehint" + vimprobable_j); - hint.style.position = "absolute"; - hint.style.left = leftpos + "px"; - hint.style.top = toppos + "px"; - hint.style.background = "red"; - hint.style.color = "#fff"; - hint.style.font = "bold 10px monospace"; - hint.style.zIndex = "99"; - var text = document.createTextNode(vimprobable_j + 1); - hint.appendChild(text); - div.appendChild(hint); - /* remember site-defined colour of this element */ - vimprobable_colors[vimprobable_j] = elem.style.color; - vimprobable_backgrounds[vimprobable_j] = elem.style.background; - /* make the link black to ensure it's readable */ - elem.style.color = "#000"; - elem.style.background = "#ff0"; - vimprobable_j++; - } - i = 0; - while (typeof(vimprobable_a[i]) != "undefined") { - vimprobable_a[i].className += " hinting_mode_hint"; - i++; - } - document.getElementsByTagName("body")[0].appendChild(div); - vimprobable_clearfocus(); - vimprobable_h = null; - if (i == 1) { - /* just one hinted element - might as well follow it */ - return vimprobable_fire(1); - } - } -} -function vimprobable_fire(n) -{ - if (typeof(vimprobable_a[n - 1]) != "undefined") { - el = vimprobable_a[n - 1]; - tag = el.nodeName.toLowerCase(); - vimprobable_clear(); - if(tag == "iframe" || tag == "frame" || tag == "textarea" || tag == "input" && (el.type == "text" || el.type == "password" || el.type == "checkbox" || el.type == "radio") || tag == "select") { - el.focus(); - if (tag == "textarea" || tag == "input") - console.log('insertmode_on'); - } else { - if (el.onclick) { - var evObj = document.createEvent('MouseEvents'); - evObj.initMouseEvent('click', true, true, window, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null); - el.dispatchEvent(evObj); - } else if (el.href) { - if (el.href.match(/^javascript:/)) { - var evObj = document.createEvent('MouseEvents'); - evObj.initMouseEvent('click', true, true, window, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null); - el.dispatchEvent(evObj); - } else { - /* send signal to open link */ - return "open;" + el.href; - } - } else { - var evObj = document.createEvent('MouseEvents'); - evObj.initMouseEvent('click', true, true, window, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null); - el.dispatchEvent(evObj); - } - } - } -} -function vimprobable_cleanup() -{ - for(e in vimprobable_a) { - if (typeof(vimprobable_a[e].className) != "undefined") { - vimprobable_a[e].className = vimprobable_a[e].className.replace(/hinting_mode_hint/,''); - /* reset to site-defined colour */ - vimprobable_a[e].style.color = vimprobable_colors[e]; - vimprobable_a[e].style.background = vimprobable_backgrounds[e]; - } - } - div.parentNode.removeChild(div); - window.onkeyup = null; -} -function vimprobable_clear() -{ - vimprobable_cleanup(); - console.log("hintmode_off") -} - -function vimprobable_update_hints(n) -{ - if(vimprobable_h != null) { - vimprobable_h.className = vimprobable_h.className.replace("_focus",""); - vimprobable_h.style.background = "#ff0"; - } - if (vimprobable_j - 1 < n * 10 && typeof(vimprobable_a[n - 1]) != "undefined") { - /* return signal to follow the link */ - return "fire;" + n; - } else { - if (typeof(vimprobable_a[n - 1]) != "undefined") { - (vimprobable_h = vimprobable_a[n - 1]).className = vimprobable_a[n - 1].className.replace("hinting_mode_hint", "hinting_mode_hint_focus"); - vimprobable_h.style.background = "#8f0"; - } - } -} - -function vimprobable_focus_input() -{ - if (document.getElementsByTagName("body")[0] !== null && typeof(document.getElementsByTagName("body")[0]) == "object") { - /* prefixing html: will result in namespace error */ - var hinttags = "//input[@type='text'] | //input[@type='password'] | //textarea"; - var r = document.evaluate(hinttags, document, - function(p) { - return 'http://www.w3.org/1999/xhtml'; - }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - var i; - var j = 0; - var k = 0; - var first = null; - for (i = 0; i < r.snapshotLength; i++) { - var elem = r.snapshotItem(i); - if (k == 0) { - if (elem.style.display != "none" && elem.style.visibility != "hidden") { - first = elem; - } else { - k--; - } - } - if (j == 1 && elem.style.display != "none" && elem.style.visibility != "hidden") { - elem.focus(); - var tag = elem.nodeName.toLowerCase(); - if (tag == "textarea" || tag == "input") - console.log('insertmode_on'); - break; - } else { - if (elem == document.activeElement) - j = 1; - } - k++; - } - if (j == 0) { - /* no appropriate field found focused - focus the first one */ - if (first !== null) { - first.focus(); - var tag = elem.nodeName.toLowerCase(); - if (tag == "textarea" || tag == "input") - console.log('insertmode_on'); - } - } - } -} +/* +Copyright (c) 2009 Leon Winter +Copyright (c) 2009-2011 Hannes Schueller +Copyright (c) 2009-2010 Matto Fransen +Copyright (c) 2010-2011 Hans-Peter Deifel +Copyright (c) 2010-2011 Thomas Adam +Copyright (c) 2011 Albert Kim +Copyright (c) 2011 Daniel Carl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +function Hints() { + var config = { + maxAllowedHints: 500, + hintCss: "z-index:100000;font-family:monospace;font-size:10px;" + + "font-weight:bold;color:white;background-color:red;" + + "padding:0px 1px;position:absolute;", + hintClass: "hinting_mode_hint", + hintClassFocus: "hinting_mode_hint_focus", + elemBackground: "#ff0", + elemBackgroundFocus: "#8f0", + elemColor: "#000" + }; + + var hintContainer; + var currentFocusNum = 1; + var hints = []; + var mode; + + this.createHints = function(inputText, hintMode) + { + if (hintMode) { + mode = hintMode; + } + + var topwin = window; + var top_height = topwin.innerHeight; + var top_width = topwin.innerWidth; + var xpath_expr; + + var hintCount = 0; + this.clearHints(); + + function helper (win, offsetX, offsetY) { + var doc = win.document; + + var win_height = win.height; + var win_width = win.width; + + /* 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; + + var scrollX = win.scrollX; + var scrollY = win.scrollY; + + hintContainer = doc.createElement("div"); + hintContainer.id = "hint_container"; + + if (typeof(inputText) == "undefined" || inputText == "") { + xpath_expr = "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href] | //input[not(@type='hidden')] | //a[href] | //area | //textarea | //button | //select"; + } else { + xpath_expr = "//*[(@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[@href and contains(., '" + inputText + "')] | //area[contains(., '" + inputText + "')] | //textarea[contains(., '" + inputText + "')] | //button[contains(@value, '" + inputText + "')] | //select[contains(., '" + inputText + "')]"; + } + + var res = doc.evaluate(xpath_expr, 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", config.hintClass); + hintSpan.style.cssText = config.hintCss; + + /* due to the different XPath result type, we will need two counter variables */ + var rect, elem, text, node, show_text; + for (var i = 0; i < res.snapshotLength; i++) + { + if (hintCount >= config.maxAllowedHints) + break; + + elem = res.snapshotItem(i); + rect = elem.getBoundingClientRect(); + if (!rect || rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY) + continue; + + var style = topwin.getComputedStyle(elem, ""); + if (style.display == "none" || style.visibility != "visible") + continue; + + var leftpos = Math.max((rect.left + scrollX), scrollX); + var toppos = Math.max((rect.top + scrollY), scrollY); + + /* making this block DOM compliant */ + var hint = hintSpan.cloneNode(false); + hint.setAttribute("id", "vimprobablehint" + hintCount); + hint.style.left = leftpos + "px"; + hint.style.top = toppos + "px"; + text = doc.createTextNode(hintCount + 1); + hint.appendChild(text); + + hintContainer.appendChild(hint); + hintCount++; + hints.push({ + elem: elem, + number: hintCount, + span: hint, + background: elem.style.background, + foreground: elem.style.color} + ); + + /* make the link black to ensure it's readable */ + elem.style.color = config.elemColor; + elem.style.background = config.elemBackground; + } + + doc.documentElement.appendChild(hintContainer); + + /* 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) { + elem = frames[i]; + rect = elem.getBoundingClientRect(); + if (!elem.contentWindow || !rect || rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY) + continue; + helper(elem.contentWindow, offsetX + rect.left, offsetY + rect.top); + } + } + } + + helper(topwin, 0, 0); + + this.clearFocus(); + this.focusHint(1); + if (hintCount == 1) { + /* just one hinted element - might as well follow it */ + return this.fire(1); + } + }; + + /* set focus on hint with given number */ + this.focusHint = function(n) + { + /* reset previous focused hint */ + var hint = _getHintByNumber(currentFocusNum); + if (hint !== null) { + hint.elem.className = hint.elem.className.replace(config.hintClassFocus, config.hintClass); + hint.elem.style.background = config.elemBackground; + } + + currentFocusNum = n; + + /* mark new hint as focused */ + var hint = _getHintByNumber(currentFocusNum); + if (hint !== null) { + hint.elem.className = hint.elem.className.replace(config.hintClass, config.hintClassFocus); + hint.elem.style.background = config.elemBackgroundFocus; + } + }; + + /* set focus to next avaiable hint */ + this.focusNextHint = function() + { + var index = _getHintIdByNumber(currentFocusNum); + + if (typeof(hints[index + 1]) != "undefined") { + this.focusHint(hints[index + 1].number); + } else { + this.focusHint(hints[0].number); + } + }; + + /* set focus to previous avaiable hint */ + this.focusPreviousHint = function() + { + var index = _getHintIdByNumber(currentFocusNum); + if (index != 0 && typeof(hints[index - 1].number) != "undefined") { + this.focusHint(hints[index - 1].number); + } else { + this.focusHint(hints[hints.length - 1].number); + } + }; + + /* filters hints matching given number */ + this.updateHints = function(n) + { + if (n == 0) { + return this.createHints(); + } + /* remove none matching hints */ + var remove = []; + for (var i = 0; i < hints.length; ++i) { + var hint = hints[i]; + if (0 != hint.number.toString().indexOf(n.toString())) { + remove.push(hint.number); + } + } + + for (var i = 0; i < remove.length; ++i) { + _removeHint(remove[i]); + } + + if (hints.length === 1) { + return this.fire(hints[0].number); + } else { + return this.focusHint(n); + } + }; + + this.clearFocus = function() + { + if (document.activeElement && document.activeElement.blur) { + document.activeElement.blur(); + } + }; + + /* remove all hints and set previous style to them */ + this.clearHints = function() + { + if (hints.length == 0) { + return; + } + for (var i = 0; i < hints.length; ++i) { + var hint = hints[i]; + if (typeof(hint.elem) != "undefined") { + hint.elem.style.background = hint.background; + hint.elem.style.color = hint.foreground; + hint.span.parentNode.removeChild(hint.span); + } + } + hints = []; + hintContainer.parentNode.removeChild(hintContainer); + window.onkeyup = null; + }; + + /* fires the modeevent on hint with given number */ + this.fire = function(n) + { + var doc, result; + if (!n) { + var n = currentFocusNum; + } + var hint = _getHintByNumber(n); + if (typeof(hint.elem) == "undefined") + return "done;"; + + var el = hint.elem; + var tag = el.nodeName.toLowerCase(); + + this.clearHints(); + + if (tag == "iframe" || tag == "frame" || tag == "textarea" || tag == "input" && (el.type == "text" || el.type == "password" || el.type == "checkbox" || el.type == "radio") || tag == "select") { + el.focus(); + if (tag == "input" || tag == "textarea") { + return "insert;" + } + return "done;"; + } + + switch (mode) + { + case "f": result = _open(el); break; + case "F": result = _openNewWindow(el); break; + default: result = _getElemtSource(el); + } + + return result; + }; + + this.focusInput = function() + { + if (document.getElementsByTagName("body")[0] === null || typeof(document.getElementsByTagName("body")[0]) != "object") + return; + + /* prefixing html: will result in namespace error */ + var hinttags = "//input[@type='text'] | //input[@type='password'] | //textarea"; + var r = document.evaluate(hinttags, document, + function(p) { + return "http://www.w3.org/1999/xhtml"; + }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + var i; + var j = 0; + var k = 0; + var first = null; + for (i = 0; i < r.snapshotLength; i++) { + var elem = r.snapshotItem(i); + if (k == 0) { + if (elem.style.display != "none" && elem.style.visibility != "hidden") { + first = elem; + } else { + k--; + } + } + if (j == 1 && elem.style.display != "none" && elem.style.visibility != "hidden") { + elem.focus(); + var tag = elem.nodeName.toLowerCase(); + if (tag == "textarea" || tag == "input") { + return "insert;"; + } + break; + } + if (elem == document.activeElement) { + j = 1; + } + k++; + } + /* no appropriate field found focused - focus the first one */ + if (j == 0 && first !== null) { + first.focus(); + var tag = elem.nodeName.toLowerCase(); + if (tag == "textarea" || tag == "input") { + return "insert;"; + } + } + }; + + /* retrieves text content fro given element */ + function _getTextFromElement(el) + { + if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) { + text = el.value; + } else if (el instanceof HTMLSelectElement) { + if (el.selectedIndex >= 0) { + text = el.item(el.selectedIndex).text; + } else{ + text = ""; + } + } else { + text = el.textContent; + } + return text.toLowerCase();; + } + + /* retrieves the hint for given hint number */ + function _getHintByNumber(n) + { + var index = _getHintIdByNumber(n); + if (index !== null) { + return hints[index]; + } + return null; + } + + /* retrieves the id of hint with given number */ + function _getHintIdByNumber(n) + { + for (var i = 0; i < hints.length; ++i) { + var hint = hints[i]; + if (hint.number === n) { + return i; + } + } + return null; + } + + /* removes hint with given number from hints array */ + function _removeHint(n) + { + var index = _getHintIdByNumber(n); + if (index === null) { + return; + } + var hint = hints[index]; + if (hint.number === n) { + hint.elem.style.background = hint.background; + hint.elem.style.color = hint.foreground; + hint.span.parentNode.removeChild(hint.span); + + /* remove hints from all hints */ + hints.splice(index, 1); + } + } + + /* opens given element */ + function _open(elem) + { + if (elem.target == "_blank") { + elem.removeAttribute("target"); + } + _clickElement(elem); + return "done;"; + } + + /* opens given element into new window */ + function _openNewWindow(elem) + { + var oldTarget = elem.target; + + /* set target to open in new window */ + elem.target = "_blank"; + _clickElement(elem); + elem.target = oldTarget; + + return "done;"; + } + + /* fire moudedown and click event on given element */ + function _clickElement(elem) + { + doc = elem.ownerDocument; + view = elem.contentWindow; + + var evObj = doc.createEvent("MouseEvents"); + evObj.initMouseEvent("mousedown", true, true, view, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null); + elem.dispatchEvent(evObj); + + var evObj = doc.createEvent("MouseEvents"); + evObj.initMouseEvent("click", true, true, view, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null); + elem.dispatchEvent(evObj); + } + + /* retrieves the url of given element */ + function _getElemtSource(elem) + { + var url = elem.href || elem.src; + return url; + } +} +hints = new Hints(); diff --git a/input-focus.js b/input-focus.js dissimilarity index 63% index bc25ed4..f8d4212 100644 --- a/input-focus.js +++ b/input-focus.js @@ -1,53 +1,50 @@ -/* - (c) 2009 by Leon Winter - (c) 2009, 2010 by Hannes Schueller - (c) 2010 by Hans-Peter Deifel - - Copyright (c) 2009 Leon Winter - Copyright (c) 2009, 2010 Hannes Schueller - Copyright (c) 2009, 2010 Matto Fransen - Copyright (c) 2010 Hans-Peter Deifel - Copyright (c) 2010 Thomas Adam - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -function vimprobable_v(e, y) { - t = e.nodeName.toLowerCase(); - if((t == 'input' && /^(text|password|checkbox|radio)$/.test(e.type)) - || /^(select|textarea)$/.test(t) - || e.contentEditable == 'true') - console.log('insertmode_'+(y=='focus'?'on':'off')); -} - -if(document.activeElement) - vimprobable_v(document.activeElement,'focus'); - -vimprobable_m=['focus','blur']; - -if (document.getElementsByTagName("body")[0] !== null && typeof(document.getElementsByTagName("body")[0]) == "object") { - for(i in vimprobable_m) - document.getElementsByTagName('body')[0].addEventListener(vimprobable_m[i], function(x) { - vimprobable_v(x.target,x.type); - }, true); -} - -self.onunload = function() { - vimprobable_v(document.activeElement, ''); -}; +/* +Copyright (c) 2009 Leon Winter +Copyright (c) 2009-2011 Hannes Schueller +Copyright (c) 2009-2010 Matto Fransen +Copyright (c) 2010-2011 Hans-Peter Deifel +Copyright (c) 2010-2011 Thomas Adam +Copyright (c) 2011 Albert Kim +Copyright (c) 2011 Daniel Carl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +function vimprobable_v(e, y) { + t = e.nodeName.toLowerCase(); + if((t == 'input' && /^(text|password|checkbox|radio)$/.test(e.type)) + || /^(select|textarea)$/.test(t) + || e.contentEditable == 'true') + console.log('insertmode_'+(y=='focus'?'on':'off')); +} + +if(document.activeElement) + vimprobable_v(document.activeElement,'focus'); + +vimprobable_m=['focus','blur']; + +if (document.getElementsByTagName("body")[0] !== null && typeof(document.getElementsByTagName("body")[0]) == "object") { + for(i in vimprobable_m) + document.getElementsByTagName('body')[0].addEventListener(vimprobable_m[i], function(x) { + vimprobable_v(x.target,x.type); + }, true); +} + +self.onunload = function() { + vimprobable_v(document.activeElement, ''); +}; diff --git a/xxxterm.c b/xxxterm.c index 7ba1074..ce72408 100644 --- a/xxxterm.c +++ b/xxxterm.c @@ -94,8 +94,12 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL; javascript.h borrowed from vimprobable2 under the following license: Copyright (c) 2009 Leon Winter -Copyright (c) 2009 Hannes Schueller -Copyright (c) 2009 Matto Fransen +Copyright (c) 2009-2011 Hannes Schueller +Copyright (c) 2009-2010 Matto Fransen +Copyright (c) 2010-2011 Hans-Peter Deifel +Copyright (c) 2010-2011 Thomas Adam +Copyright (c) 2011 Albert Kim +Copyright (c) 2011 Daniel Carl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -244,13 +248,7 @@ struct tab { #endif /* hints */ int hints_on; - int hint_mode; -#define XT_HINT_NONE (0) -#define XT_HINT_NUMERICAL (1) -#define XT_HINT_ALPHANUM (2) int new_tab; - char hint_buf[128]; - char hint_num[128]; /* custom stylesheet */ int styled; @@ -536,6 +534,9 @@ struct karg { #define XT_HINT_NEWTAB (1<<0) +#define XT_MODE_INSERT (0) +#define XT_MODE_COMMAND (1) + #define XT_TABS_NORMAL 0 #define XT_TABS_COMPACT 1 @@ -2374,29 +2375,31 @@ js_ref_to_string(JSContextRef context, JSValueRef ref) void disable_hints(struct tab *t) { - bzero(t->hint_buf, sizeof t->hint_buf); - bzero(t->hint_num, sizeof t->hint_num); - run_script(t, "vimprobable_clear()"); + DNPRINTF(XT_D_JS, "%s\n", __func__); + + run_script(t, "hints.clearHints();"); t->hints_on = 0; - t->hint_mode = XT_HINT_NONE; t->new_tab = 0; } void enable_hints(struct tab *t) { - bzero(t->hint_buf, sizeof t->hint_buf); - run_script(t, "vimprobable_show_hints()"); + DNPRINTF(XT_D_JS, "%s\n", __func__); + + run_script(t, JS_HINTING); + + if (t->new_tab) + run_script(t, "hints.createHints('', 'F');"); + else + run_script(t, "hints.createHints('', 'f');"); t->hints_on = 1; - t->hint_mode = XT_HINT_NONE; } -#define XT_JS_OPEN ("open;") -#define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN)) -#define XT_JS_FIRE ("fire;") -#define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE)) -#define XT_JS_FOUND ("found;") -#define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND)) +#define XT_JS_DONE ("done;") +#define XT_JS_DONE_LEN (strlen(XT_JS_DONE)) +#define XT_JS_INSERT ("insert;") +#define XT_JS_INSERT_LEN (strlen(XT_JS_INSERT)) int run_script(struct tab *t, char *s) @@ -2405,7 +2408,7 @@ run_script(struct tab *t, char *s) WebKitWebFrame *frame; JSStringRef str; JSValueRef val, exception; - char *es, buf[128]; + char *es; DNPRINTF(XT_D_JS, "run_script: tab %d %s\n", t->tab_id, s == (char *)JS_HINTING ? "JS_HINTING" : s); @@ -2422,36 +2425,24 @@ run_script(struct tab *t, char *s) if (val == NULL) { es = js_ref_to_string(ctx, exception); DNPRINTF(XT_D_JS, "run_script: exception %s\n", es); - g_free(es); + if (es) { + show_oops(t, "script exception: %s", es); + g_free(es); + } return (1); } else { - /* if set open in new tab */ - if (t->new_tab) - t->ctrl_click = 1; - es = js_ref_to_string(ctx, val); - DNPRINTF(XT_D_JS, "run_script: val %s\n", es); - - /* handle return value right here */ - if (!strncmp(es, XT_JS_OPEN, XT_JS_OPEN_LEN)) { - disable_hints(t); - marks_clear(t); - load_uri(t, &es[XT_JS_OPEN_LEN]); - } - - if (!strncmp(es, XT_JS_FIRE, XT_JS_FIRE_LEN)) { - snprintf(buf, sizeof buf, "vimprobable_fire(%s)", - &es[XT_JS_FIRE_LEN]); - run_script(t, buf); - disable_hints(t); - } - - if (!strncmp(es, XT_JS_FOUND, XT_JS_FOUND_LEN)) { - if (atoi(&es[XT_JS_FOUND_LEN]) == 0) - disable_hints(t); +#if 0 + /* return values */ + if (!strncmp(es, XT_JS_DONE, XT_JS_DONE_LEN)) + ; /* do nothing */ + if (!strncmp(es, XT_JS_INSERT, XT_JS_INSERT_LEN)) + ; /* do nothing */ +#endif + if (es) { + DNPRINTF(XT_D_JS, "run_script: val %s\n", es); + g_free(es); } - - g_free(es); } return (0); @@ -2463,12 +2454,11 @@ hint(struct tab *t, struct karg *args) DNPRINTF(XT_D_JS, "hint: tab %d args %d\n", t->tab_id, args->i); - if (args->i == XT_HINT_NEWTAB) - t->new_tab = 1; - - if (t->hints_on == 0) + if (t->hints_on == 0) { + if (args->i == XT_HINT_NEWTAB) + t->new_tab = 1; enable_hints(t); - else + } else disable_hints(t); return (0); @@ -4806,11 +4796,22 @@ movetab(struct tab *t, struct karg *args) int cmd_prefix = 0; int +command_mode(struct tab *t, struct karg *args) +{ + if (args->i == XT_MODE_COMMAND) + run_script(t, "hints.clearFocus();"); + else + run_script(t, "hints.focusInput();"); + return (XT_CB_HANDLED); +} + +int command(struct tab *t, struct karg *args) { char *s = NULL, *ss = NULL; GdkColor color; const gchar *uri; + struct karg a; if (t == NULL || args == NULL) { show_oops(NULL, "command invalid parameters"); @@ -4833,6 +4834,18 @@ command(struct tab *t, struct karg *args) cmd_prefix = 0; } break; + case '.': + bzero(&a, sizeof a); + a.i = 0; + hint(t, &a); + s = "."; + break; + case ',': + bzero(&a, sizeof a); + a.i = XT_HINT_NEWTAB; + hint(t, &a); + s = ","; + break; case XT_CMD_OPEN: s = ":open "; break; @@ -5727,6 +5740,8 @@ struct key_binding { guint key; TAILQ_ENTRY(key_binding) entry; /* in bss so no need to init */ } keys[] = { + { "command_mode", 0, 0, GDK_Escape }, + { "insert_mode", 0, 0, GDK_i }, { "cookiejar", MOD1, 0, GDK_j }, { "downloadmgr", MOD1, 0, GDK_d }, { "history", MOD1, 0, GDK_h }, @@ -5757,10 +5772,12 @@ struct key_binding { /* hinting */ { "hinting", 0, 0, GDK_f }, + { "hinting", 0, 0, GDK_period }, { "hinting_newtab", SHFT, 0, GDK_F }, + { "hinting_newtab", 0, 0, GDK_comma }, /* custom stylesheet */ - { "userstyle", 0, 0, GDK_i }, + { "userstyle", 0, 0, GDK_s }, /* navigation */ { "goback", 0, 0, GDK_BackSpace }, @@ -6010,9 +6027,13 @@ struct cmd { int arg; int type; } cmds[] = { + { "command_mode", 0, command_mode, XT_MODE_COMMAND, 0 }, + { "insert_mode", 0, command_mode, XT_MODE_INSERT, 0 }, { "command", 0, command, ':', 0 }, { "search", 0, command, '/', 0 }, { "searchb", 0, command, '?', 0 }, + { "hinting", 0, command, '.', 0 }, + { "hinting_newtab", 0, command, ',', 0 }, { "togglesrc", 0, toggle_src, 0, 0 }, /* yanking and pasting */ @@ -7095,6 +7116,8 @@ notify_load_status_cb(WebKitWebView* wview, GParamSpec* pspec, struct tab *t) /* take focus if we are visible */ focus_webview(t); t->focus_wv = 1; + + marks_clear(t); #ifdef USE_THREAD /* kill color thread */ t->thread = NULL; @@ -7202,6 +7225,8 @@ void webview_load_finished_cb(WebKitWebView *wv, WebKitWebFrame *wf, struct tab *t) { run_script(t, JS_HINTING); + if (t->tab_id == gtk_notebook_get_current_page(notebook)) + run_script(t, "hints.focusInput();"); } void @@ -7241,7 +7266,7 @@ webview_npd_cb(WebKitWebView *wv, WebKitWebFrame *wf, if (parse_xtp_url(t, uri)) return (TRUE); - if (t->ctrl_click) { + if ((t->hints_on && t->new_tab) || t->ctrl_click) { t->ctrl_click = 0; create_new_tab(uri, NULL, ctrl_click_focus, -1); webkit_web_policy_decision_ignore(pd); @@ -7979,8 +8004,7 @@ handle_keypress(struct tab *t, GdkEventKey *e, int entry) int wv_keypress_after_cb(GtkWidget *w, GdkEventKey *e, struct tab *t) { - char s[2], buf[128]; - const char *errstr = NULL; + char s[2]; /* don't use w directly; use t->whatever instead */ @@ -7993,115 +8017,7 @@ wv_keypress_after_cb(GtkWidget *w, GdkEventKey *e, struct tab *t) e->keyval, e->state, t); if (t->hints_on) { - /* ESC */ - if (CLEAN(e->state) == 0 && e->keyval == GDK_Escape) { - disable_hints(t); - return (XT_CB_HANDLED); - } - - /* RETURN */ - if (CLEAN(e->state) == 0 && e->keyval == GDK_Return) { - if (errstr) { - /* we have a string */ - } else { - /* we have a number */ - snprintf(buf, sizeof buf, - "vimprobable_fire(%s)", t->hint_num); - run_script(t, buf); - } - disable_hints(t); - } - - /* BACKSPACE */ - /* XXX unfuck this */ - if (CLEAN(e->state) == 0 && e->keyval == GDK_BackSpace) { - if (t->hint_mode == XT_HINT_NUMERICAL) { - /* last input was numerical */ - int l; - l = strlen(t->hint_num); - if (l > 0) { - l--; - if (l == 0) { - disable_hints(t); - enable_hints(t); - } else { - t->hint_num[l] = '\0'; - goto num; - } - } - } else if (t->hint_mode == XT_HINT_ALPHANUM) { - /* last input was alphanumerical */ - int l; - l = strlen(t->hint_buf); - if (l > 0) { - l--; - if (l == 0) { - disable_hints(t); - enable_hints(t); - } else { - t->hint_buf[l] = '\0'; - goto anum; - } - } - } else { - /* bogus */ - disable_hints(t); - } - } - - /* numerical input */ - if (CLEAN(e->state) == 0 && - ((e->keyval >= GDK_0 && e->keyval <= GDK_9) || - (e->keyval >= GDK_KP_0 && e->keyval <= GDK_KP_9))) { - snprintf(s, sizeof s, "%c", e->keyval); - strlcat(t->hint_num, s, sizeof t->hint_num); - DNPRINTF(XT_D_JS, "wv_keypress_after_cb: num %s\n", - t->hint_num); -num: - if (errstr) { - DNPRINTF(XT_D_JS, "wv_keypress_after_cb: " - "invalid link number\n"); - disable_hints(t); - } else { - snprintf(buf, sizeof buf, - "vimprobable_update_hints(%s)", - t->hint_num); - t->hint_mode = XT_HINT_NUMERICAL; - run_script(t, buf); - } - - /* empty the counter buffer */ - bzero(t->hint_buf, sizeof t->hint_buf); - return (XT_CB_HANDLED); - } - - /* alphanumerical input */ - if ((CLEAN(e->state) == 0 && e->keyval >= GDK_a && - e->keyval <= GDK_z) || - (CLEAN(e->state) == GDK_SHIFT_MASK && - e->keyval >= GDK_A && e->keyval <= GDK_Z) || - (CLEAN(e->state) == 0 && ((e->keyval >= GDK_0 && - e->keyval <= GDK_9) || - ((e->keyval >= GDK_KP_0 && e->keyval <= GDK_KP_9) && - (t->hint_mode != XT_HINT_NUMERICAL))))) { - snprintf(s, sizeof s, "%c", e->keyval); - strlcat(t->hint_buf, s, sizeof t->hint_buf); - DNPRINTF(XT_D_JS, "wv_keypress_after_cb: alphanumerical" - " %s\n", t->hint_buf); -anum: - snprintf(buf, sizeof buf, "vimprobable_cleanup()"); - run_script(t, buf); - - snprintf(buf, sizeof buf, - "vimprobable_show_hints('%s')", t->hint_buf); - t->hint_mode = XT_HINT_ALPHANUM; - run_script(t, buf); - - /* empty the counter buffer */ - bzero(t->hint_num, sizeof t->hint_num); - return (XT_CB_HANDLED); - } - + /* XXX make sure cmd entry is enabled */ return (XT_CB_HANDLED); } else { /* prefix input*/ @@ -8130,12 +8046,52 @@ wv_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) } gboolean +hint_continue(struct tab *t) +{ + const gchar *c = gtk_entry_get_text(GTK_ENTRY(t->cmd)); + char *s; + const gchar *errstr = NULL; + gboolean rv = TRUE; + long long i; + + if (!(c[0] == '.' || c[0] == ',')) + goto done; + if (strlen(c) == 1) { + /* XXX should not happen */ + rv = TRUE; + goto done; + } + + if (isdigit(c[1])) { + /* numeric input */ + i = strtonum(&c[1], 1, 4096, &errstr); + if (errstr) { + show_oops(t, "invalid numerical hint %s", &c[1]); + goto done; + } + s = g_strdup_printf("hints.updateHints(%lld);", i); + run_script(t, s); + g_free(s); + } else { + /* alphanumeric input */ + s = g_strdup_printf("hints.createHints('%s', '%c');", + &c[1], c[0] == '.' ? 'f' : 'F'); + run_script(t, s); + g_free(s); + } + + rv = TRUE; +done: + return (rv); +} + +gboolean search_continue(struct tab *t) { const gchar *c = gtk_entry_get_text(GTK_ENTRY(t->cmd)); gboolean rv = FALSE; - if (c[0] == ':') + if (c[0] == ':' || c[0] == '.' || c[0] == ',') goto done; if (strlen(c) == 1) { webkit_web_view_unmark_text_matches(t->wv); @@ -8198,6 +8154,13 @@ cmd_keyrelease_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) DNPRINTF(XT_D_CMD, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n", e->keyval, e->state, t); + /* hinting */ + if (!(e->keyval == GDK_Tab || e->keyval == GDK_ISO_Left_Tab)) { + if (hint_continue(t) == FALSE) + goto done; + } + + /* search */ if (search_continue(t) == FALSE) goto done; @@ -8558,6 +8521,7 @@ cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) { int rv = XT_CB_HANDLED; const gchar *c = gtk_entry_get_text(w); + char *s; if (t == NULL) { show_oops(NULL, "cmd_keypress_cb parameters"); @@ -8570,7 +8534,8 @@ cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) /* sanity */ if (c == NULL) e->keyval = GDK_Escape; - else if (!(c[0] == ':' || c[0] == '/' || c[0] == '?')) + else if (!(c[0] == ':' || c[0] == '/' || c[0] == '?' || + c[0] == '.' || c[0] == ',')) e->keyval = GDK_Escape; if (e->keyval != GDK_Tab && e->keyval != GDK_Shift_L && @@ -8581,11 +8546,14 @@ cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) case GDK_Tab: if (c[0] == ':') cmd_complete(t, (char *)&c[1], 1); + else if (c[0] == '.' || c[0] == ',') + run_script(t, "hints.focusNextHint();"); goto done; case GDK_ISO_Left_Tab: if (c[0] == ':') cmd_complete(t, (char *)&c[1], -1); - + else if (c[0] == '.' || c[0] == ',') + run_script(t, "hints.focusPreviousHint();"); goto done; case GDK_Down: if (c[0] != ':') { @@ -8620,8 +8588,19 @@ cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) goto done; case GDK_BackSpace: - if (!(!strcmp(c, ":") || !strcmp(c, "/") || !strcmp(c, "?"))) + if (!(!strcmp(c, ":") || !strcmp(c, "/") || !strcmp(c, "?") || + !strcmp(c, ".") || !strcmp(c, ","))) { + /* see if we are doing hinting and reset it */ + if (c[0] == '.' || c[0] == ',') { + /* recreate hints */ + s = g_strdup_printf("hints.createHints('', " + "'%c');", c[0] == '.' ? 'f' : 'F'); + run_script(t, s); + g_free(s); + } break; + } + /* FALLTHROUGH */ case GDK_Escape: hide_cmd(t); @@ -8630,6 +8609,8 @@ cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t) /* cancel search */ if (c != NULL && (c[0] == '/' || c[0] == '?')) webkit_web_view_unmark_text_matches(t->wv); + + /* no need to cancel hints */ goto done; } @@ -8670,6 +8651,7 @@ cmd_focusout_cb(GtkWidget *w, GdkEventFocus *e, struct tab *t) hide_cmd(t); hide_oops(t); + disable_hints(t); if (show_url == 0 || t->focus_wv) focus_webview(t); @@ -8692,14 +8674,13 @@ cmd_activate_cb(GtkEntry *entry, struct tab *t) DNPRINTF(XT_D_CMD, "cmd_activate_cb: tab %d %s\n", t->tab_id, c); - hide_cmd(t); - /* sanity */ if (c == NULL) goto done; - else if (!(c[0] == ':' || c[0] == '/' || c[0] == '?')) + else if (!(c[0] == ':' || c[0] == '/' || c[0] == '?' || + c[0] == '.' || c[0] == ',')) goto done; - if (strlen(c) < 2) + if (strlen(c) < 1) goto done; s = (char *)&c[1]; @@ -8724,11 +8705,16 @@ cmd_activate_cb(GtkEntry *entry, struct tab *t) history_add(&shl, search_file, s, &search_history_count); goto done; + } else if (c[0] == '.' || c[0] == ',') { + run_script(t, "hints.fire();"); + /* XXX history for link following? */ + goto done; } history_add(&chl, command_file, s, &cmd_history_count); cmd_execute(t, s); done: + hide_cmd(t); return; } -- 2.11.4.GIT