スクロール処理の拡張

overflow が 指定されているエレメント hjkl で 操作できなかったので、
作ってみた。
h,j,k,l,,,^,$,gg,G は 動いていると思います。
";d"で 選択で、抜けるのは、です。


scrollHeight と offsetHeight あるいは scrollWidth と offsetWidth
一致しないものを対象としてます。
(上手く 動作 するかは 別問題ですが…)
とりあえず、textarea も 動作しました。


また、bufferを弄っているので、異常動作する部位があるかもしれません。


(function(){
  if(!liberator.plugins.caisui) liberator.plugins.caisui={};
  const attr_name="vimperator_scroll_node";
  const COL_WIDTH = 20;
  const LINE_HEIGHT = 32;
  let caisui=liberator.plugins.caisui;
  function hookBefore(obj,attr,func1){
    let func2
    obj[attr] = (func2=obj[attr])
      ? function() func1.apply(obj,arguments) || func2.apply(obj,arguments)
      : func1;
  }
  if(!liberator.plugins.caisui.overflow){
    hints.addMode("d","overflow"
      ,function() caisui.overflow.mark.apply(this,arguments)
      ,function() caisui.overflow.tags.apply(this,arguments)
    );

    for(let f in liberator.modules.util.Array.itervalues([
     "scrollLines","scrollToPercentile","scrollPages","scrollStart","scrollStart","scrollEnd","scrollColumns"
    ])) let(f=f) hookBefore(buffer, f,function() caisui.overflow[f].apply(this,arguments));

    hookBefore(events, "onEscape", function() caisui.overflow.onEscape.apply(this, arguments));
  }

  liberator.plugins.caisui.overflow={
    tags:function(){
      caisui.overflow.addMark(content.window);
      return "//*[@"+attr_name+">'0']";
    },addMark:function(aWindow){
      Array.forEach(aWindow.document.body.getElementsByTagName("*"),function(e){
        if((e.offsetHeight !== e.scrollHeight) ||
          (e.offsetWidth !== e.scrollWidth)){
          e.setAttribute(attr_name,"1");
        }
      });
      Array.forEach(aWindow.frames, arguments.callee);
    },removeMark:function(aWindow){
      if(!aWindow) aWindow=content.window;
      var doc=aWindow.document;
      for(var x in buffer.evaluateXPath("//*[@"+attr_name+"]",doc)){
        x.removeAttribute(attr_name);
      }
      Array.forEach(aWindow.frames,arguments.callee);
    },mark:function(e){
      e.setAttribute(attr_name,"2");
      liberator.echo("mark node!");
    },getElementCore:function(aWindow){
      var e=aWindow.document.evaluate("//*[@"+attr_name+"='2']",aWindow.document,null,9,null).singleNodeValue;
      if(!e) for(var f in liberator.modules.util.Array.itervalues(aWindow.frames)){
        if(e = arguments.callee(f)) break;
      }
      return e;
    },getElement:function() caisui.overflow.getElementCore(content.window)
    ,scrollY:function(e,y){
      var h=e.scrollTop;
      e.scrollTop += y;
      if(h===e.scrollTop) liberator.beep();
    },scrollLines:function(aLines){
      var e=caisui.overflow.getElement();
      if(!e) return false;
      caisui.overflow.scrollY(e, aLines * LINE_HEIGHT);
      return true;
    },scrollPages:function(pages){
      var e=caisui.overflow.getElement();
      if(!e) return false;
      caisui.overflow.scrollY(e, pages*e.offsetHeight);
      return true;
    },scrollToPercentile:function(percentage){
      var e=caisui.overflow.getElement();
      if(!e) return false;
      e.scrollTop = e.scrollHeight*percentage/100;
      return true;
    },scrollStart:function(){
      var e=caisui.overflow.getElement();
      if(!e) return false;
      e.scrollLeft = 0;
      return true;
    },scrollEnd:function(){
      var e=caisui.overflow.getElement();
      if(!e) return false;
      e.scrollLeft = e.scrollWidth;
      return true;
    },scrollColumns:function(cols){
      var e=caisui.overflow.getElement();
      if(!e) return false;
      var w = e.scrollLeft;
      e.scrollLeft += COL_WIDTH * cols;
      if(e.scrollLeft === w) liberator.beep();
      return true;
    },onEscape:function(){
      caisui.overflow.removeMark();
    }
  };
})();