aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhogelog2008-12-12 13:44:50 +0000
committerhogelog2008-12-12 13:44:50 +0000
commit3b9aa5759f9e313c1f6e98cc6a856cc44f8023a1 (patch)
tree341f4f247cfb134e9f597408c7518805525e0bff
parent4295a51e3ce40603bedaced566c88688b728b4a6 (diff)
downloadvimperator-plugins-3b9aa5759f9e313c1f6e98cc6a856cc44f8023a1.tar.bz2
* revert commit.
git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk@26589 d0d07461-0603-4401-acd4-de1884942a52
-rw-r--r--char-hints-mod.js1369
1 files changed, 615 insertions, 754 deletions
diff --git a/char-hints-mod.js b/char-hints-mod.js
index 807153d..02a59b7 100644
--- a/char-hints-mod.js
+++ b/char-hints-mod.js
@@ -1,849 +1,710 @@
-/***** BEGIN LICENSE BLOCK ***** {{{
-Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
-The contents of this file are subject to the Mozilla Public License Version
-1.1 (the "License"); you may not use this file except in compliance with
-the License. You may obtain a copy of the License at
-http://www.mozilla.org/MPL/
-
-Software distributed under the License is distributed on an "AS IS" basis,
-WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-for the specific language governing rights and limitations under the
-License.
-
-(c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>
-
-Alternatively, the contents of this file may be used under the terms of
-either the GNU General Public License Version 2 or later (the "GPL"), or
-the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-in which case the provisions of the GPL or the LGPL are applicable instead
-of those above. If you wish to allow use of your version of this file only
-under the terms of either the GPL or the LGPL, and not to allow others to
-use your version of this file under the terms of the MPL, indicate your
-decision by deleting the provisions above and replace them with the notice
-and other provisions required by the GPL or the LGPL. If you do not delete
-the provisions above, a recipient may use your version of this file under
-the terms of any one of the MPL, the GPL or the LGPL.
-}}} ***** END LICENSE BLOCK *****/
-/**
- * ==VimperatorPlugin==
- * @name char-hints-mod.js
- * @description Character Hints mode
- * ==/VimperatorPlugin==
- *
- * It's based on vimperator-2.0pre(2008/11/28) hints.js
- *
- **/
-(function () //{{{
+// Vimperator plugin: 'Char Hints Mod'
+// Last Change: 06-Apr-2008. Jan 2008
+// License: GPL
+// Version: 0.3
+// Maintainer: Trapezoid <trapezoid.g@gmail.com>
+
+// This file is a tweak based on char-hints.js by:
+// (c) 2008: marco candrian <mac@calmar.ws>
+// This file is a tweak based on hints.js by:
+// (c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>
+
+// Tested with vimperator 0.6pre from 2008-03-07
+// (won't work with older versions)
+
+// INSTALL: put this file into ~/.vimperator/plugin/ (create folders if necessary)
+// and restart firefox or :source that file
+
+// plugin-setup
+liberator.plugins.charhints = {};
+var chh = liberator.plugins.charhints;
+
+//<<<<<<<<<<<<<<<< EDIT USER SETTINGS HERE
+
+//chh.hintchars = "asdfjkl"; // chars to use for generating hints
+chh.hintchars = "hjklasdfgyuiopqwertnmzxcvb"; // chars to use for generating hints
+
+chh.showcapitals = true; // show capital letters, even with lowercase hintchars
+chh.timeout = 500; // in 1/000sec; when set to 0, press <RET> to follow
+
+chh.fgcolor = "black"; // hints foreground color
+chh.bgcolor = "yellow"; // hints background color
+chh.selcolor = "#99FF00"; // selected/active hints background color
+
+chh.mapNormal = "f"; // trigger normal mode with...
+chh.mapNormalNewTab = "F"; // trigger and open in new tab
+chh.mapExtended = ";"; // open in extended mode (see notes below)
+
+chh.hinttags = "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | " +
+"//input[not(@type='hidden')] | //a | //area | //iframe | //textarea | //button | //select | " +
+"//xhtml:*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | " +
+"//xhtml:input[not(@type='hidden')] | //xhtml:a | //xhtml:area | //xhtml:iframe | //xhtml:textarea | " +
+"//xhtml:button | //xhtml:select";
+
+//========================================
+// extended hints mode arguments
+//
+// ; to focus a link and hover it with the mouse
+// a to save its destination (prompting for save location)
+// s to save its destination
+// o to open its location in the current tab
+// t to open its location in a new tab
+// O to open its location in an :open query
+// T to open its location in a :tabopen query
+// v to view its destination source
+// w to open its destination in a new window
+// W to open its location in a :winopen query
+// y to yank its location
+// Y to yank its text description
+
+// variables etc//{{{
+
+
+// ignorecase when showcapitals = true
+// (input keys on onEvent gets lowercased too
+
+if (chh.showcapitals)
+ chh.hintchars = chh.hintchars.toLowerCase();
+
+
+chh.submode = ""; // used for extended mode, can be "o", "t", "y", etc.
+chh.hintString = ""; // the typed string part of the hint is in this string
+chh.hintNumber = 0; // only the numerical part of the hint
+chh.usedTabKey = false; // when we used <Tab> to select an element
+
+chh.hints = [];
+chh.validHints = []; // store the indices of the "hints" array with valid elements
+
+chh.activeTimeout = null; // needed for hinttimeout > 0
+chh.canUpdate = false;
+
+// used in number2hintchars
+chh.transval = {"0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "a":10, "b":11,
+ "c":12, "d":13,"e":14, "f":15, "g":16, "h":17, "i":18, "j":19, "k":20, "l":21, "m":22, "n":23,
+ "o":24, "p":25,"q":26, "r":27, "s":28, "t":29, "u":30, "v":31, "w":32, "x":33, "y":34, "z":35};
+
+// used in hintchars2number
+chh.conversion = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+// keep track of the documents which we generated the hints for
+// docs = { doc: document, start: start_index in hints[], end: end_index in hints[] }
+chh.docs = [];
+//}}}
+// reset all important variables
+chh.reset = function ()//{{{
{
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////// PRIVATE SECTION /////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////{{{
-
- const ELEM = 0, TEXT = 1, SPAN = 2, IMGSPAN = 3;
-
- var myModes = config.browserModes;
-
- var hintMode;
- var submode = ""; // used for extended mode, can be "o", "t", "y", etc.
- var hintString = ""; // the typed string part of the hint is in this string
- var hintNumber = 0; // only the numerical part of the hint
- var usedTabKey = false; // when we used <Tab> to select an element
- var prevInput = ""; // record previous user input type, "text" || "number"
-
- // hints[] = [elem, text, span, imgspan, elem.style.backgroundColor, elem.style.color]
- var pageHints = [];
- var validHints = []; // store the indices of the "hints" array with valid elements
-
- var escapeNumbers = false; // escape mode for numbers. true -> treated as hint-text
- var activeTimeout = null; // needed for hinttimeout > 0
- var canUpdate = false;
-
- // keep track of the documents which we generated the hints for
- // docs = { doc: document, start: start_index in hints[], end: end_index in hints[] }
- var docs = [];
-
- const Mode = new Struct("prompt", "action", "tags");
- Mode.defaultValue("tags", function () function () options.hinttags);
- function extended() options.extendedhinttags;
- const hintModes = {
- ";": Mode("Focus hint", function (elem) buffer.focusElement(elem), extended),
- a: Mode("Save hint with prompt", function (elem) buffer.saveLink(elem, false)),
- s: Mode("Save hint", function (elem) buffer.saveLink(elem, true)),
- o: Mode("Follow hint", function (elem) buffer.followLink(elem, liberator.CURRENT_TAB)),
- t: Mode("Follow hint in a new tab", function (elem) buffer.followLink(elem, liberator.NEW_TAB)),
- b: Mode("Follow hint in a background tab", function (elem) buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB)),
- v: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, false), extended),
- V: Mode("View hint source", function (elem, loc) buffer.viewSource(loc, true), extended),
- w: Mode("Follow hint in a new window", function (elem) buffer.followLink(elem, liberator.NEW_WINDOW), extended),
-
- "?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended),
- O: Mode("Open location based on hint", function (elem, loc) commandline.open(":", "open " + loc, modes.EX)),
- T: Mode("Open new tab based on hint", function (elem, loc) commandline.open(":", "tabopen " + loc, modes.EX)),
- W: Mode("Open new window based on hint", function (elem, loc) commandline.open(":", "winopen " + loc, modes.EX)),
- y: Mode("Yank hint location", function (elem, loc) util.copyToClipboard(loc, true)),
- Y: Mode("Yank hint description", function (elem) util.copyToClipboard(elem.textContent || "", true), extended),
- };
-
- // reset all important variables
- function reset()
- {
- statusline.updateInputBuffer("");
- hintString = "";
- hintNumber = 0;
- usedTabKey = false;
- prevInput = "";
- pageHints = [];
- validHints = [];
- canUpdate = false;
- docs = [];
- escapeNumbers = false;
-
- if (activeTimeout)
- clearTimeout(activeTimeout);
- activeTimeout = null;
- }
-
- function updateStatusline()
- {
- statusline.updateInputBuffer((escapeNumbers ? mappings.getMapLeader() : "") + (hintNumber || ""));
- }
-
- function generate(win)
- {
- if (!win)
- win = window.content;
+ liberator.statusline.updateInputBuffer("");
+ chh.hintString = "";
+ chh.hintNumber = 0;
+ chh.usedTabKey = false;
+ chh.hints = [];
+ chh.validHints = [];
+ chh.canUpdate = false;
+ chh.docs = [];
+
+ if (chh.activeTimeout)
+ clearTimeout(chh.activeTimeout);
+ chh.activeTimeout = null;
+}
+//}}}
+chh.updateStatusline = function ()//{{{
+{
+ liberator.statusline.updateInputBuffer(("") +
+ (chh.hintString ? "\"" + chh.hintString + "\"" : "") +
+ (chh.hintNumber > 0 ? " <" + chh.hintNumber + ">" : ""));
+}
+//}}}
+// this function 'click' an element, which also works
+// for javascript links
+chh.hintchars2number = function (hintstr)//{{{
+{
+ // convert into 'normal number then make it decimal-based
- var doc = win.document;
- var height = win.innerHeight;
- var width = win.innerWidth;
- var scrollX = doc.defaultView.scrollX;
- var scrollY = doc.defaultView.scrollY;
+ var converted = "";
- var baseNodeAbsolute = util.xmlToDom(<span highlight="Hint"/>, doc);
+ // translate users hintchars into a number (chh.conversion) 0 -> 0, 1 -> 1, ...
+ for (let i = 0, l = hintstr.length; i < l; i++)
+ converted += "" + chh.conversion[chh.hintchars.indexOf(hintstr[i])];
- var elem, tagname, text, span, rect;
- var res = buffer.evaluateXPath(hintMode.tags(), doc, null, true);
+ // add one, since hints begin with 0;
- var fragment = util.xmlToDom(<div highlight="hints"/>, doc);
- var start = pageHints.length;
- for (let elem in res)
- {
- // TODO: for iframes, this calculation is wrong
- rect = elem.getBoundingClientRect();
- if (!rect || rect.top > height || rect.bottom < 0 || rect.left > width || rect.right < 0)
- continue;
-
- rect = elem.getClientRects()[0];
- if (!rect)
- continue;
-
- var computedStyle = doc.defaultView.getComputedStyle(elem, null);
- if (computedStyle.getPropertyValue("visibility") == "hidden" || computedStyle.getPropertyValue("display") == "none")
- continue;
-
- // TODO: mozilla docs recommend localName instead of tagName
- tagname = elem.tagName.toLowerCase();
- if (tagname == "input" || tagname == "textarea")
- text = elem.value;
- else if (tagname == "select")
- {
- if (elem.selectedIndex >= 0)
- text = elem.item(elem.selectedIndex).text;
- else
- text = "";
- }
- else
- text = elem.textContent.toLowerCase();
+ return parseInt(converted, chh.hintchars.length); // hintchars.length is the base/radix
+}
+//}}}
+chh.number2hintchars = function (nr)//{{{
+{
+ var oldnr = nr;
+ var converted = "";
+ var tmp = "";
- span = baseNodeAbsolute.cloneNode(true);
- span.style.left = (rect.left + scrollX) + "px";
- span.style.top = (rect.top + scrollY) + "px";
- fragment.appendChild(span);
+ tmp = nr.toString(chh.hintchars.length); // hintchars.length is the base/radix)
- pageHints.push([elem, text, span, null, elem.style.backgroundColor, elem.style.color]);
- }
+ // translate numbers into users hintchars
+ // tmp might be 2e -> (chh.transval) 2 and 14 -> (chh.hintchars) according hintchars
- if (doc.body)
- {
- doc.body.appendChild(fragment);
- docs.push({ doc: doc, start: start, end: pageHints.length - 1 });
- }
+ for (let i = 0, l = tmp.length; i < l; i++)
+ converted += "" + chh.hintchars[chh.transval[tmp[i]]];
- // also generate hints for frames
- Array.forEach(win.frames, function (frame) { generate(frame); });
+ return converted;
+}
+//}}}
+chh.openHint = function (where)//{{{
+{
+ if (chh.validHints.length < 1)
+ return false;
+
+ var x = 1, y = 1;
+ var elem = chh.validHints[chh.hintNumber - 1] || chh.validHints[0];
+ var elemTagName = elem.localName.toLowerCase();
+ elem.focus();
+
+ liberator.buffer.followLink(elem, where);
+ return true;
+}
+//}}}
+chh.focusHint = function ()//{{{
+{
+ if (chh.validHints.length < 1)
+ return false;
- return true;
+ var elem = chh.validHints[chh.hintNumber - 1] || chh.validHints[0];
+ var doc = window.content.document;
+ var elemTagName = elem.localName.toLowerCase();
+ if (elemTagName == "frame" || elemTagName == "iframe")
+ {
+ elem.contentWindow.focus();
+ return false;
}
-
- // TODO: make it aware of imgspans
- function showActiveHint(newID, oldID)
+ else
{
- var oldElem = validHints[oldID - 1];
- if (oldElem)
- setClass(oldElem, false);
-
- var newElem = validHints[newID - 1];
- if (newElem)
- setClass(newElem, true);
+ elem.focus();
}
- function setClass(elem, active)
+ var evt = doc.createEvent("MouseEvents");
+ var x = 0;
+ var y = 0;
+ // for imagemap
+ if (elemTagName == "area")
{
- let prefix = (elem.getAttributeNS(NS.uri, "class") || "") + " ";
- if (active)
- elem.setAttributeNS(NS.uri, "highlight", prefix + "HintActive");
- else
- elem.setAttributeNS(NS.uri, "highlight", prefix + "HintElem");
+ [x, y] = elem.getAttribute("coords").split(",");
+ x = Number(x);
+ y = Number(y);
}
- function showHints()
- {
+ evt.initMouseEvent("mouseover", true, true, doc.defaultView, 1, x, y, 0, 0, 0, 0, 0, 0, 0, null);
+ elem.dispatchEvent(evt);
+}
+//}}}
+chh.yankHint = function (text)//{{{
+{
+ if (chh.validHints.length < 1)
+ return false;
+
+ var elem = chh.validHints[chh.hintNumber - 1] || chh.validHints[0];
+ var loc;
+ if (text)
+ loc = elem.textContent;
+ else
+ loc = elem.href;
+
+ liberator.copyToClipboard(loc);
+ liberator.echo("Yanked " + loc, liberator.commandline.FORCE_SINGLELINE);
+}
+//}}}
+chh.saveHint = function (skipPrompt)//{{{
+{
+ if (chh.validHints.length < 1)
+ return false;
- let elem, tagname, text, rect, span, imgspan;
- let hintnum = 1;
- let validHint = hintMatcher(hintString.toLowerCase());
- let activeHint = hintNumber || 1;
- validHints = [];
+ var elem = chh.validHints[chh.hintNumber - 1] || chh.validHints[0];
- for (let [,{ doc: doc, start: start, end: end }] in Iterator(docs))
- {
- let scrollX = doc.defaultView.scrollX;
- let scrollY = doc.defaultView.scrollY;
+ try
+ {
+ liberator.buffer.saveLink(elem,skipPrompt);
+ }
+ catch (e)
+ {
+ liberator.echoerr(e);
+ }
+}
+//}}}
+chh.generate = function (win)//{{{
+{
+ var startDate = Date.now();
+
+ if (!win)
+ win = window.content;
+
+ var doc = win.document;
+ var height = win.innerHeight;
+ var width = win.innerWidth;
+ var scrollX = doc.defaultView.scrollX;
+ var scrollY = doc.defaultView.scrollY;
+
+ var baseNodeAbsolute = doc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ baseNodeAbsolute.style.backgroundColor = "red";
+ baseNodeAbsolute.style.color = "white";
+ baseNodeAbsolute.style.position = "absolute";
+ baseNodeAbsolute.style.fontSize = "10px";
+ baseNodeAbsolute.style.fontWeight = "bold";
+ baseNodeAbsolute.style.lineHeight = "10px";
+ baseNodeAbsolute.style.padding = "0px 1px 0px 0px";
+ baseNodeAbsolute.style.zIndex = "10000001";
+ baseNodeAbsolute.style.display = "none";
+ baseNodeAbsolute.className = "vimperator-hint";
+
+ var elem, tagname, text, span, rect;
+ var res = liberator.buffer.evaluateXPath(chh.hinttags, doc, null, true);
+ liberator.log("shints: evaluated XPath after: " + (Date.now() - startDate) + "ms");
+
+ var fragment = doc.createDocumentFragment();
+ var start = chh.hints.length;
+ while ((elem = res.iterateNext()) != null)
+ {
+ // TODO: for frames, this calculation is wrong
+ rect = elem.getBoundingClientRect();
+ if (!rect || rect.top > height || rect.bottom < 0 || rect.left > width || rect.right < 0)
+ continue;
+
+ rect = elem.getClientRects()[0];
+ if (!rect)
+ continue;
+
+ // TODO: mozilla docs recommend localName instead of tagName
+ tagname = elem.tagName.toLowerCase();
+ text = "";
+ span = baseNodeAbsolute.cloneNode(true);
+ span.style.left = (rect.left + scrollX) + "px";
+ span.style.top = (rect.top + scrollY) + "px";
+ fragment.appendChild(span);
+
+ chh.hints.push([elem, text, span, null, elem.style.backgroundColor, elem.style.color]);
+ }
- inner:
- for (let i in (util.interruptableRange(start, end + 1, 500)))
- {
- let hint = pageHints[i];
- [elem, text, span, imgspan] = hint;
+ doc.body.appendChild(fragment);
+ chh.docs.push({ doc: doc, start: start, end: chh.hints.length - 1 });
- // if (!validHint(text))
- // {
- // span.style.display = "none";
- // if (imgspan)
- // imgspan.style.display = "none";
+ // also generate hints for frames
+ for (let i = 0; i < win.frames.length; i++)
+ chh.generate(win.frames[i]);
- // elem.removeAttributeNS(NS.uri, "highlight");
- // continue inner;
- // }
+ liberator.log("shints: generate() completed after: " + (Date.now() - startDate) + "ms");
+ return true;
+}
+//}}}
+// TODO: make it aware of imgspans
+chh.showActiveHint = function (newID, oldID)//{{{
+{
+ var oldElem = chh.validHints[oldID - 1];
+ if (oldElem)
+ oldElem.style.backgroundColor = chh.bgcolor;
+
+ var newElem = chh.validHints[newID - 1];
+ if (newElem)
+ newElem.style.backgroundColor = chh.selcolor;
+}
+//}}}
+chh.showHints = function ()//{{{
+{
+ var startDate = Date.now();
+ var win = window.content;
+ var height = win.innerHeight;
+ var width = win.innerWidth;
- if (text == "" && elem.firstChild && elem.firstChild.tagName == "IMG")
- {
- if (!imgspan)
- {
- rect = elem.firstChild.getBoundingClientRect();
- if (!rect)
- continue;
-
- imgspan = util.xmlToDom(<span highlight="Hint"/>, doc);
- imgspan.setAttributeNS(NS.uri, "class", "HintImage");
- imgspan.style.left = (rect.left + scrollX) + "px";
- imgspan.style.top = (rect.top + scrollY) + "px";
- imgspan.style.width = (rect.right - rect.left) + "px";
- imgspan.style.height = (rect.bottom - rect.top) + "px";
- hint[IMGSPAN] = imgspan;
- span.parentNode.appendChild(imgspan);
- }
- setClass(imgspan, activeHint == hintnum)
- }
- var chars = num2chars(hintnum++);
- span.setAttribute("number", chars);
- elem.setAttribute("number", chars);
- ++hintnum;
- if (imgspan)
- imgspan.setAttribute("number", num2chars(hintnum));
- else
- setClass(elem, hintString == chars);
- //setClass(elem, activeHint == chars);
- if(chars.indexOf(hintString)==0) {
- validHints.push(elem);
- }
- }
- }
+ var elem, tagname, text, rect, span, imgspan;
+ var hintnum = 1;
+ //var findTokens = chh.hintString.split(/ +/);
+ var activeHint = chh.hintNumber || 1;
+ chh.validHints = [];
- if (options.usermode)
+ for (let j = 0; j < chh.docs.length; j++)
+ {
+ let doc = chh.docs[j].doc;
+ let start = chh.docs[j].start;
+ let end = chh.docs[j].end;
+ let scrollX = doc.defaultView.scrollX;
+ let scrollY = doc.defaultView.scrollY;
+
+outer:
+ for (let i = start; i <= end; i++)
{
- let css = [];
- // FIXME: Broken for imgspans.
- for (let [,{ doc: doc }] in Iterator(docs))
+ [elem, , span, imgspan] = chh.hints[i];
+ text = "";
+
+ if (elem.firstChild && elem.firstChild.tagName == "IMG")
{
- for (let elem in buffer.evaluateXPath("//*[@liberator:highlight and @number]", doc))
+ if (!imgspan)
{
- let group = elem.getAttributeNS(NS.uri, "highlight");
- css.push(highlight.selector(group) + "[number='" + elem.getAttribute("number") + "'] { " + elem.style.cssText + " }");
+ rect = elem.firstChild.getBoundingClientRect();
+ if (!rect)
+ continue;
+
+ imgspan = doc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ imgspan.style.position = "absolute";
+ imgspan.style.opacity = 0.5;
+ imgspan.style.zIndex = "10000000";
+ imgspan.style.left = (rect.left + scrollX) + "px";
+ imgspan.style.top = (rect.top + scrollY) + "px";
+ imgspan.style.width = (rect.right - rect.left) + "px";
+ imgspan.style.height = (rect.bottom - rect.top) + "px";
+ imgspan.className = "vimperator-hint";
+ chh.hints[i][3] = imgspan;
+ doc.body.appendChild(imgspan);
}
+ imgspan.style.backgroundColor = (activeHint == hintnum) ? chh.selcolor : chh.bgcolor;
+ imgspan.style.display = "inline";
}
- styles.addSheet("hint-positions", "*", css.join("\n"), true, true);
- }
- return true;
+ if (!imgspan)
+ elem.style.backgroundColor = (activeHint == hintnum) ? chh.selcolor : chh.bgcolor;
+ elem.style.color = chh.fgcolor;
+ if (chh.showcapitals)
+ span.textContent = chh.number2hintchars(hintnum++).toUpperCase();
+ else
+ span.textContent = chh.number2hintchars(hintnum++);
+
+ span.style.display = "inline";
+ chh.validHints.push(elem);
+ }
}
- function removeHints(timeout)
+ liberator.log("shints: showHints() completed after: " + (Date.now() - startDate) + "ms");
+ return true;
+}
+//}}}
+chh.removeHints = function (timeout)//{{{
+{
+ var firstElem = chh.validHints[0] || null;
+ var firstElemselcolor = "";
+ var firstElemColor = "";
+
+ for (let j = 0; j < chh.docs.length; j++)
{
- var firstElem = validHints[0] || null;
+ let doc = chh.docs[j].doc;
+ let start = chh.docs[j].start;
+ let end = chh.docs[j].end;
- for (let [,{ doc: doc, start: start, end: end }] in Iterator(docs))
+ for (let i = start; i <= end; i++)
{
- for (let elem in buffer.evaluateXPath("//*[@liberator:highlight='hints']", doc))
- elem.parentNode.removeChild(elem);
- for (let i in util.range(start, end + 1))
+ // remove the span for the numeric display part
+ doc.body.removeChild(chh.hints[i][2]);
+ if (chh.hints[i][3]) // a transparent span for images
+ doc.body.removeChild(chh.hints[i][3]);
+
+ if (timeout && firstElem == chh.hints[i][0])
{
- let hint = pageHints[i];
- if (!timeout || hint[ELEM] != firstElem)
- hint[ELEM].removeAttributeNS(NS.uri, "highlight");
+ firstElemselcolor = chh.hints[i][4];
+ firstElemColor = chh.hints[i][5];
}
-
- // animate the disappearance of the first hint
- if (timeout && firstElem)
+ else
{
- // USE THIS FOR MAKING THE SELECTED ELEM RED
- // firstElem.style.backgroundColor = "red";
- // firstElem.style.color = "white";
- // setTimeout(function () {
- // firstElem.style.backgroundColor = firstElemBgColor;
- // firstElem.style.color = firstElemColor;
- // }, 200);
- // OR USE THIS FOR BLINKING:
- // var counter = 0;
- // var id = setInterval(function () {
- // firstElem.style.backgroundColor = "red";
- // if (counter % 2 == 0)
- // firstElem.style.backgroundColor = "yellow";
- // else
- // firstElem.style.backgroundColor = "#88FF00";
- //
- // if (counter++ >= 2)
- // {
- // firstElem.style.backgroundColor = firstElemBgColor;
- // firstElem.style.color = firstElemColor;
- // clearTimeout(id);
- // }
- // }, 100);
- setTimeout(function () { firstElem.removeAttributeNS(NS.uri, "highlight") }, timeout);
+ // restore colors
+ let elem = chh.hints[i][0];
+ elem.style.backgroundColor = chh.hints[i][4];
+ elem.style.color = chh.hints[i][5];
}
}
- styles.removeSheet("hint-positions", null, null, null, true);
- reset();
- }
-
- function processHints(followFirst)
- {
- if (validHints.length == 0)
+ // animate the disappearance of the first hint
+ if (timeout && firstElem)
{
- liberator.beep();
- return false;
+ setTimeout(function () {
+ firstElem.style.backgroundColor = firstElemselcolor;
+ firstElem.style.color = firstElemColor;
+ }, timeout);
}
+ }
- if (options["followhints"] > 0)
- {
- if (!followFirst)
- return false; // no return hit; don't examine uniqueness
+ liberator.log("shints: removeHints() done");
+ chh.reset();
+}
+//}}}
+chh.processHints = function (followFirst)//{{{
+{
+ if (chh.validHints.length == 0)
+ {
+ liberator.beep();
+ return false;
+ }
- // OK. return hit. But there's more than one hint. And
- // there's no tab-selected current link. Do not follow in mode 2
- if ((options["followhints"] == 2) && validHints.length > 1 && !hintNumber)
- return liberator.beep();
- }
-
- if (!followFirst)
+ if (!followFirst)
+ {
+ let firstHref = chh.validHints[0].getAttribute("href") || null;
+ if (firstHref)
{
- var firstHref = validHints[0].getAttribute("href") || null;
- if (firstHref)
- {
- if (validHints.some(function (e) e.getAttribute("href") != firstHref))
- return false;
- }
- else if (validHints.length > 1)
- {
+ if (chh.validHints.some(function (e) { return e.getAttribute("href") != firstHref; }))
return false;
- }
}
+ else if (chh.validHints.length > 1)
+ return false;
+ }
- var timeout = followFirst || events.feedingKeys ? 0 : 500;
- var elems = [];
- for (let [,{ doc: doc }] in Iterator(docs))
- {
- for (let elem in buffer.evaluateXPath("//*[@number=\""+hintString+"\"]", doc))
- {
- elems.push(elem);
- }
- }
- var elem = elems[0];
- removeHints(timeout);
+ var activeNum = chh.hintNumber || 1;
+ var loc = chh.validHints[activeNum - 1].href || "";
+ switch (chh.submode)
+ {
+ case ";": chh.focusHint(); break;
+ case "a": chh.saveHint(false); break;
+ case "s": chh.saveHint(true); break;
+ case "o": chh.openHint(liberator.CURRENT_TAB); break;
+ case "O": liberator.commandline.open(":", "open " + loc, liberator.modes.EX); break;
+ case "t": chh.openHint(liberator.NEW_TAB); break;
+ case "T": liberator.commandline.open(":", "tabopen " + loc, liberator.modes.EX); break;
+ case "w": chh.openHint(liberator.NEW_WINDOW); break;
+ case "W": liberator.commandline.open(":", "winopen " + loc, liberator.modes.EX); break;
+ case "y": chh.yankHint(false); break;
+ case "Y": chh.yankHint(true); break;
+ default:
+ liberator.echoerr("INTERNAL ERROR: unknown submode: " + chh.submode);
+ }
- if (timeout == 0)
- // force a possible mode change, based on wheter an input field has focus
- events.onFocusChange();
+ var timeout = followFirst ? 0 : 500;
+ chh.removeHints(timeout);
+
+ if (liberator.modes.extended & liberator.modes.ALWAYS_HINT)
+ {
setTimeout(function () {
- if (modes.extended & modes.HINTS)
- modes.reset();
- hintMode.action(elem, elem.href || "");
- }, timeout);
- return true;
+ chh.canUpdate = true;
+ chh.hintString = "";
+ chh.hintNumber = 0;
+ liberator.statusline.updateInputBuffer("");
+ }, timeout);
}
-
- function onInput (event)
+ else
{
- prevInput = "text";
-
- // clear any timeout which might be active after pressing a number
- if (activeTimeout)
+ if (timeout == 0 || liberator.modes.isReplaying)
{
- clearTimeout(activeTimeout);
- activeTimeout = null;
+ // force a possible mode change, based on wheter an input field has focus
+ liberator.events.onFocusChange();
+ if (liberator.mode == liberator.modes.HINTS)
+ liberator.modes.reset(false);
+ }
+ else
+ {
+ liberator.modes.add(liberator.modes.INACTIVE_HINT);
+ setTimeout(function () {
+ if (liberator.mode == liberator.modes.HINTS)
+ liberator.modes.reset(false);
+ }, timeout);
}
-
- hintNumber = 0;
- hintString = commandline.getCommand().toUpperCase();
- updateStatusline();
- showHints();
- if (validHints.length == 1)
- processHints(false);
}
- function hintMatcher(hintString) //{{{
+ return true;
+}
+//}}}
+// TODO: implement framesets
+chh.show = function (mode, minor, filter)//{{{
+{
+ if (mode == liberator.modes.EXTENDED_HINT && !/^[;asoOtTwWyY]$/.test(minor))
{
- function containsMatcher(hintString) //{{{
- {
- var tokens = hintString.split(/ +/);
- return function (linkText) tokens.every(function (token) linkText.indexOf(token) >= 0);
- } //}}}
+ liberator.beep();
+ return;
+ }
- function wordStartsWithMatcher(hintString, allowWordOverleaping) //{{{
- {
- var hintStrings = hintString.split(/ +/);
- var wordSplitRegex = new RegExp(options["wordseparators"]);
+ liberator.modes.set(liberator.modes.HINTS, mode);
+ chh.submode = minor || "o"; // open is the default mode
+ chh.hintString = filter || "";
+ chh.hintNumber = 0;
+ chh.canUpdate = false;
- // What the **** does this do? --Kris
- function charsAtBeginningOfWords(chars, words, allowWordOverleaping)
- {
- var charIdx = 0;
- var numMatchedWords = 0;
- for (let [,word] in Iterator(words))
- {
- if (word.length == 0)
- continue;
+ chh.generate();
- let wcIdx = 0;
- // Check if the current word matches same characters as the previous word.
- // Each already matched word has matched at least one character.
- if (charIdx > numMatchedWords)
- {
- let matchingStarted = false;
- for (let i in util.range(numMatchedWords, charIdx))
- {
- if (chars[i] == word[wcIdx])
- {
- matchingStarted = true;
- wcIdx++;
- }
- else if (matchingStarted)
- {
- wcIdx = 0;
- break;
- }
- }
- }
-
- // the current word matches same characters as the previous word
- var prevCharIdx;
- if (wcIdx > 0)
- {
- prevCharIdx = charIdx;
- // now check if it matches additional characters
- for (; wcIdx < word.length && charIdx < chars.length; wcIdx++, charIdx++)
- {
- if (word[wcIdx] != chars[charIdx])
- break;
- }
-
- // the word doesn't match additional characters, now check if the
- // already matched characters are equal to the next characters for matching,
- // if yes, then consume them
- if (prevCharIdx == charIdx)
- {
- for (let i = 0; i < wcIdx && charIdx < chars.length; i++, charIdx++)
- {
- if (word[i] != chars[charIdx])
- break;
- }
- }
-
- numMatchedWords++;
- }
- // the current word doesn't match same characters as the previous word, just
- // try to match the next characters
- else
- {
- prevCharIdx = charIdx;
- for (let i = 0; i < word.length && charIdx < chars.length; i++, charIdx++)
- {
- if (word[i] != chars[charIdx])
- break;
- }
-
- if (prevCharIdx == charIdx)
- {
- if (!allowWordOverleaping)
- return false;
- }
- else
- numMatchedWords++;
- }
-
- if (charIdx == chars.length)
- return true;
- }
+ // get all keys from the input queue
+ var mt = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;
+ while (mt.hasPendingEvents())
+ mt.processNextEvent(true);
- return (charIdx == chars.length);
- }
+ chh.canUpdate = true;
+ chh.showHints();
- function stringsAtBeginningOfWords(strings, words, allowWordOverleaping)
- {
- var strIdx = 0;
- for (let [,word] in Iterator(words))
- {
- if (word.length == 0)
- continue;
+ if (chh.validHints.length == 0)
+ {
+ liberator.beep();
+ liberator.modes.reset();
+ return false;
+ }
+ else if (chh.validHints.length == 1)
+ {
+ chh.processHints(true);
+ return false;
+ }
+ else // still hints visible
+ return true;
+}
+//}}}
+chh.hide = function ()//{{{
+{
+ chh.removeHints(0);
+}
+//}}}
+chh.onEvent = function (event)//{{{
+{
+ var key = liberator.events.toString(event);
- let str = strings[strIdx];
- if (str.length == 0 || word.indexOf(str) == 0)
- strIdx++;
- else if (!allowWordOverleaping)
- return false;
+ if (chh.showcapitals && key.length == 1)
+ key = key.toLowerCase();
- if (strIdx == strings.length)
- return true;
- }
+ // clear any timeout which might be active after pressing a number
+ if (chh.activeTimeout)
+ {
+ clearTimeout(chh.activeTimeout);
+ chh.activeTimeout = null;
+ }
- for (; strIdx < strings.length; strIdx++)
- {
- if (strings[strIdx].length != 0)
- return false;
- }
- return true;
+ switch (key)
+ {
+ case "<Return>":
+ chh.processHints(true);
+ break;
+
+ case "<Tab>":
+ case "<S-Tab>":
+ chh.usedTabKey = true;
+ if (chh.hintNumber == 0)
+ chh.hintNumber = 1;
+
+ let oldID = chh.hintNumber;
+ if (key == "<Tab>")
+ {
+ if (++chh.hintNumber > chh.validHints.length)
+ chh.hintNumber = 1;
}
-
- function wordStartsWith(linkText)
+ else
{
- if (hintStrings.length == 1 && hintStrings[0].length == 0)
- return true;
-
- let words = linkText.split(wordSplitRegex).map(String.toLowerCase);
- if (hintStrings.length == 1)
- return charsAtBeginningOfWords(hintStrings[0], words, allowWordOverleaping);
- else
- return stringsAtBeginningOfWords(hintStrings, words, allowWordOverleaping);
+ if (--chh.hintNumber < 1)
+ chh.hintNumber = chh.validHints.length;
}
+ chh.showActiveHint(chh.hintNumber, oldID);
+ return;
- return wordStartsWith;
- } //}}}
-
- var hintMatching = options["hintmatching"];
- switch (hintMatching)
- {
- case "contains" : return containsMatcher(hintString);
- case "wordstartswith": return wordStartsWithMatcher(hintString, /*allowWordOverleaping=*/ true);
- case "firstletters" : return wordStartsWithMatcher(hintString, /*allowWordOverleaping=*/ false);
- case "custom" : return liberator.plugins.customHintMatcher(hintString);
- default : liberator.echoerr("Invalid hintmatching type: " + hintMatching);
- }
- return null;
- } //}}}
-
- function histchars() // {{{
- {
- return options["hintchars"].toUpperCase();
- } // }}}
- function chars2num(chars) //{{{
- {
- var num = 0;
- var hintchars = histchars();
- var base = hintchars.length;
- for(var i=0;i<chars.length;++i) {
- num = base*num + hintchars.indexOf(chars[i]);
- }
- return num;
- } //}}}
- function num2chars(num) //{{{
- {
- var chars = "";
- var hintchars = histchars();
- var base = hintchars.length;
- do {
- chars = hintchars[((num % base))] + chars;
- num = Math.floor(num/base);
- } while(num>0);
-
- return chars;
- } //}}}
- /////////////////////////////////////////////////////////////////////////////}}}
- ////////////////////// OPTIONS /////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////{{{
-
- const DEFAULT_HINTTAGS = "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | " +
- "//input[not(@type='hidden')] | //a | //area | //iframe | //textarea | //button | //select | " +
- "//xhtml:*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | " +
- "//xhtml:input[not(@type='hidden')] | //xhtml:a | //xhtml:area | //xhtml:iframe | //xhtml:textarea | //xhtml:button | //xhtml:select";
-
- options.add(["extendedhinttags", "eht"],
- "XPath string of hintable elements activated by ';'",
- "string", DEFAULT_HINTTAGS);
-
- options.add(["hinttags", "ht"],
- "XPath string of hintable elements activated by 'f' and 'F'",
- "string", DEFAULT_HINTTAGS);
-
- options.add(["hinttimeout", "hto"],
- "Automatically follow non unique numerical hint",
- "number", 0,
- { validator: function (value) value >= 0 });
-
- options.add(["followhints", "fh"],
- "Change the way when to automatically follow hints",
- "number", 0,
- { validator: function (value) value >= 0 && value < 3 });
-
- options.add(["hintmatching", "hm"],
- "How links are matched",
- "string", "contains",
- {
- completer: function (filter)
+ case "<BS>": //TODO: may tweak orig hints.js too (adding 2 lines ...)
+ let oldID = chh.hintNumber;
+ if (chh.hintNumber > 0)
{
- return [[m, ""] for each (m in ["contains", "wordstartswith", "firstletters", "custom"])];
- },
- validator: options.validateCompleter
- });
-
- options.add(["wordseparators", "wsp"],
- "How words are split for hintmatching",
- "string", '[.,!?:;/"^$%&?()[\\]{}<>#*+|=~ _-]');
+ chh.hintNumber = Math.floor(chh.hintNumber / chh.hintchars.length);
+ chh.hintString = chh.hintString.substr(0, chh.hintString.length - 1);
+ chh.usedTabKey = false;
+ }
+ else
+ {
+ chh.usedTabKey = false;
+ chh.hintNumber = 0;
+ liberator.beep();
+ return;
+ }
+ chh.showActiveHint(chh.hintNumber, oldID);
+ break;
+
+ case "<C-w>":
+ case "<C-u>":
+ chh.hintString = "";
+ chh.hintNumber = 0;
+ break;
+
+ default:
+ // pass any special or ctrl- etc. prefixed key back to the main vimperator loop
+ if (/^<./.test(key) || key == ":")
+ {
+ //FIXME: won't work probably
+ let map = null;
+ if ((map = liberator.mappings.get(liberator.modes.NORMAL, key)) ||
+ (map = liberator.mappings.get(liberator.modes.HINTS, key))) //TODO
+ {
+ map.execute(null, -1);
+ return;
+ }
- options.add(["hintchars", "hchar"],
- "Hints character",
- "string", "hjklasdfgyuiopqwertnmzxcvb");
+ liberator.beep();
+ return;
+ }
- /////////////////////////////////////////////////////////////////////////////}}}
- ////////////////////// MAPPINGS ////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////{{{
+ if (chh.hintchars.indexOf(key) >= 0) // TODO: check if in hintchars
+ {
+ chh.hintString += key;
+ let oldHintNumber = chh.hintNumber;
+ if (chh.hintNumber == 0 || chh.usedTabKey)
+ {
+ chh.usedTabKey = false;
+ }
- mappings.addUserMap(myModes, ["f"],
- "Start QuickHint mode",
- function () { chh.show("o"); });
+ chh.hintNumber = chh.hintchars2number(chh.hintString);
- mappings.addUserMap(myModes, ["F"],
- "Start QuickHint mode, but open link in a new tab",
- function () { chh.show("t"); });
+ chh.updateStatusline();
- mappings.addUserMap(myModes, [";"],
- "Start an extended hint mode",
- function (arg) { chh.show(arg); },
- { flags: Mappings.flags.ARGUMENT });
+ if (!chh.canUpdate)
+ return;
- /////////////////////////////////////////////////////////////////////////////}}}
- ////////////////////// PUBLIC SECTION //////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////{{{
+ if (chh.docs.length == 0)
+ {
+ chh.generate();
+ chh.showHints();
+ }
+ chh.showActiveHint(chh.hintNumber, oldHintNumber || 1);
-
- var chh = liberator.plugins.charhints = {
+ if (chh.hintNumber == 0 || chh.hintNumber > chh.validHints.length)
+ {
+ liberator.beep();
+ return;
+ }
- addMode: function (mode)
- {
- hintModes[mode] = Mode.apply(Mode, Array.slice(arguments, 1));
- },
+ // orig hints.js comment: if we write a numeric part like 3, but we have 45 hints, only follow
+ // the hint after a timeout, as the user might have wanted to follow link 34
+ if (chh.hintNumber > 0 && chh.hintNumber * chh.hintchars.length <= chh.validHints.length)
+ {
+ if (chh.timeout > 0)
+ chh.activeTimeout = setTimeout(chh.processHints, chh.timeout, true);
- show: function (minor, filter, win)
- {
- removeHints();
- hintMode = hintModes[minor];
- if (!hintMode)
- {
- liberator.beep();
+ return false;
+ }
+ // we have a unique hint
+ chh.processHints(true);
return;
}
- commandline.input(hintMode.prompt + ":", null, { onChange: onInput });
- modes.extended = modes.HINTS;
-
- submode = minor;
- hintString = filter || "";
- hintNumber = 0;
- usedTab = false;
- prevInput = "";
- canUpdate = false;
- generate(win);
-
- // get all keys from the input queue
- liberator.threadYield(true);
-
- canUpdate = true;
- showHints();
-
- if (validHints.length == 0)
+ if (chh.usedTabKey)
{
- liberator.beep();
- modes.reset();
- return false;
+ chh.usedTabKey = false;
+ chh.showActiveHint(1, chh.hintNumber);
}
- else if (validHints.length == 1)
- {
- processHints(true);
- return false;
- }
- else // still hints visible
- return true;
- },
+ }
- hide: function ()
- {
- removeHints(0);
- },
+ chh.updateStatusline();
+}//}}}
- onEvent: function (event)
- {
- var key = events.toString(event);
- var followFirst = false;
- // clear any timeout which might be active after pressing a number
- if (activeTimeout)
- {
- clearTimeout(activeTimeout);
- activeTimeout = null;
- }
+// <<<<<<<<<<<<<<< registering/setting up this plugin
- switch (key)
- {
- case "<Return>":
- followFirst = true;
- break;
-
- case "<Tab>":
- case "<S-Tab>":
- usedTabKey = true;
- if (hintNumber == 0)
- hintNumber = 1;
-
- var oldID = hintNumber;
- if (key == "<Tab>")
- {
- if (++hintNumber > validHints.length)
- hintNumber = 1;
- }
- else
- {
- if (--hintNumber < 1)
- hintNumber = validHints.length;
- }
- hintString = num2chars(hintNumber);
- showActiveHint(hintNumber, oldID);
- updateStatusline();
- return;
+//liberator.modes.setCustomMode ("CHAR-HINTS", liberator.plugins.charhints.onEvent,
+// liberator.plugins.charhints.hide);
+liberator.hints = chh;
+liberator.mappings.addUserMap([liberator.modes.NORMAL], [chh.mapNormal],
+ "Start Custum-QuickHint mode",
+ function () { liberator.plugins.charhints.show(liberator.modes.QUICK_HINT); },
+ { noremap: true }
+);
- case "<BS>":
- if (hintNumber > 0 && !usedTabKey)
- {
- hintNumber = Math.floor(hintNumber / 10);
- if (hintNumber == 0)
- prevInput = "text";
- }
- else
- {
- usedTabKey = false;
- hintNumber = 0;
- liberator.beep();
- return;
- }
- break;
-
- case mappings.getMapLeader():
- escapeNumbers = !escapeNumbers;
- if (escapeNumbers && usedTabKey) // hintNumber not used normally, but someone may wants to toggle
- hintNumber = 0; // <tab>s ? reset. Prevent to show numbers not entered.
-
- updateStatusline();
- return;
-
- default:
- var hintchars = histchars();
- if (hintchars.indexOf(key)==0)
- {
- // FIXME: Kludge.
- if (escapeNumbers)
- {
- let cmdline = document.getElementById("liberator-commandline-command");
- let start = cmdline.selectionStart;
- let end = cmdline.selectionEnd;
- cmdline.value = cmdline.value.substr(0, start) + key + cmdline.value.substr(start);
- cmdline.selectionStart = start + 1;
- cmdline.selectionEnd = end + 1;
- return;
- }
-
- prevInput = "number";
-
- var oldHintNumber = hintNumber;
- if (hintNumber == 0 || usedTabKey)
- {
- usedTabKey = false;
- hintNumber = parseInt(key, 10);
- }
- else
- hintNumber = (hintNumber * 10) + parseInt(key, 10);
-
- updateStatusline();
-
- if (!canUpdate)
- return;
-
- if (docs.length == 0)
- {
- generate();
- showHints();
- }
- showActiveHint(hintNumber, oldHintNumber || 1);
-
- if (hintNumber == 0 || hintNumber > validHints.length)
- {
- liberator.beep();
- return;
- }
-
- // if we write a numeric part like 3, but we have 45 hints, only follow
- // the hint after a timeout, as the user might have wanted to follow link 34
- if (hintNumber > 0 && hintNumber * 10 <= validHints.length)
- {
- var timeout = options["hinttimeout"];
- if (timeout > 0)
- activeTimeout = setTimeout(function () { processHints(true); }, timeout);
-
- return false;
- }
- // we have a unique hint
- processHints(true);
- return;
- }
- }
+liberator.mappings.addUserMap([liberator.modes.NORMAL], [chh.mapNormalNewTab],
+ "Start Custum-QuickHint mode, but open link in a new tab",
+ function () { liberator.plugins.charhints.show(liberator.modes.QUICK_HINT, "t"); },
+ { noremap: true }
+);
- updateStatusline();
-
- if (canUpdate)
- {
- if (docs.length == 0 && hintString.length > 0)
- generate();
-
- showHints();
- processHints(followFirst);
- }
+liberator.mappings.addUserMap([liberator.modes.NORMAL], [chh.mapExtended],
+ "Start an extended hint mode",
+ function (arg)
+ {
+ if (arg == "f")
+ liberator.plugins.charhints.show(liberator.modes.ALWAYS_HINT, "o");
+ else if (arg == "F")
+ liberator.plugins.charhints.show(liberator.modes.ALWAYS_HINT, "t");
+ else
+ liberator.plugins.charhints.show(liberator.modes.EXTENDED_HINT, arg);
},
- };
-
- for(let name in chh) {
- hints[name] = chh[name];
- }
- //}}}
-})(); //}}}
+ {
+ flags: liberator.Mappings.flags.ARGUMENT,
+ noremap: true
+ }
+);
// vim: set fdm=marker sw=4 ts=4 et: