index.md - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       index.md (8644B)
       ---
            1 Keyboard link hinting
            2 =====================
            3 
            4 Description
            5 -----------
            6 
            7 This script enables link hinting support to surf. Deploy it into ~/.surf/script.js.
            8 
            9 Keybindings
           10 -----------
           11         ^f (open link in current window)
           12         ^F (open link in new window)
           13         ^c (cancel hinting)
           14 
           15 Azerty keyboards
           16 ----------------
           17 Just replace like this to use `&é"'(-è_çà` as numbers : 
           18 
           19     "U+a0c3" : "0",
           20     "U+0026" : "1",
           21     "U+a9c3" : "2",
           22     "U+0022" : "3",
           23     "U+0027" : "4",
           24     "U+0028" : "5",
           25     "U+002d" : "6",
           26     "U+a8c3" : "7",
           27     "U+005f" : "8",
           28     "U+a7c3" : "9",
           29 
           30 Author
           31 ------
           32 
           33 - The code was originally from chromium but was adapted by nibble(.ds(at)gmail(dot)com) to work with surf.
           34 
           35 Code
           36 ----
           37 
           38         /* based on chromium plugin code, adapted by Nibble<.gs@gmail.com> */
           39         var hint_num_str = '';
           40         var hint_elems = [];
           41         var hint_open_in_new_tab = false;
           42         var hint_enabled = false;
           43 
           44         function hintMode(newtab){
           45                 hint_enabled = true;
           46                 if (newtab) {
           47                         hint_open_in_new_tab = true;
           48                 } else {
           49                         hint_open_in_new_tab = false;
           50                 }
           51                 setHints();
           52                 document.removeEventListener('keydown', initKeyBind, false);
           53                 document.addEventListener('keydown', hintHandler, false);
           54                 hint_num_str = '';
           55         }
           56 
           57         function hintHandler(e){
           58                 e.preventDefault();  //Stop Default Event 
           59                 var pressedKey = get_key(e);
           60                 if (pressedKey == 'Enter') {
           61                         if (hint_num_str == '')
           62                                 hint_num_str = '1';
           63                         judgeHintNum(Number(hint_num_str));
           64                 } else if (/[0-9]/.test(pressedKey) == false) {
           65                         removeHints();
           66                 } else {
           67                         hint_num_str += pressedKey;
           68                         var hint_num = Number(hint_num_str);
           69                         if (hint_num * 10 > hint_elems.length + 1) {
           70                                 judgeHintNum(hint_num);
           71                         } else {
           72                                 var hint_elem = hint_elems[hint_num - 1];
           73                                 if (hint_elem != undefined && hint_elem.tagName.toLowerCase() == 'a') {
           74                                         setHighlight(hint_elem, true);
           75                                 }
           76                         }
           77                 }
           78         }
           79 
           80         function setHighlight(elem, is_active) {
           81                 if (is_active) {
           82                         var active_elem = document.body.querySelector('a[highlight=hint_active]');
           83                         if (active_elem != undefined)
           84                                 active_elem.setAttribute('highlight', 'hint_elem');
           85                         elem.setAttribute('highlight', 'hint_active');
           86                 } else {
           87                         elem.setAttribute('highlight', 'hint_elem');
           88                 }
           89 
           90         }
           91 
           92         function setHintRules() {
           93                  if (document.styleSheets.length < 1) {
           94                     var style = document.createElement("style");
           95                     style.appendChild(document.createTextNode(""));
           96                     document.head.appendChild(style);
           97                 }
           98                 var ss = document.styleSheets[0];
           99                 ss.insertRule('a[highlight=hint_elem] {background-color: yellow}', 0);
          100                 ss.insertRule('a[highlight=hint_active] {background-color: lime}', 0);
          101         }
          102 
          103         function deleteHintRules() {
          104                 var ss = document.styleSheets[0];
          105                 ss.deleteRule(0);
          106                 ss.deleteRule(0);
          107         }
          108 
          109         function judgeHintNum(hint_num) {
          110                 var hint_elem = hint_elems[hint_num - 1];
          111                 if (hint_elem != undefined) {
          112                         execSelect(hint_elem);
          113                 } else {
          114                         removeHints();
          115                 }
          116         }
          117 
          118         function execSelect(elem) {
          119                 var tag_name = elem.tagName.toLowerCase();
          120                 var type = elem.type ? elem.type.toLowerCase() : "";
          121                 if (tag_name == 'a' && elem.href != '') {
          122                         setHighlight(elem, true);
          123                         // TODO: ajax, <select>
          124                         if (hint_open_in_new_tab)
          125                                 window.open(elem.href);
          126                         else location.href=elem.href;
          127 
          128                 } else if (tag_name == 'input' && (type == "submit" || type == "button" || type == "reset")) {
          129                         elem.click();
          130                 } else if (tag_name == 'input' && (type == "radio" || type == "checkbox")) {
          131                         // TODO: toggle checkbox
          132                         elem.checked = !elem.checked;
          133                 } else if (tag_name == 'input' || tag_name == 'textarea') {
          134                         elem.focus();
          135                         elem.setSelectionRange(elem.value.length, elem.value.length);
          136                 }
          137                 removeHints();
          138         }
          139 
          140         function setHints() {
          141                 setHintRules();
          142                 var win_top = window.scrollY;
          143                 var win_bottom = win_top + window.innerHeight;
          144                 var win_left = window.scrollX;
          145                 var win_right = win_left + window.innerWidth;
          146                 // TODO: <area>
          147                 var elems = document.body.querySelectorAll('a, input:not([type=hidden]), textarea, select, button');
          148                 var div = document.createElement('div');
          149                 div.setAttribute('highlight', 'hints');
          150                 document.body.appendChild(div);
          151                 for (var i = 0; i < elems.length; i++) {
          152                         var elem = elems[i];
          153                         if (!isHintDisplay(elem))
          154                                 continue;
          155                         var pos = elem.getBoundingClientRect();
          156                         var elem_top = win_top + pos.top;
          157                         var elem_bottom = win_top + pos.bottom;
          158                         var elem_left = win_left + pos.left;
          159                         var elem_right = win_left + pos.left;
          160                         if ( elem_bottom >= win_top && elem_top <= win_bottom) {
          161                                 hint_elems.push(elem);
          162                                 setHighlight(elem, false);
          163                                 var span = document.createElement('span');
          164                                 span.style.cssText = [ 
          165                                         'left: ', elem_left, 'px;',
          166                                         'top: ', elem_top, 'px;',
          167                                         'position: absolute;',
          168                                         'font-size: 13px;',
          169                                         'background-color: ' + (hint_open_in_new_tab ? '#ff6600' : 'red') + ';',
          170                                         'color: white;',
          171                                         'font-weight: bold;',
          172                                         'padding: 0px 1px;',
          173                                         'z-index: 100000;'
          174                                                 ].join('');
          175                                 span.innerHTML = hint_elems.length;
          176                                 div.appendChild(span);
          177                                 if (elem.tagName.toLowerCase() == 'a') {
          178                                         if (hint_elems.length == 1) {
          179                                                 setHighlight(elem, true);
          180                                         } else {
          181                                                 setHighlight(elem, false);
          182                                         }
          183                                 }
          184                         }
          185                 }
          186         }
          187 
          188         function isHintDisplay(elem) {
          189                 var pos = elem.getBoundingClientRect();
          190                 return (pos.height != 0 && pos.width != 0);
          191         }
          192 
          193         function removeHints() {
          194                 if (!hint_enabled)
          195                         return;
          196                 hint_enabled = false;
          197                 deleteHintRules();
          198                 for (var i = 0; i < hint_elems.length; i++) {
          199                         hint_elems[i].removeAttribute('highlight');
          200                 }
          201                 hint_elems = [];
          202                 hint_num_str = '';
          203                 var div = document.body.querySelector('div[highlight=hints]');
          204                 if (div != undefined) {
          205                         document.body.removeChild(div);
          206                 }
          207                 document.removeEventListener('keydown', hintHandler, false);
          208                 document.addEventListener('keydown', initKeyBind, false);
          209         }
          210 
          211         function addKeyBind( key, func, eve ){
          212                 var pressedKey = get_key(eve);
          213                 if( pressedKey == key ){
          214                         eve.preventDefault();  //Stop Default Event 
          215                         eval(func);
          216                 }
          217         }
          218 
          219         document.addEventListener( 'keydown', initKeyBind, false );
          220 
          221         function initKeyBind(e){
          222                 var t = e.target;
          223                 if( t.nodeType == 1){
          224                         addKeyBind( 'C-f', 'hintMode()', e );
          225                         addKeyBind( 'C-F', 'hintMode(true)', e );
          226                         addKeyBind( 'C-c', 'removeHints()', e );
          227                 }
          228         }
          229 
          230         var keyId = {
          231                 "U+0008" : "BackSpace",
          232                 "U+0009" : "Tab",
          233                 "U+0018" : "Cancel",
          234                 "U+001B" : "Esc",
          235                 "U+0020" : "Space",
          236                 "U+0021" : "!",
          237                 "U+0022" : "\"",
          238                 "U+0023" : "#",
          239                 "U+0024" : "$",
          240                 "U+0026" : "&",
          241                 "U+0027" : "'",
          242                 "U+0028" : "(",
          243                 "U+0029" : ")",
          244                 "U+002A" : "*",
          245                 "U+002B" : "+",
          246                 "U+002C" : ",",
          247                 "U+002D" : "-",
          248                 "U+002E" : ".",
          249                 "U+002F" : "/",
          250                 "U+0030" : "0",
          251                 "U+0031" : "1",
          252                 "U+0032" : "2",
          253                 "U+0033" : "3",
          254                 "U+0034" : "4",
          255                 "U+0035" : "5",
          256                 "U+0036" : "6",
          257                 "U+0037" : "7",
          258                 "U+0038" : "8",
          259                 "U+0039" : "9",
          260                 "U+003A" : ":",
          261                 "U+003B" : ";",
          262                 "U+003C" : "<",
          263                 "U+003D" : "=",
          264                 "U+003E" : ">",
          265                 "U+003F" : "?",
          266                 "U+0040" : "@",
          267                 "U+0041" : "a",
          268                 "U+0042" : "b",
          269                 "U+0043" : "c",
          270                 "U+0044" : "d",
          271                 "U+0045" : "e",
          272                 "U+0046" : "f",
          273                 "U+0047" : "g",
          274                 "U+0048" : "h",
          275                 "U+0049" : "i",
          276                 "U+004A" : "j",
          277                 "U+004B" : "k",
          278                 "U+004C" : "l",
          279                 "U+004D" : "m",
          280                 "U+004E" : "n",
          281                 "U+004F" : "o",
          282                 "U+0050" : "p",
          283                 "U+0051" : "q",
          284                 "U+0052" : "r",
          285                 "U+0053" : "s",
          286                 "U+0054" : "t",
          287                 "U+0055" : "u",
          288                 "U+0056" : "v",
          289                 "U+0057" : "w",
          290                 "U+0058" : "x",
          291                 "U+0059" : "y",
          292                 "U+005A" : "z",
          293                 //"U+005B" : "[",
          294                 //"U+005C" : "\\",
          295                 //"U+005D" : "]",
          296                 "U+00DB" : "[",
          297                 "U+00DC" : "\\",
          298                 "U+00DD" : "]",
          299                 "U+005E" : "^",
          300                 "U+005F" : "_",
          301                 "U+0060" : "`",
          302                 "U+007B" : "{",
          303                 "U+007C" : "|",
          304                 "U+007D" : "}",
          305                 "U+007F" : "Delete",
          306                 "U+00A1" : "¡",
          307                 "U+0300" : "CombGrave",
          308                 "U+0300" : "CombAcute",
          309                 "U+0302" : "CombCircum",
          310                 "U+0303" : "CombTilde",
          311                 "U+0304" : "CombMacron",
          312                 "U+0306" : "CombBreve",
          313                 "U+0307" : "CombDot",
          314                 "U+0308" : "CombDiaer",
          315                 "U+030A" : "CombRing",
          316                 "U+030B" : "CombDblAcute",
          317                 "U+030C" : "CombCaron",
          318                 "U+0327" : "CombCedilla",
          319                 "U+0328" : "CombOgonek",
          320                 "U+0345" : "CombYpogeg",
          321                 "U+20AC" : "€",
          322                 "U+3099" : "CombVoice",
          323                 "U+309A" : "CombSVoice",
          324         }
          325 
          326         function get_key(evt){
          327                 var key = keyId[evt.keyIdentifier] || evt.keyIdentifier,
          328                         ctrl = evt.ctrlKey ? 'C-' : '',
          329                         meta = (evt.metaKey || evt.altKey) ? 'M-' : '',
          330                         shift = evt.shiftKey ? 'S-' : '';
          331                 if (evt.shiftKey){
          332                         if (/^[a-z]$/.test(key)) 
          333                                 return ctrl+meta+key.toUpperCase();
          334                         if (/^[0-9]$/.test(key)) {
          335                                 switch(key) {
          336                                         // TODO
          337                                         case "4":
          338                                                 key = "$";
          339                                         break;
          340                                 };
          341                                 return key;
          342                         }
          343                         if (/^(Enter|Space|BackSpace|Tab|Esc|Home|End|Left|Right|Up|Down|PageUp|PageDown|F(\d\d?))$/.test(key)) 
          344                                 return ctrl+meta+shift+key;
          345                 }
          346                 return ctrl+meta+key;
          347         }