1 /**
  2  * @fileOverview
  3  * JavaScript for EmEditorのためのライブラリです。
  4  * <p>
  5  * EmEditorのマクロフォルダの直下に<code>lib</code>フォルダを作り、そこに配置してください。<br />
  6  * この位置でないと<code>sgc4eesnip</code>からのインクルードが上手くいきません。<br />
  7  * <br />
  8  * 使用する際は、マクロの冒頭で以下のように宣言します。<br />
  9  * <code>#include "lib\sgc4jsee.jsee"</code><br />
 10  * <p>
 11  * 様々な静的メソッドを提供する{@link JseeUtil}クラスが用意されています。<br />
 12  * また、既存のclipboardDataが確実に動作するように、他の既存機能を使った{@link clipboardData}で上書きします。
 13  * </p>
 14  * 
 15  * @author gecca from 雪月花 (http://setsugecca.org/)
 16  * @version 1.00 for EmEditor v10.0
 17  */
 18 
 19 /**
 20  * JavaScript for EmEditorのためのユーティリティクラスです。<br />
 21  * 様々な静的メソッドを提供します。<br />
 22  * <br />
 23  * このクラスのメソッドが返却するディレクトリパスは、全て"\"で終了します。
 24  * 
 25  * @class
 26  */
 27 var JseeUtil = {
 28 	/**
 29 	 * このファイルのファイル名を取得します。
 30 	 * 
 31 	 * @function
 32 	 * 
 33 	 * @example
 34 	 * alert(JseeUtil.fileName());
 35 	 * 
 36 	 * @returns {String} このファイルの名前
 37 	 */
 38 	fileName: function() {
 39 		return "sgc4jsee.jsee";
 40 	},
 41 	
 42 	/**
 43 	 * このファイルが置かれているディレクトリへのパスを取得します。<br />
 44 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。<br />
 45 	 * 一度目の呼び出しで存在チェックを行います。
 46 	 * 
 47 	 * @function
 48 	 * 
 49 	 * @example
 50 	 * alert(JseeUtil.dirPath());
 51 	 * 
 52 	 * @returns {String} このファイルが置かれているディレクトリへのパス
 53 	 */
 54 	dirPath: (function() {
 55 		var cache;
 56 		return function() {
 57 			if(!cache) {
 58 				cache = JseeUtil.getMacroPath() + "lib\\";
 59 				if(!JseeUtil.fso().FolderExists(cache)) {
 60 					throw new Error("sgc4jsee.jseeを格納するディレクトリが下記の場所に見つかりませんでした。\n\n" + cache);
 61 				};
 62 			}
 63 			return cache;
 64 		};
 65 	})(),
 66 	
 67 	/**
 68 	 * このファイルへの絶対パスを取得します。<br />
 69 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。<br />
 70 	 * 一度目の呼び出しで存在チェックを行います。
 71 	 * 
 72 	 * @function
 73 	 * 
 74 	 * @example
 75 	 * alert(JseeUtil.filePath());
 76 	 * 
 77 	 * @returns {String} このファイルへの絶対パス
 78 	 */
 79 	filePath: (function() {
 80 		var cache;
 81 		return function() {
 82 			if(!cache) {
 83 				cache = JseeUtil.dirPath() + JseeUtil.fileName();
 84 				if(!JseeUtil.fso().FileExists(cache)) {
 85 					throw new Error("sgc4jsee.jseeが下記の場所に見つかりませんでした。\n\n" + cache);
 86 				};
 87 			}
 88 			return cache;
 89 		};
 90 	})(),
 91 	
 92 	/**
 93 	 * スニペット用INIファイルのファイル名を取得します。
 94 	 * 
 95 	 * @function
 96 	 * 
 97 	 * @example
 98 	 * alert(JseeUtil.getSnipIniName());
 99 	 * 
100 	 * @returns {String} スニペット用INIファイルのファイル名
101 	 */
102 	getSnipIniName: function() {
103 		return "sgc4eesnip.ini";
104 	},
105 	
106 	/**
107 	 * スニペット用INIファイルへの絶対パスを取得します。<br />
108 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。<br />
109 	 * 一度目の呼び出しで存在チェックを行います。
110 	 * 
111 	 * @function
112 	 * 
113 	 * @example
114 	 * alert(JseeUtil.getSnipIniPath());
115 	 * 
116 	 * @returns {String} スニペット用INIファイルへの絶対パス
117 	 */
118 	getSnipIniPath: (function() {
119 		var cache;
120 		return function() {
121 			if(!cache) {
122 				cache = JseeUtil.dirPath() + JseeUtil.getSnipIniName();
123 				if(!JseeUtil.fso().FileExists(cache)) {
124 					throw new Error("スニペット用INIファイルが下記の場所に見つかりませんでした。\n\n" + cache);
125 				};
126 			}
127 			return cache;
128 		};
129 	})(),
130 	
131 	/**
132 	 * スニペット用INIファイルを読み込み、指定のセレクションを<code>{key1:value1, key2:value2}</code>の形のオブジェクトに変換します。<br />
133 	 * セレクション名は"["と"]"で囲わずに指定します。<br />
134 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
135 	 * <p>
136 	 * {@link JseeUtil.getSnipIniPath}と{@link JseeUtil.getIniObj}を順に呼び出し、指定されたセレクションの情報を抽出します。
137 	 * </p>
138 	 * 
139 	 * @function
140 	 * 
141 	 * @example
142 	 * var ini = JseeUtil.getSnipIniObj("行の2重化");
143 	 * alert(ini.selectOriginalLines);
144 	 * 
145 	 * @param {String} selection セレクション
146 	 * @returns {Object} スニペット用INIファイルの内容から生成した<code>{key1:value1, key2:value2}</code>の形のオブジェクト、読み込みに失敗すれば<code>undefined</code><br />
147 	 * @throws {Error} スニペット用INIファイルに指定されたセレクションが宣言されていない場合にスロー
148 	 */
149 	getSnipIniObj: (function() {
150 		var cache;
151 		return function(selection) {
152 			if(!cache) {
153 				cache = JseeUtil.getIniObj(JseeUtil.getSnipIniPath());
154 			}
155 			
156 			var selectionObj = cache["[" + selection + "]"];
157 			
158 			// セレクションの存在チェック
159 			if(!selectionObj) {
160 				JseeUtil.openFile(JseeUtil.getSnipIniPath());
161 				throw new Error("指定されたセレクション\"" + selection + "\"はスニペット用INIファイルに宣言されていません。\nINIファイルを開きます。");
162 			}
163 			
164 			return selectionObj;
165 		};
166 	})(),
167 	
168 	/**
169 	 * スニペット用INIファイルを開き、指定されたセレクションとキーに該当する値を選択状態にします。<br />
170 	 * 選択が成功すれば<code>true</code>、失敗すれば<code>false</code>を返します。<br />
171 	 * <p>
172 	 * {@link JseeUtil.getSnipIniPath}、{@link JseeUtil.openFile}、{@link JseeUtil.selectIniValue}を呼び出します。
173 	 * </p>
174 	 * 
175 	 * @function
176 	 * 
177 	 * @example
178 	 * JseeUtil.selectSnipIniValue("行の2重化", "selectOriginalLines");
179 	 * 
180 	 * @param {String} selection 選択状態にしたい値が所属するセレクション
181 	 * @param {String} key 選択状態にしたい値を設定しているキー
182 	 * @returns {Boolean} 選択が成功すれば<code>true</code>、失敗すれば<code>false</code>
183 	 */
184 	selectSnipIniValue: function(selection, key) {
185 		JseeUtil.openFile(JseeUtil.getSnipIniPath());
186 		return JseeUtil.selectIniValue(selection, key);
187 	},
188 	
189 	/**
190 	 * 正規表現で使われる文字をエスケープします。
191 	 * <p>
192 	 * ※関数名はJavaのPattern#quote(String)を参考にしました。
193 	 * </p>
194 	 * 
195 	 * @function
196 	 * 
197 	 * @example
198 	 * var arg = "$*";
199 	 * alert("aa\t$*zz".replace(new RegExp("\t" + JseeUtil.quote(arg) + ".*"), ""));
200 	 * 
201 	 * @param {String} target 対象文字列
202 	 * @return {String} エスケープされた文字列
203 	 * @throws {Error} 引数が<code>undefined</code>か<code>null</code>の場合にスロー
204 	 */
205 	quote: (function() {
206 		var reg;
207 		return function(target) {
208 			// String()に掛ける前にundefinedとnullはチェックしておく
209 			if(JseeUtil.isUndef(target)) {
210 				throw new Error("引数にundefinedが指定されました。");
211 			}
212 			if(target === null) {
213 				throw new Error("引数にnullが指定されました。");
214 			}
215 			if(!reg) {
216 				reg = /([\\*+.?!={}()\[\]^$|\/])/g;
217 			}
218 			
219 			return String(target).replace(reg, "\\$1");
220 		}
221 	})(),
222 	
223 	/**
224 	 * 引数が<code>undefined</code>かどうかを判別します。
225 	 * 
226 	 * @function
227 	 * 
228 	 * @example
229 	 * var und;
230 	 * if(JseeUtil.isUndef(und)) {
231 	 *     alert("undefinedです。");
232 	 * }
233 	 * 
234 	 * @param {Object} target 判別対象
235 	 * @returns {Boolean} 判別対象が<code>undefined</code>であれば<code>true</code>、そうでなければ<code>false</code>
236 	 */
237 	isUndef: function(target) {
238 		return (typeof target) === "undefined";
239 	},
240 	
241 	/**
242 	 * 引数に渡された値を、人が認識しやすい文字列に変換します。
243 	 * <ul>
244 	 * <li><code>undefined</code>の場合、"undefined"を返却</li>
245 	 * <li><code>null</code>の場合、"null"を返却</li>
246 	 * <li>正規表現オブジェクトの場合、パターン文字列を{@link JseeUtil.escEscSeq}に掛けて返却</li>
247 	 * <li>{@link Object.toString()}が"[object Object]"でなければ、それを返却</li>
248 	 * <li>{@link Object.toString()}が"[object Object]"であれば、プロパティ解析結果を{@link JseeUtil.escEscSeq}に掛けて、{prop1:value1,prop2:valu2}の形式の文字列として返却</li>
249 	 * </ul>
250 	 * 
251 	 * @function
252 	 * 
253 	 * @example
254 	 * var und;
255 	 * var map = {a: "aaa", b: "bbb", c: {c1: "ccc", c2: und}};
256 	 * alert(JseeUtil.toString(map));
257 	 * 
258 	 * @param {Object} target 変換したい値
259 	 * @returns {String} 人が認識しやすい文字列
260 	 */
261 	toString: function(target) {
262 		var obj2str = function(obj) {
263 			var arr = [];
264 			for(var prop in target) {
265 				arr.push(JseeUtil.escEscSeq(prop) + ":" + JseeUtil.escEscSeq(target[prop]));
266 			}
267 			return "{" + arr.join(",") + "}";
268 		};
269 		
270 		// String()を使うと、"undefined"が出た時にtargetのことかtarget.toString()のことかが分からない
271 		if(JseeUtil.isUndef(target)) {
272 			return "undefined";
273 		} else if(target === null) {
274 			return "null";
275 		} else if(target instanceof RegExp) {
276 			return JseeUtil.escEscSeq(target.source);
277 		} else if(JseeUtil.isUndef(target.toString)) {
278 			try {
279 				return obj2str(target);
280 			} catch(e) {
281 				return "";
282 			}
283 		} else {
284 			var str = target.toString();
285 			if(str == "[object Object]") {
286 				try {
287 					return obj2str(target);
288 				} catch(e) {
289 					return "[object Object]";
290 				}
291 			} else if(typeof str == "string")  {
292 				return str;
293 			} else {
294 				return "{}"
295 			}
296 		}
297 	},
298 	
299 	/**
300 	 * エスケープシーケンスをエスケープします。<br />
301 	 * 対象となるエスケープシーケンスは、"\r"、"\n"、"\t"のみです。
302 	 * 
303 	 * @function
304 	 * 
305 	 * @example
306 	 * var escSeq = "\r\n\t";
307 	 * alert(escSeq);
308 	 * alert(JseeUtil.escEscSeq(escSeq));
309 	 * 
310 	 * @param {String} target エスケープシーケンス
311 	 * @returns {String} エスケープされたエスケープシーケンス
312 	 * @throws {Error} 引数が<code>undefined</code>か<code>null</code>の場合にスロー
313 	 */
314 	escEscSeq: function(target) {
315 		// String()に掛ける前にundefinedとnullはチェックしておく
316 		if(JseeUtil.isUndef(target)) {
317 			throw new Error("引数にundefinedが指定されました。");
318 		}
319 		if(target === null) {
320 			throw new Error("引数にnullが指定されました。");
321 		}
322 		return String(target).replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t");
323 	},
324 	
325 	/**
326 	 * 行選択でない状態から行選択状態にします。複数行の行選択に対応します。
327 	 * 
328 	 * @function
329 	 * 
330 	 * @example
331 	 * JseeUtil.selectLines();
332 	 */
333 	selectLines: function() {
334 		var redraw = Redraw;
335 		Redraw = false;
336 		
337 		var topX = document.selection.GetTopPointX(eePosLogical);
338 		var topY = document.selection.GetTopPointY(eePosLogical);
339 		var bottomX = document.selection.GetBottomPointX(eePosLogical);
340 		var bottomY = document.selection.GetBottomPointY(eePosLogical);
341 		
342 		if(topX == 1 && bottomX == 1 && topY != bottomY) {
343 			// 行選択の場合は選択し直す
344 			document.selection.SetActivePoint(eePosLogical, 1, topY);
345 			document.selection.SetActivePoint(eePosLogical, 1, bottomY, true);
346 		} else {
347 			var nextTopX = 1;
348 			var nextTopY = topY;
349 			var nextBottomX = (bottomX == 1 || bottomY != document.getLines() ? 1 : document.getLine(bottomY).length + 1);
350 			var nextBottomY = ((topX == 1 || bottomX != 1) && bottomY != document.getLines() ? bottomY + 1 : bottomY);
351 			document.selection.SetActivePoint(eePosLogical, nextTopX, nextTopY);
352 			document.selection.SetActivePoint(eePosLogical, nextBottomX, nextBottomY, true);
353 		}
354 		
355 		Redraw = redraw;
356 	},
357 	
358 	/**
359 	 * アウトプットバーにログを出力するロガーを取得します。
360 	 * <p>
361 	 * ロガーはlog関数を持ち、その関数の引数にアウトプットバーに出力する値を指定します。<br />
362 	 * available関数を判別してからlog関数を呼び出すと、高速化が見込めます。
363 	 * </p>
364 	 * 
365 	 * @function
366 	 * 
367 	 * @example
368 	 * var logger = getLogger(true, "hoge.jsee");
369 	 * logger.log("デバッグ中です。");
370 	 * if(logger.available()) {
371 	 *     logger.log(proc(proc1() + proc2()));
372 	 * }
373 	 * 
374 	 * @param {Boolean} available ログ出力を有効にする場合は<code>true</code>、無効にする場合は<code>false</code>
375 	 * @param {String} [scope] "ファイル名 クラス名 関数名"といった風に、ロガーが使われるスコープを好きに指定 (省略可能)
376 	 * @return <code>log(Object)</code>関数と<code>available()</code>関数を持つオブジェクト
377 	 */
378 	getLogger: (function() {
379 		var first = true;
380 		return function(available, scope) {
381 			if(available) {
382 				OutputBar.Visible = true;
383 				if(first) {
384 					OutputBar.writeln("\n================================================================================================================================");
385 					OutputBar.writeln("|| " + JseeUtil.dateFormat(new Date(), "yyyy/MM/dd HH:mm:ss"));
386 					OutputBar.writeln("================================================================================================================================");
387 					first = false;
388 				}
389 				
390 				var head = scope ? "||" + scope + "||" : "";
391 				return {
392 					log: function(obj) {OutputBar.writeln(head + JseeUtil.toString(obj))},
393 					available: function() {return true;}
394 				}
395 			} else {
396 				return {
397 					log: function() {},
398 					available: function() {return false;}
399 				};
400 			}
401 		};
402 	})(),
403 	
404 	/**
405 	 * アウトプットバーにログを出力します。<br />
406 	 * {@link JseeUtil.getLogger}を使うまでもない場合に使用します。
407 	 * 
408 	 * @function
409 	 * 
410 	 * @example
411 	 * JseeUtil.log("hoge");
412 	 * 
413 	 * @param {Object} obj ログに表示する情報
414 	 */
415 	log: function(obj) {
416 		OutputBar.Visible = true;
417 		OutputBar.writeln(JseeUtil.toString(obj));
418 	},
419 	
420 	/**
421 	 * 日付をフォーマットに沿って文字列化します。
422 	 * <p>サポートするフォーマットは以下に挙げたもののみです。</p>
423 	 * <ul>
424 	 * <li>yyyy</li>
425 	 * <li>yy</li>
426 	 * <li>y</li>
427 	 * <li>MM</li>
428 	 * <li>M</li>
429 	 * <li>dd</li>
430 	 * <li>d</li>
431 	 * <li>HH</li>
432 	 * <li>H</li>
433 	 * <li>mm</li>
434 	 * <li>m</li>
435 	 * <li>ss</li>
436 	 * <li>s</li>
437 	 * <li>SSS</li>
438 	 * <li>S</li>
439 	 * </ul>
440 	 * 
441 	 * @function
442 	 * 
443 	 * @example
444 	 * var date = new Date();
445 	 * var dateString = JseeUtil.dateFormat(date, "yyyy/MM/dd HH:mm:ss.SSS");
446 	 * alert(dateString);
447 	 * dateString = JseeUtil.dateFormat(date, "y/M/d H:m:s.S");
448 	 * alert(dateString);
449 	 * 
450 	 * @param {Date} date 日付
451 	 * @param {String} format フォーマット
452 	 * @returns {String} 日付をフォーマットに沿って文字列化したもの
453 	 * @throws {Error} 引数に異常があった場合にスロー
454 	 */
455 	dateFormat: function(date, format) {
456 		// 引数チェック
457 		var errors = [];
458 		if(arguments.length != 2) errors.push("引数は2つ指定してください。");
459 		else {
460 			if(!(date instanceof Date)) errors.push("日付がDate型ではありません。");
461 			if(typeof format != "string") errors.push("フォーマットがString型ではありません。");
462 		}
463 		if(errors.length > 0) throw new Error(errors.join("\n"));
464 		
465 		var onechar = /^(.)$/;
466 		var hash = {};
467 		
468 		// プロパティの定義順と置換順は同じ!
469 		hash.yyyy = String(date.getYear());
470 		hash.yy = /..$/.exec(hash.yyyy)[0];
471 		hash.y = hash.yy.replace(/^0/g, "");
472 		var M = String(date.getMonth() + 1);
473 		hash.MM = M.replace(onechar, "0$1");
474 		hash.M = M;
475 		var d = String(date.getDate());
476 		hash.dd = d.replace(onechar, "0$1");
477 		hash.d = d;
478 		var H = String(date.getHours());
479 		hash.HH = H.replace(onechar, "0$1");
480 		hash.H = H;
481 		var m = String(date.getMinutes());
482 		hash.mm = m.replace(onechar, "0$1");
483 		hash.m = m;
484 		var s = String(date.getSeconds());
485 		hash.ss = s.replace(onechar, "0$1");
486 		hash.s = s;
487 		var S = String(date.getMilliseconds());
488 		hash.SSS = S.replace(onechar, "0$1").replace(/^(..)$/, "0$1");
489 		hash.S = S;
490 		
491 		for(var prop in hash) format = format.split(prop).join(hash[prop]);
492 		return format;
493 	},
494 	
495 	/**
496 	 * マクロフォルダのパスを取得します。同時にパスの存在チェックも行います。<br />
497 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
498 	 * 
499 	 * @function
500 	 * 
501 	 * @example
502 	 * var macroPath = JseeUtil.getMacroPath();
503 	 * 
504 	 * @throws {Error} マクロフォルダのパスが読み込めなかった場合にスロー
505 	 * @throws {Error} マクロフォルダのパスが存在しなかった場合にスロー
506 	 */
507 	getMacroPath: (function() {
508 		var cache;
509 		return function() {
510 			if(JseeUtil.isUndef(cache)) {
511 				cache = editor.GetProfileString(eeRegCommon, "", "MacroFolder", "") + "\\";
512 				if(!cache) {
513 					throw new Error("マクロフォルダのパスが設定されていません。");
514 				}
515 				// 相対パスの場合はEmEditorホームからの絶対パスに変換
516 				if(cache.indexOf(".\\") == 0) {
517 					cache = cache.replace(/^\.\\/, JseeUtil.getEditorPath);
518 				}
519 				if(!JseeUtil.fso().FolderExists(cache)) {
520 					throw new Error("設定されている下記のマクロフォルダは存在しません。\n\n" + cache);
521 				}
522 			}
523 			return cache;
524 		};
525 	})(),
526 	
527 	/**
528 	 * EmEditorのフォルダパスを返却します。<br />
529 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
530 	 * 
531 	 * @function
532 	 * 
533 	 * @example
534 	 * var editorPath = JseeUtil.getEditorPath();
535 	 */
536 	getEditorPath: (function() {
537 		var cache;
538 		return function() {
539 			if(JseeUtil.isUndef(cache)) {
540 				cache = editor.FullName.replace(/[^\\]*$/, "");
541 			}
542 			return cache;
543 		};
544 	})(),
545 	
546 	/**
547 	 * EmEditorの実行ファイルの名前を返却します。<br />
548 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
549 	 * 
550 	 * @function
551 	 * 
552 	 * @example
553 	 * var scriptPath = JseeUtil.getScriptPath();
554 	 */
555 	getEditorName: (function() {
556 		var cache;
557 		return function() {
558 			if(JseeUtil.isUndef(cache)) {
559 				cache = editor.FullName.replace(/^.*\\(.*?)$/, "$1");
560 			}
561 			return cache;
562 		};
563 	})(),
564 	
565 	/**
566 	 * 実行中のスクリプトが格納されているディレクトリパスを返却します。<br />
567 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
568 	 * 
569 	 * @function
570 	 * 
571 	 * @example
572 	 * var scriptPath = JseeUtil.getScriptPath();
573 	 */
574 	getScriptPath: (function() {
575 		var cache;
576 		return function() {
577 			if(JseeUtil.isUndef(cache)) {
578 				cache = ScriptFullName.replace(/[^\\]*$/, "");
579 			}
580 			return cache;
581 		}
582 	})(),
583 	
584 	/**
585 	 * 開いているドキュメントに設定されたバックアップフォルダへのパスを取得します。<br />
586 	 * 取得できなければ空文字を返します。
587 	 * 
588 	 * @function
589 	 * 
590 	 * @example
591 	 * var backupDirPath = JseeUtil.getBackupDirPath();
592 	 * 
593 	 * @returns {String} バックアップフォルダへのパス
594 	 */
595 	getBackupDirPath: function() {
596 		return document.Config.Backup.Folder;
597 	},
598 	
599 	/**
600 	 * テンポラリフォルダへのパスを取得します。<br />
601 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
602 	 * 
603 	 * @function
604 	 * 
605 	 * @example
606 	 * var tempDirPath = JseeUtil.getTempDirPath();
607 	 * 
608 	 * @returns {String} テンポラリフォルダへのパス
609 	 */
610 	getTempDirPath: (function() {
611 		var cache;
612 		return function() {
613 			if(!cache) {
614 				cache = JseeUtil.fso().GetSpecialFolder(2).Path + "\\";
615 			}
616 			return cache;
617 		};
618 	})(),
619 	
620 	/**
621 	 * ファイルを開きます。<br />
622 	 * 指定されたパスが存在しない場合は例外を投げます。
623 	 * 
624 	 * @function
625 	 * 
626 	 * @example
627 	 * JseeUtil.openFile("C:\\hoge.txt");
628 	 * 
629 	 * @param {String} path 開くファイルのパス
630 	 * @throws {Error} 指定されたパスが存在しない場合にスロー
631 	 */
632 	openFile: function(path) {
633 		if(!JseeUtil.fso().FileExists(path)) {
634 			throw new Error("以下のファイルを開こうとしましたが存在しませんでした。\n" + path);
635 		}
636 		editor.OpenFile(path, undefined, eeOpenAllowNewWindow);
637 	},
638 	
639 	/**
640 	 * 外部アプリを実行し、標準出力と標準エラーの情報を取得します。<br />
641 	 * 返却値は右記の形のオブジェクトです。<code>{out: exeが出力した標準出力, err: exeが出力した標準エラー}</code><br />
642 	 * <p>
643 	 * exeのファイルパスに空白が含まれる場合、必ずダブルクォーテーションで囲ってください。
644 	 * </p>
645 	 * <p>
646 	 * 実行したアプリを終了させるまでEmEditorの制御は復帰しません。
647 	 * </p>
648 	 * 
649 	 * @function
650 	 * 
651 	 * @example
652 	 * // 以下を実行すると、"test"がダイアログ表示されます。
653 	 * alert(JseeUtil.exec("cmd /c echo test").out);
654 	 * 
655 	 * // 以下を実行すると、メモ帳を閉じるまでEmEditorが待機します。
656 	 * JseeUtil.exec("notepad");
657 	 * 
658 	 * @param {String} execSentence 外部アプリへのファイルパスや、外部アプリに渡すパラメータ
659 	 * @returns {Object} {out: 外部アプリが出力した標準出力, err: 外部アプリが出力した標準エラー}
660 	 * @throws {Error} 実行ファイルへのパスが間違っている場合にスロー
661 	 */
662 	exec: function(execSentence) {
663 		var logger = JseeUtil.getLogger(false, "sgc4jsee.jsee JseeUtil.exec");
664 		
665 		var shell = JseeUtil.shell();
666 		
667 		// 実行
668 		logger.log("実行パス:" + execSentence);
669 		try {
670 			var execed = shell.Exec(execSentence);
671 		} catch(e) {
672 			// 「指定されたファイルが見つかりません。」の場合
673 			if(e.message.match(/指定されたファイルが見つかりません。\r\n/)) {
674 				throw new Error("以下のコマンドを実行しようとしましたが失敗しました。パスに空白が入っている場合は\"\"で囲ってください。\n\n" + execSentence);
675 			// それ以外の場合
676 			} else {
677 				throw e;
678 			}
679 		}
680 		
681 		// 標準情報を返却 (JseeUtil.readAllでアプリの終了まで読み込み続ける)
682 		return {
683 			out: JseeUtil.readAll(execed.StdOut),
684 			err: JseeUtil.readAll(execed.StdErr)
685 		};
686 	},
687 	
688 	/**
689 	 * 数値をゼロ埋めします。
690 	 * 
691 	 * @function
692 	 * 
693 	 * @example
694 	 * alert(JseeUtil.zeroFill(1, 3)); // "001"
695 	 * 
696 	 * @param {Number} target ゼロ埋め対象となる数値 (String型も許可します。)
697 	 * @param {Number} digit ゼロ埋め後の桁数
698 	 * @returns {String} ゼロ埋めした数値
699 	 */
700 	zeroFill: function(target, digit) {
701 		var targetNum = Number(target);
702 		if(targetNum >= 0) {
703 			var targetStr = String(targetNum);
704 		} else {
705 			var targetStr = String(targetNum * -1);
706 		}
707 		
708 		var zeros = [];
709 		for(var i=targetStr.length;i<digit;zeros[i++] = "0");
710 		
711 		if(targetNum >= 0) {
712 			return zeros.join("") + targetStr;
713 		} else {
714 			return "-" + zeros.join("") + targetStr;
715 		}
716 	},
717 	
718 	/**
719 	 * 配列に指定の要素が含まれるかどうかを調べます。(比較には"=="を使用します。)
720 	 * 
721 	 * @function
722 	 * 
723 	 * @example
724 	 * if(JseeUtil.contains(array, 1)) {
725 	 *     alert("1が含まれています。");
726 	 * }
727 	 * 
728 	 * @param {Object[]} array 対象となる配列
729 	 * @param {Object} item 探す要素
730 	 * @returns {Boolean} 指定の要素が含まれていれば<code>true</code>、そうでなければ<code>false</code>
731 	 * @throws {Error} 指定された配列がArray型でない場合にスロー
732 	 */
733 	contains: function(array, item) {
734 		if(!(array instanceof Array)) {
735 			throw new Error("指定された配列がArray型ではありません。");
736 		}
737 		
738 		for(var i=0;i<array.length;i++) {
739 			if(array[i] == item) {
740 				return true;
741 			}
742 		}
743 		return false;
744 	},
745 	
746 	/**
747 	 * 各要素のアクセスに<code>Enumerator</code>を使用する必要のある<code>Collection</code>オブジェクトを配列に変換します。
748 	 * 
749 	 * @function
750 	 * 
751 	 * @example
752 	 * var subFolders = JseeUtil.col2arr(folder.SubFolders);
753 	 * for(var i=0;i<subFolders.length;i++) {
754 	 *     alert(subFolders[i].Name);
755 	 * }
756 	 * 
757 	 * @param {Collection} col2arr <code>Collection</code>オブジェクト
758 	 * @returns {Object[]} 配列
759 	 */
760 	col2arr: function(collection) {
761 		var arr = [];
762 		for(var e = new Enumerator(collection);!e.atEnd();e.moveNext()) arr.push(e.item());
763 		return arr;
764 	},
765 	
766 	/**
767 	 * INIファイルを読み込み、<code>{key1:value1, key2:value2}</code>の形のオブジェクトに変換します。<br />
768 	 * "[selection]"というセレクションに"key"が定義されていれば、<code>obj.["[selection]"].key</code>の形で参照します。<br />
769 	 * ";"か"#"で始まる文字列をコメントとして解釈し、コメントのエスケープや、行の途中からのコメントもサポートします。
770 	 * 
771 	 * <p>
772 	 * パスの検索は以下の順番で行い、見つかればそれをインクルードします。<br />
773 	 * <ul>
774 	 * <li>実行しているマクロと同じディレクトリからの相対パスとして探す</li>
775 	 * <li>絶対パスとして探す</li>
776 	 * </ul>
777 	 * </p>
778 	 * 
779 	 * @function
780 	 * 
781 	 * @example
782 	 * // INIファイル内容
783 	 * key1=value1
784 	 * key2
785 	 * ;セレクション1
786 	 * [selection1]
787 	 * key3 = value3
788 	 * 
789 	 * // マクロ内容
790 	 * var ini = JseeUtil.getIniObj("INIファイル.ini");
791 	 * alert(ini.key1);                  // "value1"
792 	 * alert(ini.key2);                  // ""
793 	 * alert(ini.["[selection1]"].key3); // "value3"
794 	 * alert(ini.key3);                  // undefined
795 	 * 
796 	 * @param {String} iniFilePath INIファイルへの相対パス
797 	 * @returns {Object} <code>{key1:value1, key2:value2}</code>の形のオブジェクト、読み込みに失敗すれば<code>undefined</code><br />
798 	 * @throws {Error} 指定されたパスが、実行しているマクロと同じディレクトリからの相対パスでも、マクロディレクトリからの相対パスでもない場合にスロー
799 	 */
800 	getIniObj: function(iniFilePath) {
801 		var logger = JseeUtil.getLogger(false, "sgc4jsee.jsee JseeUtil.getIniObj");
802 		
803 		var targetFilePath = (function() {
804 			var fso = JseeUtil.fso();
805 			
806 			// 実行しているマクロと同じディレクトリからの相対パスとして探す
807 			var relativePath = JseeUtil.getScriptPath() + iniFilePath;
808 			if(fso.FileExists(relativePath)) {
809 				logger.log("実行しているマクロと同じディレクトリからの相対パスとして開きます。");
810 				return relativePath;
811 			}
812 			
813 			// 見つからなければ、絶対パスとして探す
814 			if(fso.FileExists(iniFilePath)) {
815 				logger.log("絶対パスとして開きます。");
816 				return iniFilePath;
817 			}
818 			
819 			// それでも見つからなければ、例外とする
820 			throw new Error("指定された下記のパスは、実行しているマクロと同じディレクトリからの相対パスでも、マクロディレクトリからの相対パスでもありません。\n\n" + iniFilePath);
821 		})();
822 		
823 		// 対象パスが見つかればファイルを読み込んでObjectに変換
824 		var iniFileContent = JseeUtil.readFile(targetFilePath);
825 		logger.log("↓iniFileContent\n" + iniFileContent);
826 		
827 		// INIファイルを行の配列に変換 (文字のある行のみを改行抜きで配列化)
828 		var lines = iniFileContent.split(/(\r\n|\r|\n)/);
829 		
830 		// INIファイルの内容を格納するオブジェクト
831 		var obj = {};
832 		// 現在のセレクション
833 		var currentSelection;
834 		
835 		for(var i=0;i<lines.length;i++) {
836 			// コメントを削除して、エスケープされたコメントを元に戻す
837 			var line = lines[i].replace(/(^|[^\\])(;|#).*/, "$1").replace(/\\(;|#)/g, "$1");
838 			logger.log("line:" + line);
839 			
840 			// キーと値にマッチ
841 			var m = line.match(/^\s*([^\s][^=]*?)\s*=\s*(.*?)$/);
842 			
843 			if(m) {
844 				var key = m[1];
845 				var value = (m[2] !== null) ? m[2].replace(/^ *(.*?) *$/g, "$1") : "";;
846 				
847 				// 現在のセレクションがあれば、その中に設定
848 				if(currentSelection) {
849 					obj[currentSelection][key] = value;
850 				} else {
851 					obj[key] = value;
852 				}
853 			
854 			// キーがセレクションの形であれば、それを現在のセレクションに設定
855 			} else if(line.match(/(\[.*\])/)) {
856 				currentSelection = RegExp.$1;
857 				obj[currentSelection] = {};
858 				logger.log("currentSelection:" + currentSelection);
859 			}
860 		}
861 		
862 		if(logger.available()) {
863 			logger.log("");
864 			logger.log("■INIファイル内容");
865 			for(var prop in obj) {
866 				logger.log(prop);
867 				for(var childProp in obj[prop]) {
868 					logger.log("    " + JseeUtil.toString(childProp));
869 					logger.log("        " + JseeUtil.toString(obj[prop][childProp]));
870 				}
871 			}
872 		}
873 		
874 		return obj;
875 	},
876 	
877 	/**
878 	 * ポップアップメニューを表示します。<br />
879 	 * 引数には[["menuText1", function1, arguments1], ["menuText2", function2, arguments2]]の形の二次元配列を指定します。<br />
880 	 * 
881 	 * @function
882 	 * 
883 	 * @example
884 	 * function hello(a, b) {
885 	 *     alert(a + b);
886 	 * }
887 	 * function bye() {
888 	 *     alert("Bye");
889 	 * }
890 	 * JseeUtil.createPopupMenu(
891 	 *     [["Helloを表示", hello, "He", "llo"],
892 	 *     ["Byeを表示", bye]]
893 	 * );
894 	 * 
895 	 * @param {Object[]} menuItems メニュー項目配列 ([["menuText1", function1, arguments1], ["menuText2", function2, arguments2]]の形の二次元配列)
896 	 * @param {Boolean} [willPopupAtCaret=false] キャレット位置にポップアップを表示する場合は<code>true</code> (省略可能)
897 	 * @throws {Error} 引数が誤った形式である場合にスロー
898 	 */
899 	createPopupMenu: function(menuItems, willPopupAtCaret) {
900 		// 配列の宣言が[a, b, c,]とカンマで終わっている場合を許容する
901 		if(JseeUtil.isUndef(menuItems[menuItems.length - 1])) {
902 			menuItems.pop();
903 		}
904 		
905 		// 引数チェック
906 		if(!(menuItems instanceof Array)) {
907 			throw new Error("引数には配列を指定してください。");
908 		}
909 		for(var i=0;i<menuItems.length;i++) {
910 			if(!(menuItems[i] instanceof Array)) {
911 				throw new Error("引数の配列の" + (i + 1) + "行目が配列ではありません。");
912 			}
913 			if(menuItems[i].length < 2) {
914 				throw new Error("引数の配列の" + (i + 1) + "行目の配列の長さが2より小さい" + menuItems[i].length + "です。");
915 			}
916 			if(typeof menuItems[i][1] != "function") {
917 				throw new Error("引数の[" + i + ", 1]の位置の値が関数ではなく" + typeof menuItems[i][1] + "です。");
918 			}
919 		}
920 		
921 		// メニューを生成し、メニューアイテムを設定
922 		var menu = CreatePopupMenu();
923 		for(var i=0;i<menuItems.length;i++) {
924 			menu.Add(JseeUtil.toString(menuItems[i][0]), i + 1);
925 		}
926 		
927 		// メニューを表示
928 		var selectedIndex = menu.Track(!willPopupAtCaret ? eePosMouse : undefined);
929 		
930 		// 選択値を元に関数呼び出し
931 		if(selectedIndex) {
932 			var args = [];
933 			for(var i=2;i<menuItems[selectedIndex - 1].length;i++) {
934 				args.push(menuItems[selectedIndex - 1][i]);
935 			}
936 			menuItems[selectedIndex - 1][1].apply(this, args);
937 		}
938 	},
939 	
940 	/**
941 	 * <code>new ActiveXObject("WScript.Shell");</code>を呼び出します。<br />
942 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
943 	 * 
944 	 * @function
945 	 * 
946 	 * @example
947 	 * var shell = JseeUtil.shell();
948 	 * shell.Run("notepad.exe");
949 	 * 
950 	 * @returns {Object} WSHのシェルオブジェクト
951 	 */
952 	shell: (function() {
953 		var cache;
954 		return function() {
955 			if(!cache) {
956 				cache = new ActiveXObject("WScript.Shell");
957 			}
958 			return cache;
959 		};
960 	})(),
961 	
962 	/**
963 	 * <code>new ActiveXObject("Scripting.FileSystemObject");</code>を呼び出します。<br />
964 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
965 	 * 
966 	 * @function
967 	 * 
968 	 * @example
969 	 * var fso = JseeUtil.fso();
970 	 * alert(fso.getFolder(".").Path);
971 	 * 
972 	 * @returns {Object} WSHのファイルシステムオブジェクト
973 	 */
974 	fso: (function() {
975 		var cache;
976 		return function() {
977 			if(!cache) {
978 				cache = new ActiveXObject("Scripting.FileSystemObject");
979 			}
980 			return cache;
981 		};
982 	})(),
983 	
984 	/**
985 	 * インデントに使われる文字列を取得します。
986 	 * 
987 	 * @function
988 	 * 
989 	 * @example
990 	 * var indent = JseeUtil.getIndent();
991 	 * alert(indent);
992 	 * 
993 	 * @returns {String} インデント文字列
994 	 */
995 	getIndent: function() {
996 		// インデントが空白の場合
997 		if(document.Config.Indent.InsertSpaces) {
998 			var spaces = [];
999 			for(var i=0;i<document.Config.Indent.TabColumns;i++) spaces.push(" ");
1000 			return spaces.join("");
1001 		
1002 		// インデントがタブの場合
1003 		} else {
1004 			return "\t";
1005 		}
1006 	},
1007 	
1008 	/**
1009 	 * ファイルを読み込みます。<br />
1010 	 * ファイルが存在しなければ空文字を返します。
1011 	 * 
1012 	 * @function
1013 	 * 
1014 	 * @example
1015 	 * var tempFilePath = JseeUtil.getTempDirPath() + "EmEditorMacros_sample.tmp";
1016 	 * JseeUtil.writeFile(tempFilePath, "hogehoge");
1017 	 * JseeUtil.appendFile(tempFilePath, "\nfugafuga");
1018 	 * var content = JseeUtil.readFile(tempFilePath);
1019 	 * alert(content);
1020 	 * 
1021 	 * @param {String} fileName ファイル名
1022 	 * @returns {String} 読み込んだファイル内容
1023 	 */
1024 	readFile: function(filePath) {
1025 		var fso = JseeUtil.fso();
1026 		if(!fso.FileExists(filePath)) {
1027 			return "";
1028 		}
1029 		var stream = fso.OpenTextFile(filePath, 1);
1030 		var result = JseeUtil.readAll(stream);
1031 		stream.Close();
1032 		return result;
1033 	},
1034 	
1035 	/**
1036 	 * ファイルに書き込みます。<br />
1037 	 * 既に存在するファイルパスが指定された場合は上書きします。<br />
1038 	 * 存在しないファイルパスが指定された場合は、フォルダ及びファイルを作成します。
1039 	 * 
1040 	 * @function
1041 	 * 
1042 	 * @example
1043 	 * var tempFilePath = JseeUtil.getTempDirPath() + "EmEditorMacros_sample.tmp";
1044 	 * JseeUtil.writeFile(tempFilePath, "hogehoge");
1045 	 * JseeUtil.appendFile(tempFilePath, "\nfugafuga");
1046 	 * var content = JseeUtil.readFile(tempFilePath);
1047 	 * alert(content);
1048 	 * 
1049 	 * @param {String} filePath ファイルへの絶対パス
1050 	 * @param {String} content 書き込む内容
1051 	 */
1052 	writeFile: function(filePath, content) {
1053 		var shell = JseeUtil.shell();
1054 		var fso = JseeUtil.fso();
1055 		// ディレクトリ階層をmkdirで作成
1056 		if(filePath.match(/^(.+)\\([^\\]*)$/) && !fso.FolderExists(RegExp.$1)) {
1057 			shell.Run("cmd /c mkdir " + RegExp.$1, 0, true);
1058 		}
1059 		var stream = fso.OpenTextFile(filePath, 2, true);
1060 		JseeUtil.writeAll(stream, content);
1061 		stream.Close();
1062 	},
1063 	
1064 	/**
1065 	 * ファイルに追加で書き込みます。<br />
1066 	 * 存在しないファイルパスが指定された場合は、フォルダ及びファイルを作成します。
1067 	 * 
1068 	 * @function
1069 	 * 
1070 	 * @example
1071 	 * var tempFilePath = JseeUtil.getTempDirPath() + "EmEditorMacros_sample.tmp";
1072 	 * JseeUtil.writeFile(tempFilePath, "hogehoge");
1073 	 * JseeUtil.appendFile(tempFilePath, "\nfugafuga");
1074 	 * var content = JseeUtil.readFile(tempFilePath);
1075 	 * alert(content);
1076 	 * 
1077 	 * @param {String} filePath ファイルへの絶対パス
1078 	 * @param {String} content 追加で書き込む内容
1079 	 */
1080 	appendFile: function(filePath, content) {
1081 		try {
1082 			var fso = JseeUtil.fso();
1083 			var stream = fso.OpenTextFile(filePath, 8);
1084 			JseeUtil.writeAll(stream, content);
1085 			stream.Close();
1086 		} catch(e) {
1087 			if(e.message == "パスが見つかりません。" || e.message == "ファイルが見つかりません。") {
1088 				throw new Error("追加で書き込む対象のファイルが下記の場所に見つかりませんでした。\n\n" + filePath);
1089 			} else {
1090 				throw e;
1091 			}
1092 		}
1093 	},
1094 	
1095 	/**
1096 	 * 指定のストリームから全て読み込みます。<br />
1097 	 * <code>stream.ReadAll()</code>と同じ機能ですが、バッファサイズの問題を起こりにくくします。
1098 	 * 
1099 	 * @function
1100 	 * 
1101 	 * @example
1102 	 * var stream = fso.OpenTextFile(filePath, 1);
1103 	 * var result = JseeUtil.readAll(stream);
1104 	 * stream.Close();
1105 	 * alert(result);
1106 	 * 
1107 	 * @param {Stream} stream ストリーム
1108 	 * @returns {String} 読み込んだ内容
1109 	 */
1110 	readAll: function(stream) {
1111 		var buf = "";
1112 		while(!stream.AtEndOfStream) {
1113 			buf += stream.Read(102400);
1114 		}
1115 		return buf;
1116 	},
1117 	
1118 	/**
1119 	 * 指定のストリームに書き込みます。<br />
1120 	 * <code>stream.Write()</code>と同じ機能ですが、バッファサイズの問題を起こりにくくします。
1121 	 * 
1122 	 * @function
1123 	 * 
1124 	 * @example
1125 	 * var stream = fso.OpenTextFile(filePath, 2, true);
1126 	 * JseeUtil.writeAll(stream, "content");
1127 	 * stream.Close();
1128 	 * 
1129 	 * @param {Stream} stream ストリーム
1130 	 * @param {String} content 書き込む内容
1131 	 */
1132 	writeAll: function(stream, content) {
1133 		var i = 0;
1134 		for(;i+102400<content.length;i+=102400) {
1135 			stream.Write(content.substring(i, i + 102400));
1136 		}
1137 		stream.Write(content.substring(i));
1138 	},
1139 	
1140 	/**
1141 	 * VBのスコープの開始部分にマッチする正規表現オブジェクトを取得します。<br />
1142 	 * Function、Sub、Class、Module、Property、If、Get、Set、With、Select、Try、Structure、Enum、Forにマッチします。<br />
1143 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
1144 	 * <p>
1145 	 * 正規表現グループは、0がマッチした行全体、2がマッチした単語です。
1146 	 * </p>
1147 	 * 
1148 	 * @function
1149 	 * 
1150 	 * @example
1151 	 * var startMatcher = JseeUtil.getVBScopeStartMatcher();
1152 	 * var m1 = "Partial Private Shared Function func() As Object".match(startMatcher);
1153 	 * alert(m1);
1154 	 * var m2 = "Public ReadOnly Property name() As String".match(startMatcher);
1155 	 * alert(m2);
1156 	 * 
1157 	 * @returns {RegExp} VBのスコープの開始部分にマッチする正規表現オブジェクト
1158 	 */
1159 	getVBScopeStartMatcher: (function() {
1160 		var cache;
1161 		return function() {
1162 			if(!cache) {
1163 				cache = /^((?!End\s)(?!.+\sEnd\s)(?!Exit\s)(?!.+\sExit\s)[^']*\s+)?(Function|Sub|Class|Module|Property|Structure|Enum|Get|Set|With|Select|Try|If|For)(|\s.*|'.*)$/im;
1164 			}
1165 			return cache;
1166 		};
1167 	})(),
1168 	
1169 	/**
1170 	 * VBのスコープの終了部分にマッチする正規表現オブジェクトを取得します。<br />
1171 	 * End Function、End Sub、End Class、End Module、End Property、End If、End Get、End Set、End With、End Select、End Try、End Structure、End Enum、Nextにマッチします。<br />
1172 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
1173 	 * <p>
1174 	 * 正規表現グループは、0がマッチした行全体、2がマッチした単語、3がEndを除く単語(Nextにマッチした場合は存在しません)です。
1175 	 * </p>
1176 	 * 
1177 	 * @function
1178 	 * 
1179 	 * @example
1180 	 * var endMatcher = JseeUtil.getVBScopeEndMatcher();
1181 	 * var matched = "End Class".match(endMatcher);
1182 	 * alert(matched);
1183 	 * 
1184 	 * @returns {RegExp} VBのスコープの終了部分にマッチする正規表現オブジェクト
1185 	 */
1186 	getVBScopeEndMatcher: (function() {
1187 		var cache;
1188 		return function() {
1189 			if(!cache) {
1190 				cache = /^([^']*\s+)?(End\s+(Function|Sub|Class|Module|Property|Structure|Enum|Get|Set|With|Select|Try|If)|Next)(|\s.*|'.*)$/im;
1191 			}
1192 			return cache;
1193 		};
1194 	})(),
1195 	
1196 	/**
1197 	 * VBのEnd Hogeで閉じられるスコープの開始部分にマッチする正規表現オブジェクトを取得します。<br />
1198 	 * Function、Sub、Class、Module、Property、If、Get、Set、With、Select、Tryにマッチします。<br />
1199 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
1200 	 * <p>
1201 	 * 正規表現グループは、0がマッチした行全体、2がマッチした単語です。
1202 	 * </p>
1203 	 * 
1204 	 * @function
1205 	 * 
1206 	 * @example
1207 	 * var startMatcher = JseeUtil.getVBEndClosedScopeStartMatcher();
1208 	 * var matched = "Public ReadOnly Property name() As String".match(startMatcher);
1209 	 * alert(matched);
1210 	 * 
1211 	 * @returns {RegExp} VBのルーチンの開始部分にマッチする正規表現オブジェクト
1212 	 */
1213 	getVBEndClosedScopeStartMatcher: (function() {
1214 		var cache;
1215 		return function() {
1216 			if(!cache) {
1217 				cache = /^((?!End\s)(?!.+\sEnd\s)(?!Exit\s)(?!.+\sExit\s)[^']*\s+)?(Function|Sub|Class|Module|Property|Structure|Enum|Get|Set|With|Select|Try|If)(\s.*|'.*)?$/im;
1218 			}
1219 			return cache;
1220 		};
1221 	})(),
1222 	
1223 	/**
1224 	 * VBのEnd Hogeで閉じられるスコープの終了部分にマッチする正規表現オブジェクトを取得します。<br />
1225 	 * End Function、End Sub、End Class、End Module、End Property、End If、End Get、End Set、End With、End Select、End Tryにマッチします。<br />
1226 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
1227 	 * <p>
1228 	 * 正規表現グループは、0がマッチした行全体、2がマッチした単語、3がEndを除く単語です。
1229 	 * </p>
1230 	 * 
1231 	 * @function
1232 	 * 
1233 	 * @example
1234 	 * var endMatcher = JseeUtil.getVBEndClosedScopeEndMatcher();
1235 	 * var matched = "End Class".match(endMatcher);
1236 	 * alert(matched);
1237 	 * 
1238 	 * @returns {RegExp} VBのルーチンの終了部分にマッチする正規表現オブジェクト
1239 	 */
1240 	getVBEndClosedScopeEndMatcher: (function() {
1241 		var cache;
1242 		return function() {
1243 			if(!cache) {
1244 				cache = /^([^']*\s+)?(End\s+(Function|Sub|Class|Module|Property|Structure|Enum|Get|Set|With|Select|Try|If))(|\s.*|'.*)$/im;
1245 			}
1246 			return cache;
1247 		};
1248 	})(),
1249 	
1250 	/**
1251 	 * VBのルーチンの開始部分にマッチする正規表現オブジェクトを取得します。<br />
1252 	 * FunctionとSubにマッチします。<br />
1253 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
1254 	 * <p>
1255 	 * 正規表現グループは、0がマッチした行全体、2がマッチした単語です。
1256 	 * </p>
1257 	 * 
1258 	 * @function
1259 	 * 
1260 	 * @example
1261 	 * var startMatcher = JseeUtil.getVBRoutineStartMatcher();
1262 	 * var matched = "Partial Private Shared Function func(ByVal arg As Object) As Object".match(startMatcher);
1263 	 * alert(matched);
1264 	 * 
1265 	 * @returns {RegExp} VBのルーチンの開始部分にマッチする正規表現オブジェクト
1266 	 */
1267 	getVBRoutineStartMatcher: (function() {
1268 		var cache;
1269 		return function() {
1270 			if(!cache) {
1271 				cache = /^((?!End\s)(?!.+\sEnd\s)(?!Exit\s)(?!.+\sExit\s)[^']*\s+)?(Function|Sub)(|\s.*|'.*)$/im;
1272 			}
1273 			return cache;
1274 		};
1275 	})(),
1276 	
1277 	/**
1278 	 * VBのルーチンの終了部分にマッチする正規表現オブジェクトを取得します。<br />
1279 	 * End FunctionとEnd Subにマッチします。<br />
1280 	 * 二度目以降の呼び出しからは、キャッシュした値を返します。
1281 	 * <p>
1282 	 * 正規表現グループは、0がマッチした行全体、2がマッチした単語、3がEndを除く単語です。
1283 	 * </p>
1284 	 * 
1285 	 * @function
1286 	 * 
1287 	 * @example
1288 	 * var endMatcher = JseeUtil.getVBRoutineEndMatcher();
1289 	 * var matched = "End Sub".match(endMatcher);
1290 	 * alert(matched);
1291 	 * 
1292 	 * @returns {RegExp} VBのルーチンの終了部分にマッチする正規表現オブジェクト
1293 	 */
1294 	getVBRoutineEndMatcher: (function() {
1295 		var cache;
1296 		return function() {
1297 			if(!cache) {
1298 				cache = /^([^']*\s+)?(End\s+(Function|Sub))(|\s.*|'.*)$/im;
1299 			}
1300 			return cache;
1301 		};
1302 	})(),
1303 	
1304 	/**
1305 	 * アッパーキャメルケース("AaaBbb")に変換します。
1306 	 * 
1307 	 * @function
1308 	 * 
1309 	 * @example
1310 	 * var camelized = JseeUtil.upperCamelize("AAA_BBB");
1311 	 * alert(camelized);
1312 	 * 
1313 	 * @param {String} target 対象文字列
1314 	 * @returns {String} アッパーキャメルケースに変換された文字列
1315 	 */
1316 	upperCamelize: function(target) {
1317 		if(!target.match("_")) {
1318 			return target.toLowerCase().replace(/^./, target.charAt(0).toUpperCase());
1319 		}
1320 		
1321 		var result = "";
1322 		var foundUnderScore = false;
1323 		
1324 		target = target.toLowerCase();
1325 		for(var i = 0;i<target.length;i++) {
1326 			var c = target.charAt(i);
1327 			if(c == "_") {
1328 				foundUnderScore = true;
1329 			} else {
1330 				result += foundUnderScore ? c.toUpperCase() : c;
1331 				foundUnderScore = false;
1332 			}
1333 		}
1334 		
1335 		return result.replace(/^./, target.charAt(0).toUpperCase());
1336 	},
1337 	
1338 	/**
1339 	 * ロウワーキャメルケース("aaaBbb")に変換します。
1340 	 * 
1341 	 * @function
1342 	 * 
1343 	 * @example
1344 	 * var camelized = JseeUtil.lowerCamelize("AAA_BBB");
1345 	  * alert(camelized);
1346 	 * 
1347 	 * @param {String} target 対象文字列
1348 	 * @returns {String} ロウワーキャメルケースに変換された文字列
1349 	 */
1350 	lowerCamelize: function(target) {
1351 		if(!target.match("_")) {
1352 			return target.toLowerCase();
1353 		}
1354 		
1355 		var result = "";
1356 		var foundUnderScore = false;
1357 		
1358 		target = target.toLowerCase();
1359 		for(var i = 0;i<target.length;i++) {
1360 			var c = target.charAt(i);
1361 			if(c == "_") {
1362 				foundUnderScore = true;
1363 			} else {
1364 				result += foundUnderScore ? c.toUpperCase() : c;
1365 				foundUnderScore = false;
1366 			}
1367 		}
1368 		
1369 		return result;
1370 	},
1371 	
1372 	/**
1373 	 * アンダースコア区切り("AAA_BBB")に変換します。
1374 	 * 
1375 	 * @function
1376 	 * 
1377 	 * @example
1378 	 * var underScores = JseeUtil.underScorize("AaaBbb");
1379 	  * alert(underscores);
1380 	 * 
1381 	 * @param {String} target 対象文字列
1382 	 * @returns {String} アンダースコア区切りに変換された文字列
1383 	 */
1384 	underScorize: function(target) {
1385 		return target.replace(/(?!^.*)([A-Z])/g, "_$1").toUpperCase();
1386 	},
1387 	
1388 	/**
1389 	 * 必ず新規タブを開き、それをアクティブにします。<br />
1390 	 * 内部の動きは以下の通りです。
1391 	 * <ul>
1392 	 * <li>現在のタブが無題でなければそのまま新規タブを開きます。</li>
1393 	 * <li>現在のタブが無題の場合は別の無題でないタブをアクティブにしてから新規タブを開きます。</li>
1394 	 * <li>無題でないタブが存在しなければ、[元に戻す]が有効なタブを探し、あればそれを利用して新規タブを開きます。</li>
1395 	 * <li>[元に戻す]が有効な無題のタブが存在しなければ、既に存在する無題のタブを編集して新規タブを2つ開いた後、編集したタブを閉じます。</li>
1396 	 * </ul>
1397 	 * 
1398 	 * @function
1399 	 * 
1400 	 * @example
1401 	 * JseeUtil.newTab();
1402 	 * JseeUtil.newTab();
1403 	 * JseeUtil.newTab();
1404 	 */
1405 	newTab: function() {
1406 		var logger = JseeUtil.getLogger(false, "sgc4jsee.jsee JseeUtil.newTab");
1407 		
1408 		var docCnt = editor.Documents.Count;
1409 		
1410 		var redraw = Redraw;
1411 		Redraw = false;
1412 		
1413 		var initDoc = document;
1414 		
1415 		var openBlankTab = function() {
1416 			editor.ExecuteCommandByID(4096);
1417 			if(!document.Saved) {
1418 				logger.log("テンプレートファイルが設定されているので元に戻す");
1419 				document.Undo();
1420 			}
1421 			// アクティブ順を元に近い状態にしておく
1422 			var newDoc = document;
1423 			initDoc.Activate();
1424 			newDoc.Activate();
1425 		};
1426 		
1427 		(function() {
1428 			if(initDoc.FullName || !initDoc.Saved) {
1429 				logger.log("ファイルが開かれているか、ドキュメントが変更されている場合、普通に開く");
1430 				openBlankTab();
1431 				return;
1432 			}
1433 			
1434 			logger.log("まっさらな無題ドキュメントがアクティブな場合");
1435 			
1436 			logger.log("開く基点となるタブを探す");
1437 			var docs = JseeUtil.col2arr(editor.Documents);
1438 			for(var i=0;i<docs.length-1;i++) {
1439 				var doc = docs[i];
1440 				if(doc.FullName || !doc.Saved) {
1441 					logger.log("見つけた" + i + 1 + "つ目のタブを基点に開く");
1442 					doc.Activate();
1443 					openBlankTab();
1444 					// アクティブ順を元に近い状態にしておく
1445 					var newDoc = document;
1446 					initDoc.Activate();
1447 					newDoc.Activate();
1448 					return;
1449 				}
1450 			}
1451 			
1452 			logger.log("無題ドキュメントしかないので、各タブのUndo/Redoを試す");
1453 			for(var i=0;i<docs.length;i++) {
1454 				var doc = docs[i];
1455 				doc.Activate();
1456 				doc.Undo();
1457 				if(!doc.Saved) {
1458 					logger.log(i + 1 + "つ目のタブでUndoが成功したので、これを基点に開く");
1459 					openBlankTab();
1460 					var newDoc = document;
1461 					doc.Activate();
1462 					doc.Redo();
1463 					newDoc.Activate();
1464 					return;
1465 				}
1466 				doc.Redo();
1467 				if(!doc.Saved) {
1468 					logger.log(i + 1 + "つ目のタブでRedoが成功したので、これを基点に開く");
1469 					openBlankTab();
1470 					var newDoc = document;
1471 					doc.Activate();
1472 					doc.Undo();
1473 					doc.Activate();
1474 					return;
1475 				}
1476 			}
1477 			
1478 			logger.log("全てのタブが完全に新規の無題ドキュメントと判断し、編集してから2つタブを開き、編集した自身は閉じる");
1479 			initDoc.selection.Text = " ";
1480 			var saved = document.Saved;
1481 			openBlankTab();
1482 			initDoc.Activate();
1483 			openBlankTab();
1484 			var newDoc = document;
1485 			initDoc.Activate();
1486 			initDoc.Undo();
1487 			initDoc.Close();
1488 			newDoc.Activate();
1489 		})();
1490 		
1491 		// 防御機構
1492 		if(docCnt == editor.Documents.Count) {
1493 			throw new Error("起こりえないはずの例外です。");
1494 		}
1495 		
1496 		Redraw = redraw;
1497 	},
1498 	
1499 	/**
1500 	 * [設定のプロパティ]に紐付く[テンプレートファイル]を開きます。
1501 	 * 
1502 	 * @function
1503 	 * 
1504 	 * @example
1505 	 * JseeUtil.openTemplate(document.configName);
1506 	 * 
1507 	 * @param {String} configName documentオブジェクトのconfigName
1508 	 */
1509 	openTemplate: function(configName) {
1510 		document.ConfigName = "Java";
1511 		alert(document.Config.FileNew.TemplateFile);
1512 	},
1513 	
1514 	/**
1515 	 * 箱型選択で全選択します。
1516 	 * 
1517 	 * @function
1518 	 * 
1519 	 * @example
1520 	 * JseeUtil.selectAllBoxText();
1521 	 */
1522 	selectAllBoxText: function() {
1523 		var redraw = Redraw;
1524 		Redraw = false;
1525 		
1526 		var allText = JseeUtil.getAllText();
1527 		if(!allText) {
1528 			return;
1529 		}
1530 		
1531 		// 文字のある行のみを改行抜きで配列化
1532 		var lines = allText.split(/(\r\n|\r|\n)/);
1533 		var maxLength = 0;
1534 		for(var i=0;i<lines.length;i++) {
1535 			if(maxLength < lines[i].length) {
1536 				maxLength = lines[i].length;
1537 			}
1538 		}
1539 		// 改行の長さを付加
1540 		maxLength += 1;
1541 		
1542 		document.selection.StartOfDocument(false);
1543 		editor.ExecuteCommandByID(4155);
1544 		document.selection.EndOfDocument(false);
1545 		Redraw = redraw;
1546 		document.selection.CharRight(false,maxLength * 2);
1547 	},
1548 	
1549 	/**
1550 	 * キャレット位置から後方へ検索し、見つかった単語を選択します。<br />
1551 	 * 見つかった場合は<code>true</code>、見つからなかった場合は<code>false</code>、空文字が指定された場合も<code>false</code>を返します。
1552 	 * <p>
1553 	 * 既存の検索機能を使わないため、検索履歴やハイライトに影響を与えません。<br />
1554 	 * 検索対象に<code>String</code>型が指定された場合は文字列検索を、<code>RegExp</code>型が指定された場合は正規表現検索(multilineがONの状態で)を行います。<br />
1555 	 * 正規表現検索はJScriptによる検索であり、既存の検索機能とは挙動が違うので注意してください。(特に"\s"は改行に一致するため、"[^\S\r\n]"などに書き換える必要があります。)
1556 	 * </p>
1557 	 * 
1558 	 * @function
1559 	 * 
1560 	 * @example
1561 	 * if(!JseeUtil.findNext("hoge")) {
1562 	 *     alert("見つかりませんでした。");
1563 	 * }
1564 	 * if(!JseeUtil.findNext(/\d+/)) {
1565 	 *     alert("見つかりませんでした。");
1566 	 * }
1567 	 * 
1568 	 * @param {String|RegExp} target 検索する文字列または正規表現オブジェクト
1569 	 * @returns {Boolean} 見つかった場合は<code>true</code>、見つからなかった場合は<code>false</code>
1570 	 */
1571 	findNext: function(target) {
1572 		var logger = JseeUtil.getLogger(false, "sgc4jsee.jsee JseeUtil.findNext");
1573 		
1574 		if(!target) {
1575 			return false;
1576 		}
1577 		
1578 		// キャレット末尾のX座標、Y座標
1579 		var bottomX = document.selection.GetBottomPointX(eePosLogical);
1580 		var bottomY = document.selection.GetBottomPointY(eePosLogical);
1581 		// キャレットより後ろの文字列
1582 		var bottomRightTextOnlyLf = JseeUtil.getBottomRightText().replace(/\r\n|\r/g, "\n");
1583 		
1584 		// 検索対象文字列先頭の位置
1585 		var targetHeadPos;
1586 		// 検索対象文字列
1587 		var targetText;
1588 		
1589 		// 文字列検索
1590 		if(typeof target == "string") {
1591 			// キャレットより後ろの文字列を検索して、検索対象文字列先頭の位置を取得
1592 			targetHeadPos = bottomRightTextOnlyLf.indexOf(target);
1593 			// 検索にかからなければ処理を終了
1594 			if(targetHeadPos < 0) {
1595 				return false;
1596 			}
1597 			// 検索対象文字列を設定
1598 			targetText = target;
1599 		// 正規表現検索
1600 		} else if(target instanceof RegExp) {
1601 			target = JseeUtil.getRegExpAddedFlgs(target, "m");
1602 			
1603 			// キャレットより前の文字列を検索して、検索にかかった情報を取得
1604 			if(target.test(bottomRightTextOnlyLf)) {
1605 				// 検索対象文字列先頭の位置を取得
1606 				var targetHeadPos = RegExp.index;
1607 				// 検索対象文字列を設定
1608 				targetText = RegExp.lastMatch;
1609 			// 検索にかからなければ処理を終了
1610 			} else {
1611 				return false;
1612 			}
1613 		// 文字列検索でも正規表現検索でもなければ例外
1614 		} else {
1615 			throw new Error("引数がString型でもRegExp型でもありません。");
1616 		}
1617 		logger.log("targetHeadPos:" + targetHeadPos);
1618 		logger.log("targetText:" + targetText);
1619 		
1620 		// 改行へのマッチャー
1621 		var brMatcher = /\n/g;
1622 		
1623 		// キャレットより後ろの文字列から、キャレットから検索文字列までの文字列を抽出
1624 		var betweenCaretAndTarget = bottomRightTextOnlyLf.substring(0, targetHeadPos);
1625 		// キャレットから検索文字列までの文字列にある改行をカウント
1626 		var betweenCaretAndTargetBrCnt = 0;
1627 		while(brMatcher.test(betweenCaretAndTarget)) betweenCaretAndTargetBrCnt++;
1628 		logger.log("betweenCaretAndTargetBrCnt:" + betweenCaretAndTargetBrCnt);
1629 		// キャレットから検索文字列までの文字列にある最後の改行位置から、検索文字列位置までの間隔を、検索文字列先頭のX座標とみなす
1630 		// またはキャレットのX座標+検索文字列の位置までを、検索文字列先頭のX座標とみなす
1631 		var targetHeadX = betweenCaretAndTargetBrCnt ? targetHeadPos - RegExp.lastIndex + 1 : bottomX + targetHeadPos;
1632 		logger.log("RegExp.lastIndex:" + RegExp.lastIndex);
1633 		logger.log("targetHeadX:" + targetHeadX);
1634 		// キャレット位置から、キャレットから検索文字列までの文字列にある改行の数だけ下がった位置を、検索文字列先頭のY座標とみなす
1635 		var targetHeadY = bottomY + betweenCaretAndTargetBrCnt;
1636 		logger.log("targetHeadY:" + targetHeadY);
1637 		// 検索文字列の先頭に移動
1638 		document.selection.SetActivePoint(eePosLogical, targetHeadX, targetHeadY);
1639 		
1640 		// 検索文字列にある改行をカウント
1641 		var targetBrCnt = 0;
1642 		while(brMatcher.test(targetText)) targetBrCnt++;
1643 		logger.log("targetBrCnt:" + targetBrCnt);
1644 		// 検索文字列の長さ-検索文字列にある最後の改行位置までの間隔=最後の改行からの文字数を、検索文字列末尾のX座標とみなす
1645 		// 検索文字列のX座標+検索文字列の長さを、検索文字列末尾のX座標とみなす
1646 		var targetFootX = targetBrCnt ? targetText.length - RegExp.lastIndex + 1 : targetHeadX + targetText.length;
1647 		logger.log("targetFootX:" + targetFootX);
1648 		// 検索文字列先頭のY座標から、検索文字列にある改行の数だけ下がった位置を、検索文字列末尾のY座標とみなす
1649 		var targetFootY = targetHeadY + targetBrCnt;
1650 		logger.log("targetFootY:" + targetFootY);
1651 		// 検索文字列を選択
1652 		document.selection.SetActivePoint(eePosLogical, targetFootX, targetFootY, true);
1653 		
1654 		return true;
1655 	},
1656 	
1657 	/**
1658 	 * キャレット位置から前方へ検索し、見つかった単語を選択します。
1659 	 * 見つかった場合は<code>true</code>、見つからなかった場合は<code>false</code>、空文字が指定された場合も<code>false</code>を返します。
1660 	 * <p>
1661 	 * 既存の検索機能を使わないため、検索履歴やハイライトに影響を与えません。<br />
1662 	 * 検索対象に<code>String</code>型が指定された場合は文字列検索を、<code>RegExp</code>型が指定された場合は正規表現検索(multilineがONの状態で)を行います。<br />
1663 	 * 正規表現検索はJScriptによる検索であり、既存の検索機能とは挙動が違うので注意してください。(特に"\s"は改行に一致するため、"[^\S\r\n]"などに書き換える必要があります。)
1664 	 * </p>
1665 	 * 
1666 	 * @function
1667 	 * 
1668 	 * @example
1669 	 * if(!JseeUtil.findPrev("hoge")) {
1670 	 *     alert("見つかりませんでした。");
1671 	 * }
1672 	 * if(!JseeUtil.findPrev(/\d+/)) {
1673 	 *     alert("見つかりませんでした。");
1674 	 * }
1675 	 * 
1676 	 * @param {String|RegExp} target 検索する文字列または正規表現オブジェクト
1677 	 * @returns {Boolean} 見つかった場合は<code>true</code>、見つからなかった場合は<code>false</code>
1678 	 */
1679 	findPrev: function(target) {
1680 		var logger = JseeUtil.getLogger(false, "sgc4jsee.jsee JseeUtil.findPrev");
1681 		
1682 		if(!target) {
1683 			return false;
1684 		}
1685 		
1686 		// キャレット先頭のX座標、Y座標
1687 		var topX = document.selection.GetTopPointX(eePosLogical);
1688 		var topY = document.selection.GetTopPointY(eePosLogical);
1689 		// キャレットより前の文字列
1690 		var topLeftTextOnlyLf = JseeUtil.getTopLeftText().replace(/\r\n|\r/g, "\n");;
1691 		
1692 		// 検索対象文字列先頭の位置
1693 		var targetHeadPos;
1694 		// 検索対象文字列
1695 		var targetText;
1696 		
1697 		// 文字列検索
1698 		if(typeof target == "string") {
1699 			// キャレットより前の文字列を検索して、検索対象文字列先頭の位置を取得
1700 			targetHeadPos = topLeftTextOnlyLf.lastIndexOf(target);
1701 			// 検索にかからなければ処理を終了
1702 			if(targetHeadPos < 0) {
1703 				return false;
1704 			}
1705 			// 検索対象文字列を設定
1706 			targetText = target;
1707 		// 正規表現検索
1708 		} else if(target instanceof RegExp) {
1709 			target = JseeUtil.getRegExpAddedFlgs(target, "gm");
1710 			var found = false;
1711 			// キャレットより前の文字列を検索して、最後に検索にかかった情報を取得
1712 			while(target.test(topLeftTextOnlyLf)) {
1713 				if(!RegExp.lastMatch) {
1714 					continue;
1715 				}
1716 				found = true;
1717 				// 検索対象文字列先頭の位置を取得
1718 				var targetHeadPos = RegExp.index;
1719 				// 検索対象文字列を設定
1720 				targetText = RegExp.lastMatch;
1721 			}
1722 			// 検索にかからなければ処理を終了
1723 			if(!found) {
1724 				return false;
1725 			}
1726 		// 文字列検索でも正規表現検索でもなければ例外
1727 		} else {
1728 			throw new Error("引数がString型でもRegExp型でもありません。");
1729 		}
1730 		logger.log("targetHeadPos:" + targetHeadPos);
1731 		logger.log("targetText:" + targetText);
1732 		
1733 		// 改行へのマッチャー
1734 		var brMatcher = /\n/g;
1735 		
1736 		// キャレットより前の文字列から、ドキュメント先頭から検索文字列までの文字列を抽出
1737 		var betweenCaretAndTarget = topLeftTextOnlyLf.substring(0, targetHeadPos);
1738 		// ドキュメント先頭から検索文字列までの文字列にある改行をカウント
1739 		var betweenDocumentStartAndTargetBrCnt = 0;
1740 		while(brMatcher.test(betweenCaretAndTarget)) betweenDocumentStartAndTargetBrCnt++;
1741 		logger.log("betweenDocumentStartAndTargetBrCnt:" + betweenDocumentStartAndTargetBrCnt);
1742 		// ドキュメント先頭から検索文字列までの文字列にある最後の改行から、検索文字列位置までの間隔を、検索文字列先頭のX座標とみなす
1743 		// または先頭X座標+検索文字列の位置までを、検索文字列先頭のX座標とみなす
1744 		var targetHeadX = betweenDocumentStartAndTargetBrCnt ? targetHeadPos - RegExp.lastIndex + 1 : 1 + targetHeadPos;
1745 		logger.log("RegExp.lastIndex:" + RegExp.lastIndex);
1746 		logger.log("targetHeadX:" + targetHeadX);
1747 		// 先頭Y座標から検索文字列までの文字列にある改行の数だけ下がった位置を、検索文字列先頭のY座標とみなす
1748 		var targetHeadY = 1 + betweenDocumentStartAndTargetBrCnt;
1749 		logger.log("targetHeadY:" + targetHeadY);
1750 		// 検索文字列の先頭に移動
1751 		document.selection.SetActivePoint(eePosLogical, targetHeadX, targetHeadY);
1752 		
1753 		// 検索文字列にある改行をカウント
1754 		var targetBrCnt = 0;
1755 		while(brMatcher.test(targetText)) targetBrCnt++;
1756 		logger.log("targetBrCnt:" + targetBrCnt);
1757 		// 検索文字列の長さ-検索文字列にある最後の改行位置までの間隔=最後の改行からの文字数を、検索文字列末尾のX座標とみなす
1758 		// 検索文字列のX座標+検索文字列の長さを、検索文字列末尾のX座標とみなす
1759 		var targetFootX = targetBrCnt ? targetText.length - RegExp.lastIndex + 1 : targetHeadX + targetText.length;
1760 		logger.log("targetFootX:" + targetFootX);
1761 		// 検索文字列先頭のY座標から、検索文字列にある改行の数だけ下がった位置を、検索文字列末尾のY座標とみなす
1762 		var targetFootY = targetHeadY + targetBrCnt;
1763 		logger.log("targetFootY:" + targetFootY);
1764 		// 検索文字列を選択
1765 		document.selection.SetActivePoint(eePosLogical, targetFootX, targetFootY, true);
1766 		
1767 		return true;
1768 	},
1769 	
1770 	/**
1771 	 * 文書の先頭から後方へ検索し、見つかった単語を選択します。<br />
1772 	 * <code>document.selection.StartOfDocument(false);</code>と{@link JseeUtil.findNext}を呼び出します。
1773 	 * 
1774 	 * @function
1775 	 * 
1776 	 * @example
1777 	 * if(!JseeUtil.findAll("hoge")) {
1778 	 *     alert("見つかりませんでした。");
1779 	 * }
1780 	 * if(!JseeUtil.findAll(/\d+/)) {
1781 	 *     alert("見つかりませんでした。");
1782 	 * }
1783 	 * 
1784 	 * @param {String|RegExp} target 検索する文字列または正規表現オブジェクト
1785 	 * @returns {Boolean} 見つかった場合は<code>true</code>、見つからなかった場合は<code>false</code>
1786 	 */
1787 	findAll: function(target) {
1788 		document.selection.StartOfDocument(false);
1789 		return JseeUtil.findNext(target);
1790 	},
1791 	
1792 	/**
1793 	 * 正規表現オブジェクトのフラグ("g", "m", "i"の3種類)を追加した、新しい正規表現オブジェクトを返します。<br />
1794 	 * 新しい正規表現オブジェクトは、元の正規表現オブジェクトの検索結果等は保持していません。
1795 	 * 
1796 	 * @function
1797 	 * 
1798 	 * @example
1799 	 * alert(JseeUtil.getRegExpAddedFlgs(/hoge/mi, "g"));
1800 	 * 
1801 	 * @param {RegExp} regexp 変換対象正規表現オブジェクト
1802 	 * @returns {RegExp} <code>global</code>が<code>true</code>である正規表現オブジェクト
1803 	 */
1804 	getRegExpAddedFlgs: function(regexp, flgs) {
1805 		var g = regexp.global     || flgs.indexOf("g") >= 0;
1806 		var m = regexp.multiline  || flgs.indexOf("m") >= 0;
1807 		var i = regexp.ignoreCase || flgs.indexOf("i") >= 0;
1808 		return new RegExp(regexp.source, (g ? "g" : "") + (m ? "m" : "") + (i ? "i" : ""));
1809 	},
1810 	
1811 	/**
1812 	 * ドキュメントの全文字列を取得します。<br />
1813 	 * <code>document.GetLine</code>で全ての行を取得するより高速に動作します。<br />
1814 	 * <strong>部分編集時には、その範囲内しか取れません。注意してください。</strong>
1815 	 * 
1816 	 * @function
1817 	 * 
1818 	 * @example
1819 	 * var allText = JseeUtil.getAllText();
1820 	 * 
1821 	 * @returns {String} ドキュメントの全文字列
1822 	 */
1823 	getAllText: function() {
1824 		var redraw = Redraw;
1825 		Redraw = false;
1826 		
1827 		var topX = document.selection.GetTopPointX(eePosLogical);
1828 		var topY = document.selection.GetTopPointY(eePosLogical);
1829 		var bottomX = document.selection.GetBottomPointX(eePosLogical);
1830 		var bottomY = document.selection.GetBottomPointY(eePosLogical);
1831 		
1832 		document.selection.SelectAll();
1833 		var text = document.selection.Text;
1834 		
1835 		document.selection.SetActivePoint(eePosLogical, topX, topY);
1836 		document.selection.SetActivePoint(eePosLogical, bottomX, bottomY, true);
1837 		
1838 		Redraw = redraw;
1839 		return text;
1840 	},
1841 	
1842 	/**
1843 	 * キャレット位置より左上側の文字列を取得します。<br />
1844 	 * 選択状態にある場合、左上寄りの側を基準にして取得します。("aaabbbccc"の内"bbb"を選択していれば、"aaa"が取得される)<br />
1845 	 * <strong>部分編集時には、その範囲内しか取れません。注意してください。</strong>
1846 	 * 
1847 	 * @function
1848 	 * 
1849 	 * @example
1850 	 * var topLeftText = JseeUtil.getTopLeftText();
1851 	 * alert(topLeftText);
1852 	 * 
1853 	 * @returns {String} ドキュメントの全文字列
1854 	 */
1855 	getTopLeftText: function() {
1856 		var redraw = Redraw;
1857 		Redraw = false;
1858 		
1859 		var topX = document.selection.GetTopPointX(eePosLogical);
1860 		var topY = document.selection.GetTopPointY(eePosLogical);
1861 		var bottomX = document.selection.GetBottomPointX(eePosLogical);
1862 		var bottomY = document.selection.GetBottomPointY(eePosLogical);
1863 		
1864 		if(document.selection.Text) {
1865 			document.selection.SetActivePoint(eePosLogical, topX, topY);
1866 		}
1867 		document.selection.StartOfDocument(true);
1868 		var text = document.selection.Text;
1869 		
1870 		document.selection.SetActivePoint(eePosLogical, topX, topY);
1871 		document.selection.SetActivePoint(eePosLogical, bottomX, bottomY, true);
1872 		
1873 		Redraw = redraw;
1874 		return text;
1875 	},
1876 	
1877 	/**
1878 	 * キャレット位置より右下側の文字列を取得します。<br />
1879 	 * 選択状態にある場合、右下寄りの側を基準にして取得します。("aaabbbccc"の内"bbb"を選択していれば、"ccc"が取得される)<br />
1880 	 * <strong>部分編集時には、その範囲内しか取れません。注意してください。</strong>
1881 	 * 
1882 	 * @function
1883 	 * 
1884 	 * @example
1885 	 * var bottomRightText = JseeUtil.getBottomRightText();
1886 	 * alert(bottomRightText);
1887 	 * 
1888 	 * @returns {String} ドキュメントの全文字列
1889 	 */
1890 	getBottomRightText: function() {
1891 		var redraw = Redraw;
1892 		Redraw = false;
1893 		
1894 		var topX = document.selection.GetTopPointX(eePosLogical);
1895 		var topY = document.selection.GetTopPointY(eePosLogical);
1896 		var bottomX = document.selection.GetBottomPointX(eePosLogical);
1897 		var bottomY = document.selection.GetBottomPointY(eePosLogical);
1898 		
1899 		if(document.selection.Text) {
1900 			document.selection.CharRight(false,1);
1901 		}
1902 		document.selection.EndOfDocument(true);
1903 		var text = document.selection.Text;
1904 		
1905 		document.selection.SetActivePoint(eePosLogical, topX, topY);
1906 		document.selection.SetActivePoint(eePosLogical, bottomX, bottomY, true);
1907 		
1908 		Redraw = redraw;
1909 		return text;
1910 	},
1911 	
1912 	/**
1913 	 * 対象文字列を改行ごとに区切り、行の配列にします。<br />
1914 	 * 引数の指定によって、各行の末尾に改行コードを含ませることが出来ます。<br />
1915 	 * <code>str</code>が空文字の場合、空文字要素を1つ持つ配列として返します。
1916 	 * 
1917 	 * @function
1918 	 * 
1919 	 * @example
1920 	 * var lines = JseeUtil.text2lines("aaa\nbbb");
1921 	 * alert(lines);
1922 	 * 
1923 	 * @param {String} str 対象文字列
1924 	 * @param {Boolean} [withBreak=false] 各行の末尾に改行コードを含ませる場合は<code>true</code> (省略可能)
1925 	 * @returns {String[]} 行の配列
1926 	 */
1927 	str2lines: function(str, withBreak) {
1928 		if(JseeUtil.isUndef(withBreak)) {
1929 			withBreak = false;
1930 		}
1931 		
1932 		var lines = [];
1933 		var re = /([^\r\n]*)(\r\n|\r|\n)|([^\r\n]+)$/g;
1934 		while(re.test(str)) {
1935 			if(withBreak) {
1936 				lines.push(RegExp.lastMatch);
1937 			} else {
1938 				lines.push(RegExp.$3 || RegExp.$1);
1939 			}
1940 		}
1941 		return lines;
1942 	},
1943 	
1944 	/**
1945 	 * ドキュメントの全ての行を文字列配列として取得します。<br />
1946 	 * 引数に<code>true</code>を設定すれば、各行の末尾に改行コードも付随します。<br />
1947 	 * <strong>部分編集時には、その範囲内しか取れません。注意してください。</strong>
1948 	 * <p>
1949 	 * {@link JseeUtil.getAllText}と{@link JseeUtil.str2lines}を順に呼び出します。
1950 	 * </p>
1951 	 * 
1952 	 * @function
1953 	 * 
1954 	 * @example
1955 	 * var lines = JseeUtil.getAllLines(true);
1956 	 * OutputBar.Visible = true;
1957 	 * OutputBar.writeln(lines);
1958 	 * 
1959 	 * @param {Boolean} withBreak 各行の末尾に改行コードを付随させるかどうか
1960 	 * @returns {String[]} ドキュメントの全ての行を格納した文字列配列
1961 	 */
1962 	getAllLines: function(withBreak) {
1963 		return JseeUtil.str2lines(JseeUtil.getAllText(), withBreak);
1964 	},
1965 	
1966 	/**
1967 	 * キャレット位置より左上側の行を文字列配列として取得します。<br />
1968 	 * 選択状態にある場合、左上寄りの側を基準にして取得します。("abc\def"の内"e"を選択していれば、["abc", "d"]が取得される)<br />
1969 	 * 引数の指定によって、各行の末尾に改行コードを含ませることが出来ます。<br />
1970 	 * <strong>部分編集時には、その範囲内しか取れません。注意してください。</strong>
1971 	 * <p>
1972 	 * {@link JseeUtil.getTopLeftText}と{@link JseeUtil.str2lines}を順に呼び出します。
1973 	 * </p>
1974 	 * 
1975 	 * @function
1976 	 * 
1977 	 * @example
1978 	 * var topLeftLines = JseeUtil.getTopLeftLines(true);
1979 	 * OutputBar.Visible = true;
1980 	 * OutputBar.writeln(topLeftLines);
1981 	 * 
1982 	 * @param {Boolean} [withBreak=false] 各行の末尾に改行コードを含ませる場合は<code>true</code> (省略可能)
1983 	 * @returns {String[]} ドキュメントの左上側の行を格納した文字列配列
1984 	 */
1985 	getTopLeftLines: function(withBreak) {
1986 		return JseeUtil.str2lines(JseeUtil.getTopLeftText(), withBreak);
1987 	},
1988 	
1989 	/**
1990 	 * キャレット位置より右下側の行を文字列配列として取得します。<br />
1991 	 * 選択状態にある場合、右下寄りの側を基準にして取得します。("abc\def"の内"b"を選択していれば、["c", "def"]が取得される)<br />
1992 	 * 引数の指定によって、各行の末尾に改行コードを含ませることが出来ます。<br />
1993 	 * <strong>部分編集時には、その範囲内しか取れません。注意してください。</strong>
1994 	 * <p>
1995 	 * {@link JseeUtil.getBottomRightText}と{@link JseeUtil.str2lines}を順に呼び出します。
1996 	 * </p>
1997 	 * 
1998 	 * @function
1999 	 * 
2000 	 * @example
2001 	 * var bottomRightLines = JseeUtil.getBottomRightLines(true);
2002 	 * OutputBar.Visible = true;
2003 	 * OutputBar.writeln(bottomRightLines);
2004 	 * 
2005 	 * @param {Boolean} [withBreak=false] 各行の末尾に改行コードを含ませる場合は<code>true</code> (省略可能)
2006 	 * @returns {String[]} ドキュメントの全ての行を含む文字列配列
2007 	 */
2008 	getBottomRightLines: function(withBreak) {
2009 		return JseeUtil.str2lines(JseeUtil.getBottomRightText(), withBreak);
2010 	},
2011 	
2012 	/**
2013 	 * はい/いいえ/キャンセルを選択するダイアログを表示します。<br />
2014 	 * 返却値は右記の形のオブジェクトです。<code>{yes: true/false, no: true/false, cancel: true/false}</code><br />
2015 	 * 
2016 	 * @function
2017 	 * 
2018 	 * @example
2019 	 * var ans = JseeUtil.yesNoCancel("選択してください");
2020 	 * if(ans.yes) {
2021 	 *     alert("「はい」が選択されました。");
2022 	 * } else if(ans.no) {
2023 	 *     alert("「いいえ」が選択されました。");
2024 	 * } else {
2025 	 *     alert("「キャンセル」が選択されました。");
2026 	 * }
2027 	 * 
2028 	 * @param {String} message 表示するメッセージ
2029 	 * @param {String} title title ダイアログのタイトル
2030 	 * @returns {returnType} 右記の形のオブジェクト <code>{yes: true/false, no: true/false, cancel: true/false}</code><br />
2031 	 */
2032 	yesNoCancel: function(message) {
2033 		var result = {yes: false, no: false, cancel: false};
2034 		var ans = JseeUtil.shell().Popup(message, 0, "EmEditor", 3);
2035 		if(ans == 6) {
2036 			result.yes = true;
2037 		} else if(ans == 7) {
2038 			result.no = true;
2039 		} else {
2040 			result.cancel = true;
2041 		}
2042 		return result;
2043 	},
2044 	
2045 	/**
2046 	 * 指定された文字列内から、最小のインデント文字列を返します。<br />
2047 	 * 桁数が同じタブインデントと空白インデントが存在する場合、先に現れた方を返します。
2048 	 * 
2049 	 * @function
2050 	 * 
2051 	 * @example
2052 	 * var minimumIndent = JseeUtil.getMinimumIndent("\t\taaa\n\tbbb\n\t\t\tccc\n    ddd\n");
2053 	 * alert(JseeUtil.escEscSeq(minimumIndent).replace(/ /g, "*"));
2054 	 * var minimumIndent = JseeUtil.getMinimumIndent("    ddd\n\t\taaa\n\tbbb\n\t\t\tccc\n");
2055 	 * alert(JseeUtil.escEscSeq(minimumIndent).replace(/ /g, "*"));
2056 	 * 
2057 	 * @param {String} target 最小のインデント文字列を探す対象の文字列
2058 	 * @returns {String} 最小のインデント文字列
2059 	 */
2060 	getMinimumIndent: function(target) {
2061 		var tabSpaces = [];
2062 		for(var i=0;i<document.Config.Indent.TabColumns;i++) {tabSpaces.push(" ");}
2063 		var tabSpace = tabSpaces.join("");
2064 		
2065 		// 対象文字列の全インデントを(インデントされていない行は空白として)抽出
2066 		var m = target.replace(/\r\n|\r/g, "\n").replace(/\n$/, "").match(/^[^\r\n\S]*/gm);
2067 		if(!m) {
2068 			return "";
2069 		}
2070 		
2071 		var min = "";
2072 		var minLength = 0;
2073 		for(var i=0;i<m.length;i++) {
2074 			var next = m[i].replace(/\t/g, tabSpace);
2075 			if(i == 0) {
2076 				if(next == "") {
2077 					return "";
2078 				}
2079 				min = m[i];
2080 				minLength = next.length;
2081 			} else {
2082 				if(next.length < minLength) {
2083 					if(next == "") {
2084 						return "";
2085 					}
2086 					min = m[i];
2087 					minLength = next.length;
2088 				}
2089 			}
2090 		}
2091 		return min;
2092 	},
2093 	
2094 	/**
2095 	 * 指定された文字列内から、最小のインデント文字列を返します。<br />
2096 	 * {@link JseeUtil.getMinimumIndent}と違ってタブインデントと空白インデントを同一視せず、<br />
2097 	 * 設定のプロパティで設定されているインデント(タブまたは空白のどちらか)のみを認識します。
2098 	 * 
2099 	 * @function
2100 	 * 
2101 	 * @example
2102 	 * var minimumIndent = JseeUtil.getMinimumIndentStrict("\t\taaa\n\tbbb\n\t\t\tccc\n");
2103 	 * alert(JseeUtil.escEscSeq(minimumIndent));
2104 	 * 
2105 	 * @param {String} target 最小のインデント文字列を探す対象の文字列
2106 	 * @returns {String} 最小のインデント文字列
2107 	 */
2108 	getMinimumIndentStrict: function(target) {
2109 		var indents = target.replace(/(\r\n|\r)/g, "\n").replace(/\n$/, "").match(new RegExp("^(" + JseeUtil.getIndent() + ")*", "gm"));
2110 		if(!indents) {
2111 			return "";
2112 		}
2113 		var min;
2114 		for(var i=0;i<indents.length;i++) {
2115 			if(indents[i] == "") {
2116 				return "";
2117 			}
2118 			if(i == 0 || min.length > indents[i].length) {
2119 				min = indents[i];
2120 			}
2121 		}
2122 		return min;
2123 	},
2124 	
2125 	/**
2126 	 * キャレット位置の行末の改行、それがなければ前の行の行末の改行、それでもなければ"\r\n"を返します。<br />
2127 	 * EmEditorで改行を挿入する場合の改行コード判断基準を真似ていますが、1つも改行のないドキュメントへの改行挿入時の判断が微妙に違います。
2128 	 * 
2129 	 * @function
2130 	 * 
2131 	 * @example
2132 	 * alert(JseeUtil.escEscSeq(JseeUtil.getBr()));
2133 	 * 
2134 	 * @returns {String} キャレット位置の行末の改行、それがなければ前の行の行末の改行、それでもなければ"\r\n"
2135 	 */
2136 	getBr: function() {
2137 		var execed = /(\r\n|\r|\n)$/.exec(document.GetLine(document.selection.GetActivePointY(eePosLogical), eeGetLineWithNewLines));
2138 		if(execed) {
2139 			return execed[0];
2140 		} else if(document.selection.GetActivePointY(eePosLogical) != 1) {
2141 			return /(\r\n|\r|\n)$/.exec(document.GetLine(document.selection.GetActivePointY(eePosLogical) - 1, eeGetLineWithNewLines))[0];
2142 		} else {
2143 			return "\r\n";
2144 		}
2145 	},
2146 	
2147 	/**
2148 	 * INIファイルに対して、指定されたセレクションとキーに該当する値を選択状態にします。<br />
2149 	 * 選択が成功すれば<code>true</code>、失敗すれば<code>false</code>を返します。<br />
2150 	 * <p>
2151 	 * ";"か"#"で始まる文字列をコメントとして解釈し、コメントと行末の空白は選択範囲から外します。
2152 	 * </p>
2153 	 * 
2154 	 * @function
2155 	 * 
2156 	 * @example
2157 	 * // 以下のような内容のINIファイルを開いている状態で、"hoge"を選択状態にしたい場合
2158 	 * // [selection1]
2159 	 * // key1=hoge ;ほげです
2160 	 * 
2161 	 * JseeUtil.selectIniValue("selection1", "key1");
2162 	 * 
2163 	 * @param {String} selection 選択状態にしたい値が所属するセレクション
2164 	 * @param {String} key 選択状態にしたい値を設定しているキー
2165 	 * @returns {Boolean} 選択が成功すれば<code>true</code>、失敗すれば<code>false</code>
2166 	 */
2167 	selectIniValue: function(selection, key) {
2168 		var redraw = Redraw;
2169 		Redraw = false;
2170 		
2171 		if(!JseeUtil.findAll(new RegExp("^\\[" + JseeUtil.quote(selection) + "\\][^\\[]*?" + JseeUtil.quote(key) + "\\s*="))) {
2172 			return false;
2173 		}
2174 		
2175 		// =から行末までを選択
2176 		document.selection.CharRight(false,1);
2177 		document.selection.EndOfLine(true,eeLineView);
2178 		
2179 		// その時点での状態を保持
2180 		var textAfterEqual = document.selection.Text;
2181 		var topX = document.selection.GetTopPointX(eePosLogical);
2182 		var bottomX = document.selection.GetBottomPointX(eePosLogical);
2183 		var topY = document.selection.GetTopPointY(eePosLogical);
2184 		
2185 		// INIファイルの値として有効な文字が最初に現れる位置
2186 		var availableCharPos = textAfterEqual.search(/[^\s;]/);
2187 		// INIファイルの値として無効な文字が現れる位置 (コメントまたは末尾の空白)
2188 		var unavailableCharPos = textAfterEqual.search(/(\s+|\s*;.*)$/);
2189 		
2190 		// 有効文字が存在しない場合
2191 		if(availableCharPos < 0 || unavailableCharPos < availableCharPos) {
2192 			// 選択を解除
2193 			Redraw = redraw;
2194 			document.selection.SetActivePoint(eePosLogical, topX, topY);
2195 			
2196 		// 有効文字が存在する場合
2197 		} else {
2198 			// アクティブポイントを、有効文字の位置に変更
2199 			document.selection.SetActivePoint(eePosLogical, topX + availableCharPos, topY);
2200 			
2201 			// 無効な文字が存在しない場合
2202 			if(unavailableCharPos < 0) {
2203 				// 行末選択時の末尾まで選択範囲を伸ばす
2204 				Redraw = redraw;
2205 				document.selection.SetActivePoint(eePosLogical, bottomX, topY, true);
2206 			
2207 			// 無効な文字が存在する場合
2208 			} else {
2209 				// 無効な文字直前まで選択範囲を伸ばす
2210 				Redraw = redraw;
2211 				document.selection.SetActivePoint(eePosLogical, topX + unavailableCharPos, topY, true);
2212 			}
2213 		}
2214 		
2215 		return true;
2216 	},
2217 	
2218 	/**
2219 	 * ドキュメントの設定を判別して、文字列結合記号を取得します。<br />
2220 	 * SQLなら"||"、PerlとPHPなら"."、VBから始まる設定名なら"&"、それ以外は"+"を返します。
2221 	 * 
2222 	 * @function
2223 	 * 
2224 	 * @example
2225 	 * alert(JseeUtil.getConcater());
2226 	 * 
2227 	 * @returns {String} 文字列結合記号
2228 	 */
2229 	getConcater: function() {
2230 		var configName = document.ConfigName;
2231 		if(configName == "SQL") {
2232 			return "||";
2233 		}
2234 		if(configName == "Perl" || configName == "PHP") {
2235 			return ".";
2236 		}
2237 		if(/^VB/i.test(configName)) {
2238 			return "&";
2239 		}
2240 		return "+";
2241 	}
2242 };
2243 
2244 /**
2245  * 既存のclipboardDataを上書きします。<br />
2246  * clipboardDataが確実に動作するように、他の既存機能を使って動作を代替します。
2247  * 
2248  * @class
2249  */
2250 var clipboardData = {
2251 	_unusedChars: "→㌴㌓ㄑㄢᠠ᧠ꌛ",
2252 	_notLineCopyMatcher: /^→㌴㌓ㄑㄢᠠ᧠ꌛ/,
2253 	_brMatcher: /\r\n|\r|\n/g,
2254 	
2255 	/**
2256 	 * クリップボードデータを取得します。<br />
2257 	 * 必ず新規タブを開き、そこに<code>document.selection.Paste(eeCopyUnicode)</code>で貼り付けて、貼り付け結果を保持し、開いた新規タブを閉じてから、貼り付け結果を返します。
2258 	 * <p>
2259 	 * 既存機能では線形コピー時に<code>getData("BoxText")</code>を呼ぶと空文字を返しますが、<br />
2260 	 * このメソッドでは一行のみの箱型選択と区別できないため、最後に改行の付いた一行の線形コピー時にだけ空文字を返さず、クリップボードデータをそのまま返します。
2261 	 * </p>
2262 	 * 
2263 	 * @function
2264 	 * 
2265 	 * @example
2266 	 * alert(clipboardData.getData("Text"));
2267 	 * 
2268 	 * @param {String} type クリップボードデータタイプ ("Text"と"BoxText"に対応)
2269 	 * @returns {String} クリップボードデータ
2270 	 */
2271 	getData: function(type) {
2272 		if(type != "Text" && type != "BoxText") {
2273 			// 予想されないtypeを指定されるとこのエラーが発生する仕様なので、それに追従
2274 			throw new Error("プロシージャの呼び出し、または引数が不正です。");
2275 		}
2276 		
2277 		var redraw = Redraw;
2278 		Redraw = false;
2279 		var doc = document;
2280 		JseeUtil.newTab();
2281 		document.selection.SelectAll();
2282 		document.selection.Paste(eeCopyUnicode);
2283 		document.selection.SelectAll();
2284 		var result = document.selection.Text;
2285 		if(type == "BoxText") {
2286 			// "BoxText"が指定された場合、文字を入力後に再度貼り付けて、何もない状態での貼り付けと比較し、箱型選択だったかどうかを判別する
2287 			document.selection.Text = clipboardData._unusedChars;
2288 			document.selection.Paste(eeCopyUnicode);
2289 			document.selection.SelectAll();
2290 			// "BoxText"が指定された場合、線形コピーまたは行コピーの場合は空文字を返す仕様なので、それに追従
2291 			var text = document.selection.Text;
2292 			if(clipboardData._unusedChars + result == text || !clipboardData._notLineCopyMatcher.test(text)) {
2293 				// 一行のみの箱型選択と区別できないため、最後に改行の付いた一行の線形コピー時にだけ空文字を返さないようにする
2294 				if(!clipboardData._brMatcher.test(result) || clipboardData._brMatcher.test(result)) {
2295 					result = "";
2296 				}
2297 				//if(Window.clipboardData.getData("BoxText") != result) alert("既存のclipboardData.getData(\"BoxText\")が失敗しました。\n\ngetData:" + Window.clipboardData.getData("Text").replace(/^([\s\S]{20})[\s\S]*$/, "$1...") + "\n\n実際:" + result.replace(/^([\s\S]{20})[\s\S]*$/, "$1..."));
2298 			}
2299 		}
2300 		//else if(Window.clipboardData.getData("Text") != result) alert("既存のclipboardData.getData(\"Text\")が失敗しました。\n\ngetData:" + Window.clipboardData.getData("Text").replace(/^([\s\S]{20})[\s\S]*$/, "$1...") + "\n\n実際:" + result.replace(/^([\s\S]{20})[\s\S]*$/, "$1..."));
2301 		document.selection.Delete(1);
2302 		document.Close();
2303 		doc.Activate();
2304 		Redraw = redraw;
2305 		return result;
2306 	},
2307 	
2308 	/**
2309 	 * クリップボードデータを設定します。<br />
2310 	 * 必ず新規タブを開き、そこに指定されたデータを貼り付けて、指定されたタイプによる選択を行ってから、<code>document.selection.Copy(eeCopyUnicode)</code>でコピーして、新規タブを閉じます。
2311 	 * 
2312 	 * @function
2313 	 * 
2314 	 * @example
2315 	 * clipboardData.setData("Text", "hoge");
2316 	 * 
2317 	 * @param {String} type クリップボードデータタイプ ("Text"と"BoxText"に対応)
2318 	 * @param {String} data クリップボードデータ
2319 	 */
2320 	setData: function(type, data) {
2321 		if(type != "Text" && type != "BoxText") {
2322 			// 予想されないtypeを指定されるとこのエラーが発生する仕様なので、それに追従
2323 			throw new Error("プロシージャの呼び出し、または引数が不正です。");
2324 		}
2325 		var redraw = Redraw;
2326 		Redraw = false;
2327 		var doc = document;
2328 		JseeUtil.newTab();
2329 		document.selection.SelectAll();
2330 		document.selection.Text = data;
2331 		document.selection.SelectAll();
2332 		if(type == "Text") {
2333 			document.selection.Copy(eeCopyUnicode);
2334 		} else if(type == "BoxText") {
2335 			JseeUtil.selectAllBoxText();
2336 			document.selection.Copy(eeCopyUnicode);
2337 			document.selection.SelectAll();
2338 		}
2339 		document.selection.Delete(1);
2340 		document.Close();
2341 		doc.Activate();
2342 		Redraw = redraw;
2343 	},
2344 	
2345 	/**
2346 	 * クリップボードデータをクリアします。<br />
2347 	 * 必ず新規タブを開き、全選択を行ってから、<code>document.selection.Copy(eeCopyUnicode)</code>で空文字をコピーして、新規タブを閉じます。
2348 	 * 
2349 	 * @function
2350 	 * 
2351 	 * @example
2352 	 * clipboardData.setData("Text", "hoge");
2353 	 * alert(clipboardData.getData("Text"));
2354 	 * clipboardData.clearData();
2355 	 * alert(clipboardData.getData("Text"));
2356 	 */
2357 	clearData: function() {
2358 		var redraw = Redraw;
2359 		Redraw = false;
2360 		var doc = document;
2361 		JseeUtil.newTab();
2362 		document.selection.SelectAll();
2363 		document.selection.Copy(eeCopyUnicode);
2364 		document.Close();
2365 		doc.Activate();
2366 		Redraw = redraw;
2367 	}
2368 };
2369