Hints を 弄ってみた

気になった点を修正するつもりが、かなり修正しちゃった…

  • tags の 引数に 対象 window を 渡す
  • tags の 返り値が Array 構造 OK(length と 数字 属性があれば なんでも良いはず)
  • tags が Generator に 対応
  • 画面外や非表示のframeを処理しないように修正
  • スクリーンに一部分が表示されているフレームは 表示部位のみhints対象として処理


なので、 tags に

function(win)  win.document.querySelectorAll("a")

function(win) [win.document.body]

function(win){
  for(let [,e] in Iterator(win.document.querySelectorAll("a"))){
    //xpath じゃ不可能な フィルター判定
    yield e;
  }
}

が 登録できます。


また下二つは、多分パフォーマンスにつながると思います。


シビアな位置だと 表示されないことがあるかもしれませんので、
フィードバックしていただけたらと思います。

//_hints-generate-ext.js
(function(){
	let name = "Hints.prototype._generate";
  if(!userContext[name]) userContext[name]=Hints.prototype._generate;

	function evalFunc(src, obj) liberator.eval(["(function()",src,")()"].join(" "),obj)
	Hints.prototype._generate = evalFunc(_generate.toString(), Hints.prototype._generate);

  function _generate(win,screen) {
      if (!win)
          win = window.content;
      if(!screen)
        screen = {top:0,left:0,height:win.innerHeight,width:win.innerWidth};

      let doc = win.document;
      let [offsetX, offsetY] = this._getContainerOffsets(doc);

      let baseNodeAbsolute = util.xmlToDom(<span highlight="Hint"/>, doc);

      function _isVisibleFromElement(elem) _isVisible(elem.getBoundingClientRect())
      function _isVisible(rect){
        return (
          !rect 
          || rect.top > screen.height 
          || rect.bottom < screen.top 
          || rect.left > screen.width 
          || rect.right < screen.left
        ) ? false: true;
      }

      let res = this._hintMode.tags(win);
      if (typeof res == "string") {
          res = function (tags) {var it = util.evaluateXPath(tags, doc, null, true);var elem;while ((elem = it.iterateNext())) {yield elem;}}(res);
      } else if ("length" in res) {
          res = util.Array.itervalues(res);
      }

      let fragment = util.xmlToDom(<div highlight="hints"/>, doc);
      let start = this._pageHints.length;
      let elem;

      for(let elem in res){
          let hint = { elem: elem, showText: false };

          // TODO: for iframes, this calculation is wrong
          if(!_isVisibleFromElement(elem)) continue;

          rect = elem.getClientRects()[0];
          if (!rect)
              continue;

          let computedStyle = doc.defaultView.getComputedStyle(elem, null);
          if (computedStyle.getPropertyValue("visibility") != "visible" || computedStyle.getPropertyValue("display") == "none")
              continue;

          if (elem instanceof HTMLInputElement || elem instanceof HTMLSelectElement || elem instanceof HTMLTextAreaElement)
              [hint.text, hint.showText] = this._getInputHint(elem, doc);
          else
              hint.text = elem.textContent.toLowerCase();

          hint.span = baseNodeAbsolute.cloneNode(true);

          let leftPos = Math.max((rect.left + offsetX), offsetX);
          let topPos =  Math.max((rect.top + offsetY), offsetY);

          if (elem instanceof HTMLAreaElement)
              [leftPos, topPos] = this._getAreaOffset(elem, leftPos, topPos);

          hint.span.style.left = leftPos + "px";
          hint.span.style.top =  topPos + "px";
          fragment.appendChild(hint.span);

          this._pageHints.push(hint);
      }

      let body = doc.body || util.evaluateXPath(["body"], doc).snapshotItem(0);
      if (body) {
          body.appendChild(fragment);
          this._docs.push({ doc: doc, start: start, end: this._pageHints.length - 1 });
      }

      for(let frame in util.Array.itervalues(win.frames)){
        elem = frame.frameElement;
        if(elem.getClientRects().length === 0) continue;
        rect = elem.getBoundingClientRect();
        if(!_isVisible(rect)) continue;

        this._generate(frame, {
          top : Math.max(0, - rect.top),
          left : Math.max(0, - rect.left),
          height: Math.min(frame.innerHeight, screen.height - rect.top),
          width:  Math.min(frame.innerWidth,  screen.width  - rect.left)
        });
      }

      return true;
  }
})();