お待たせしても、コードのできは、★1っ!? getStringRange

完成間近か!?経過報告。

  • さらなる、むちゃ振りを予測しつつ、文字列による検索において、index値と offset値 がマイナスでも機能するように思案中。
  • 検索位置の目印を、movePointer で移動しておいて、getOffsetPointer で、相対的な位置のノードを取得する
  • 文字列の検索位置からノードを探すより、最初にルート以下のテキストノードとその文字数を配列に保存し、そこから検索したほうがスマートなような気がしてきました。
<!DOCTYPE html>
<title></title>
<body style="background-color:#666">
<h1><em>洋野町</em>(ひろのちょう)</h1>
<h2>プロフィール</h2>
<ul>
<li>
洋野町は、岩手県の中で、最北端に位置し、東側は太平洋に面しています。
<li>
「リアス式海岸」といえば、「三陸」を連想する人もいると思います。残念ながら洋野町は、
そこより北に位置し、範囲に含まれません。そしてこれといって特徴がありません。
<li>
人口は、7月31日現在で、19420人です。(100歳を超えなくても、所在不明な人はいます。予測)
<li>
数年前に、隣の大野村と合併しました。それまでは、種市町(たねいちまち)と呼ばれていました。
<li>
「南部もぐり」と呼ばれ、潜水技術が発展しており、日本中の海洋建設の基礎工事現場などで、本町出身者の方々は活躍しています。
<li>本州最東端は、洋野町から南に100kmほどの地点にある半島です。

</ul>

<script>

(function () {
  var root; // ルート
  var serch; // 検索文字列 or 検索正規表現
  var index; // 
  var offset; // 
  var doc; // 
  var text; // 
  var lastIndex; // 
  var pointer; // 


  //正規表現なのか
  var isRegExp = function ( reg ) {
    return (reg instanceof RegExp);
  };


  // range の作成
  var newRange = function ( ) {
    return doc.createRange ();
  };


  //次のノードを返す
  var getNextNode = function ( node ) {
    var n;

    if (n = node.firstChild)
      return n;

    do {
      if (n = node.nextSibling)
        return n;

      node = node.parentNode;

      if (root == node)
        break;
    }
    while (node);

    return null;
  };


  //前のノードを返す
  var getPreviousNode = function ( node ) {
    var n;

    if (n = node.previousSibling) {
      while (n.hasChildNodes ())
        n = n.lastChild;
      return n;
    }

    node = node.parentNode;
    if (root == node)
      node = null;

    return node;
  };


  // テキストノードを得る
  var getTextNode = function (node, flag) {
    var n = node;
    do {
      n = flag
        ? getPreviousNode (n)
        : getNextNode (n);
      if (! n)
        return null;

    } while (3 !== n.nodeType);
    return n;
  };
  

  // ポインターの位置からのオフセット位置のノードとオフセットを返す
  var getOffsetPointer = function (o) {
    var n = pointer.node;
    var s, p;
    
    if (o < 0) { // オフセットがマイナス
      p = -pointer.offset;
      
      while (p > o) {
        if (n = getTextNode (n, -1))
          p -= n.data.length;

        else
          return { 'node': pointer.node, 'offset': 0 };
      }
      return { 'node': n, 'offset': -p + o };
    }

    else { // オフセットがプラス
      s = pointer.offset;
      p = n.data.length - s;

      while (p < o) {
        if (n = getTextNode (n)) {
          o -= p;
          p += n.data.length;
        }
        else
          return { 'node': pointer.node, 'offset': pointer.node.data.length };
      }
      
      return { 'node': n, 'offset': s + o };
    }
  };


  // p で示される位置まで、テキストノードの文字をカウントしながら移動する
  // ノードとその文字のオフセットを返す
  var movePointer = function (p) {
    var n = pointer.node;
    var max, len;

    do {
      len = n.data.length;
      max = pointer.index + len;
      if (p < max) break;

      pointer.index = max;
      n = getTextNode (n);
      if (! n)
        throw new Error; //テキストノードが終端
    }
    while (true);

    pointer.offset = len - (max - p);
    pointer.node = n;
  };


  // 正規表現で検索
  var collectRegExp = function ( ) {
    var range, obj, words;
    var result = [ ];
    var i, w, o;

    do {
//    search.lastIndex = lastIndex;
      words = search.exec (text);
      
      if (! words)
        break;
      range = newRange ();
      movePointer (words.index) // pointerを移動する
      
      for (i = 0; w = words[i++]; ) {
        o = words[0].indexOf (w);
        
        obj = getOffsetPointer (o);
        range.setStart (obj.node, obj.offset);

        obj = getOffsetPointer (o + w.length);
        range.setEnd (obj.node, obj.offset);

        result.push (range);
//      lastIndex += words[0].length;
      }
    } while (true);

    return result;
  };


  // 文字列で検索
  var collectString = function ( ) {
    var range, obj, words;
    var result = [ ];
    var len = search.length;
    var o = 'undefined' === typeof offset
      ? len - index
      : offset < 0
        ? len - index + offset
        : offset;
      

    do {
      lastIndex = text.indexOf (search, lastIndex);
      if (-1 === lastIndex)
        break;

      range = newRange ();
      movePointer (lastIndex) // pointerを移動する

      obj = getOffsetPointer (index);
      range.setStart (obj.node, obj.offset);

      obj = getOffsetPointer (index + o);
      range.setEnd (obj.node, obj.offset);

      result.push (range);
      lastIndex += len;
      
    } while (true);

    return result;
  };



  //初期設定
  var init = function (a, b, c, d) {

    if (2 > arguments.length)
      return null;

    root   = a;
    search = b;
    index  = c || 0;
    offset = d;
    
    doc    = root.ownerDocument;
    text   = root.textContent;
    lastIndex = 0;
    pointer = { 'node': getTextNode (root), 'index': 0, 'offset': 0 };
    
    return isRegExp (search)
      ? collectRegExp ()
      : collectString ();
  };


  // 選択領域を設定する
  var setSelectRange = function (range) {
    var selection = this.getSelection();
    var i, r;
    
    selection.removeAllRanges (); // 解除
    for (i = 0; r = range[i++]; )
      selection.addRange (r);
  };


  // グローバルに登録
  this.getStringRange = init;
  this.setSelectRange = setSelectRange;

})();

//_______

setSelectRange (getStringRange (document.body, '洋野町', -5, -1));
alert("ok");
setSelectRange (getStringRange (document.body, /(最[東西南北]端)/g));


</script>

友達のヘアーサロンのホームページが検索されるように、ボソッと記載
ビューティーサロン「セピア」です
県外からも大歓迎ってが!