/*******************************************

           正規表現でページ内検索
                                 2002/09/21
                                   by kazya
********************************************
[動作]
検索バーに入っている文字列を正規表現としてアク
ティブなタブ（フレームページの場合はアクティブ
なフレーム）を検索します。検索キーワードを空白
で区切ることによってor検索となります。大文字小
文字は区別されません。
Sleipnir1.30以降ではShiftキーを押しながら実行
することによって、逆順に検索することができます。
Ctrlキーを押しながら起動することでマッチした
キーワードをコントロールバー＞アウトプットに出
力します。scripts\OnDoubleClickに同梱のToClip
Board.jsを入れておくと出力されたキーワード上で
ダブルクリックするした際にその内容がクリップボ
ードに送られます。

[設定]
Confirm[trueかfalse] : trueで「次を検索するか」
という確認ボックスが出ますが、続けて検索する際の
検索速度が速くなります。

[注意]
ToClipBoard.jsを利用する場合、WindowsXPを使って
いる方を除いてWSHを最新のバージョンにアップデー
トする必要があります。
http://www.microsoft.com/downloads/release.asp?ReleaseID=33248

また、うまく検索できないことがあります。

[対象]
Sleipnir正式版、および最新ベータ版
*******************************************/
var Confirm = true;

var obj, document, id, window;
obj      = new ActiveXObject("Sleipnir.API");
id       = obj.GetDocumentID(obj.ActiveIndex);
document = obj.GetDocumentObject(id);
window   = obj.GetWindowObject(id);

var section = 'search_re';
var IsFirst = true;


if (document == null) {
	obj.MessageBox('Document が開かれていません');
}else{	
	//フレームページならアクティブなページを検索対象に
	while(document.activeElement.tagName.toLowerCase() == 'frame'){
		document = document.activeElement.contentWindow.document;
	}
	
	do{
		//開かれているURLと検索バーのテキスト、及びDocumentIDが前回と一致しない場合、各種データを初期化
		if (!((obj.GetProfileString(section, 'searchstring', '') == obj.SearchBarString)&&(obj.GetProfileString(section, 'address', '') == obj.URL)&&(obj.GetProfileInt(section, 'id', 0) == id))){
			initialize();
		}
		
		//最初の検索の時にはbookmarkを作成
		if (IsFirst){
			//半角空白、または全角空白で区切られている場合は「|」で置換
			//逆に言えばこれらは検索できない
			var query = '('+obj.SearchBarString.replace(/( |　)/g,'|')+')';
			IsFirst = false;
			var bookmark = new Array();
			var sortdata = new Array();
			var MatchText, ResultList;
			
			MatchText = GetMatchText(document, query);
			ResultList = GetBookmarkData(document, MatchText);
			for (i =0; i < ResultList.length / 2; i++){
				bookmark.push(ResultList[2*i]);
				sortdata.push(ResultList[2*i+1]);
			}
			sortdata.sort(s);
			
			if ((obj.Version != 120)&&(obj.CtrlDown)){
				obj.OutputClear();
				obj.OutputVisible = true;
				obj.OutputAddString(query+'で検索した結果'+bookmark.length+'箇所ヒットしました');
				obj.OutputAddString('マッチしたキーワードは以下の通り');
				for (j = 0; j < MatchText.length; j++){
					obj.OutputAddString(MatchText[j]);
				}
				obj.OutputScript = 'OnDoubleClick\\ToClipBoard.js';
			}
			MatchText  = null;
			ResultList = null;
		}
		//何個目のキーワードまで検索したかを保持
		var flag = obj.GetProfileInt(section, 'flag', 0);
		
		//逆順検索
		if ((obj.Version != 120)&&(obj.ShiftDown)){
			flag = flag -2;
			obj.WriteProfileInt(section, 'flag', flag);
		}
		
		if ((bookmark.length > flag)&&(flag >= 0)){
			var text = document.body.createTextRange();
			sortdata[flag].match(/^-?\d+:(.*):-?\d+$/);
			text.moveToBookmark(bookmark[RegExp.$1]);
			text.select();
			text = null;
			flag++;
			obj.WriteProfileInt(section, 'flag', flag);
			if (!(Confirm)){
				break;
			}
		}else{
			obj.MessageBox('検索が終わりました。\nスクリプトを終了します。');
			obj.WriteProfileInt(section, 'flag', 0);
			break;
		}
		
	}while(window.confirm('次を検索しますか？'));
	bookmark = null;
	sortdata = null;
	document = null;
}
obj = null;
//[MAINEND]

