getStringRange 完成か!

かなり製作が遅くなりました。
これでも、ドーピングしました^^;
(リポD×7本以上摂取。)
見直しては、書き換えを繰り返し、そして全面書き直し、
何度繰り返したことか・・・。

<!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 getNextNode = function (node) {
    var n = node.firstChild;

    if (n) return n;

    do {
      if (n = node.nextSibling) return n;
      node = node.parentNode;
      if (this == node) return null; // this == root
    }
    while (node);

    throw new Error; // 親ノードが見つからない
  };


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

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

    node = node.parentNode;
    if (! node)
      throw new Error; // 親ノードが見つからない
    if (this == node) // this == root
      return null;

    return node;
  };


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

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


  // ポインターの位置からのオフセット位置のノードとオフセットを返す
  var getOffsetPointer = function (offset) {
    var pointer = {
      'root'  : this.root,
      'target': this.target,
      'index' : this.index
    };
    var index = this.index + this.offset + offset;
    
    movePointer.call (pointer, index);

    return { 'target': pointer.target, 'offset': pointer.offset };
  };


  // indexで示されるテキストノードと、文字位置を返す
  var movePointer = function (index) {
    var n = this.target;
    var m;
    var max;
    
    if (! n)
      throw new Error; // テキストノードが見つからない

    if (index < this.index) {
      // 前方に検索
      do {
        m = getTextNode.call (this.root, n, true);
        if (! m || index < 0) {
           this.target = n;
           this.index = 0;
           this.offset = 0;
           return this;
        }
        n = m;
        this.index -= n.length;
      }
      while (index < this.index);
    }
   
    else {
      // 後方に検索
      while ((max = this.index + n.length) < index) {
        m = getTextNode.call (this.root, n);
        if (! m) {
          this.target = n;
          this.offset = n.length;
          return this;
        }
        n = m;
        this.index = max;
      }
    }

    this.target = n;
    this.offset = index - this.index;
//  return this;
  };


  // 正規表現で検索
  var getRangeByRegExp = function (search) {
    var doc = this.root.ownerDocument;
    var text = this.root.textContent;
    var target;
    var str;
    var i, offset;
    var result = [ ];
    var range;
    var obj;

    // 検索ループ
    while ((target = search.exec (text))) {
      movePointer.call (this, target.index) // pointerを移動する
      
      for (i = 1; str = target[i++]; ) { // 全体は含めず
        range = doc.createRange ();
        offset = target[0].indexOf (str);

        obj = getOffsetPointer.call (this, offset);
        range.setStart (obj.target, obj.offset);

        obj = getOffsetPointer.call (this, offset + str.length);
        range.setEnd (obj.target, obj.offset);

        result.push (range);
      }
    };

    return result;
  };


  // 文字列で検索
  var getRangeByString = function (search/*String*/, index/*Number*/, offset/*number*/) {
    var doc = this.root.ownerDocument;
    var text = this.root.textContent; // 検索対象の全文字列
    var len = search.length;
    var lastIndex = 0;
    var result = [ ];
    var range;
    var obj;
    
    // index, offset 初期値の設定 offset がマイナスなら最後尾からとする
    index = index || 0;
    offset = 'undefined' === typeof offset
      ? len - index
      : offset < 0
        ? len - index + offset
        : offset;

    // 検索ループ
    while (-1 < (lastIndex = text.indexOf (search, lastIndex))) {
      range = doc.createRange ();

      // pointer を検索対象の最初の位置に移動する
      movePointer.call (this, lastIndex);

      obj = getOffsetPointer.call (this, index); // 検索文字列の最後をセット
      range.setStart (obj.target, obj.offset); // ノードとそのオフセット値をセット

      obj = getOffsetPointer.call (this, index + offset); // 検索文字列の最後をセット
      range.setEnd (obj.target, obj.offset);

      result.push (range);
      lastIndex += len; // 次の検索対象
    }

    return result;
  };


  //search が、文字列 or 正規表現かで振り分ける
  var getRange = function (root, search, index, offset) {
    if (arguments.length < 2)
      return [ ];

    var pointer = { // pointer オブジェクトの作成
      'root'   : root,
      'target' : getTextNode.call (root, root),
      'index'  : 0 /*,
      'offset' : 0 */
    };
    
    return search instanceof RegExp //正規表現なのかで分岐
      ? getRangeByRegExp.call (pointer, search)
      : getRangeByString.call (pointer, search, index, offset);
  };

  // __________________
  
  // 選択領域を設定する
  var setSelectRange = function (range) {
    var selection = this.getSelection (); // this == window
    var i, r;
    
    selection.removeAllRanges (); // 解除

    for (i = 0; r = range[i++]; )
      selection.addRange (r);
  };


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

})();

//_______

setSelectRange (getStringRange (document.body, '洋野町'));
alert("ok");
setSelectRange (getStringRange (document.body, /(洋野|種市)(町)/g));


</script>