/**
 * GetMatchText(document, query)
 * 正規表現queryでdocumentを検索しマッチする文字列を返す
 * 引数 : document documentオブジェクト
 *        query 検索する文字列。正規表現
 * 返り値 : マッチする文字列を格納する配列。ただし0番目の配列は空文字列
 *
**/
function GetMatchText(document, query){
	var text = document.body.innerText;
	var re = new RegExp(query, 'ig');
	var matchtext = text.match(re);
	var result = new Array();
	var original;
	if (matchtext != null){
		result.push('');
		for (i = 0; i < matchtext.length; i++){
			original = true;
			//すでに同じキーワードが登録されていたら多重登録しない
			for (j = 0; j < result.length; j++){
				if (matchtext[i].toLowerCase() == result[j].toLowerCase()){
					original = false;
					break;
				}
			}
			if (original){
				result.push(matchtext[i]);
			}
		}
	}
	
	re = null;
	return(result);
}

/**
 * GetBookmarkData(document, query)
 * GetMatchTextで取り出したテキストを元にTextRangeオブジェクトのBookmark及び位置データをもどす。
 * 引数 : document documentオブジェクト
          query GetMatchTextの返り値
 * 返り値 : 偶数の要素にgetBookmark()の返り値、奇数の要素に位置データを格納した配列
 *
**/

/*
補足
位置データは「上からの位置:何番目のbookmarkに対応するか:左からの位置」というように格納される
*/

function GetBookmarkData(document, query){
	var obj = new ActiveXObject('Sleipnir.API');
	var result = new Array();
	var text = document.body.createTextRange();
	var temp = 0;
	for (i = 1; i < query.length ; i++){
		while (text.findText(query[i])){
			result.push(text.getBookmark());
			result.push(text.offsetTop+':'+temp+':'+text.offsetLeft);
			text.move('character', 1);
			temp++;
		}
		text.move('textedit', -1);
	}
	obj = null;
	return(result);
}

/**
 * s(a, b)
 * GetBookmarkDataから返ってくる位置データをソートする
 *
**/
function s(a, b){
	if ((a.match(/^-?\d+/) - b.match(/^-?\d+/)) == 0){
		//上からの位置が一致したら、左からの位置でソート
		return(a.match(/-?\d+$/) - b.match(/-?\d+$/));
	}
	return(a.match(/^-?\d+/) - b.match(/^-?\d+/));
}

/**
 * initialize()
 * データの初期化を行う
 *
**/
function initialize(){
	var obj = new ActiveXObject('Sleipnir.API');
	obj.WriteProfileString(section, 'Comment', 'ここのセクションは削除しても動作に支障ありません');
	obj.WriteProfileString(section, 'searchstring', obj.SearchBarString);
	obj.WriteProfileString(section, 'address', obj.URL);
	obj.WriteProfileInt(section, 'id', id);
	obj.WriteProfileInt(section, 'flag', 0);
	obj = null;
	return(0);
}

//[END]	

/*転載、改変は自由

更新履歴
	
	2002/09/21
	・アウトプットを利用しマッチしたテキストを出力できるようにした
	
	2002/09/18
	・逆順に検索できるようにした
	
	2002/09/12
	・うまく検索できない不具合のうち、一部を修正
	
	2002/09/10
	・フレームページの検索に対応
	
	2002/09/08
	・連続して検索するときの速度を向上させるためのオプションをつけた
	・検索するテキストを保存するのは無意味（むしろ逆効果）のようだったので中止
	・英字キーワード検索の際に多重検索してしまうことがあったのを修正
	
	2002/09/06
	・キーワードが複数あった際に、検索していく順番を修正
	・Script.iniを使用するようになった
	・高速化のため実際に検索するテキストを保存しておくようにした（けれど、あまり意味がなさそうです・・・）
	
	2002/09/05
	・公開
*/