nanashi_kana

gethtmldat

Mar 26th, 2015
138,674
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /********************************************************************/
  2. /*  gethtmldat.js :   ver.0.73   2017/10/04                         */
  3. /********************************************************************/
  4. //
  5. //  Jane2ch.exeでhtmlから過去ログを取得しdat形式に変換してLogsフォルダに格納する
  6. //  htmltodat-convert2.wsf+htmltodat.exeと同じような機能を果たす
  7. //  htmltodat-convert2.wsfとGetLog.wsfからコードを盗用してます(許して)
  8. //
  9. //==============================================================
  10. //  ※ 重要 ※
  11. //  なお、このスクリプトはpastbinのサイトから download するのではなく、
  12. //  RAW( http://pastebin.com/raw.php?i=3EZzrsxY )で表示したものか 又は
  13. //  下の方にある RAW Paste Data から、コピー&ペースト(CTRL+A,CTRL+C 及び CTRL+V)で、
  14. //  テキストエディタに貼り付け、gethtmldat.js という名前でShift-JISコード(メモ帳ではANSI)
  15. //  で保存し、Jane2ch.exe のあるフォルダに格納してください
  16. //  ※注意※ DOWNLOADすると、文字コードがUTF-8となり実行できません
  17. //           また、行番号付きでコピー&ペーストしても実行できません
  18. //==============================================================
  19. //
  20. //  Jane Styleでのコマンド登録方法は、メニュー - ツール - 設定 - 機能 - コマンド で
  21. //    コマンド名   - 過去ログ取得(gethtmldat)  ※ここには任意の名称をつけられます
  22. //    コマンド内容 - wscript "$BASEPATHgethtmldat.js" $LINK$URL
  23. //  を入力して、追加、OKします
  24. //       ※ なお、Jane直下フォルダでは無い場合はパスをフルパスで指定してください
  25. //          jane styleの下のscriptフォルダに入れた場合は、
  26. //                   wscript "$BASEPATHscript\gethtmldat.js" $LINK$URL
  27. //
  28. //
  29. //  起動オプション: 順不同 []内は省略可
  30. //    [+]http://~   url指定
  31. //                   (先頭に+を付けるとdatにURL末尾の指定範囲のレス部分だけ上書きする /aは無効)
  32. //    [/n:]999       レス番指定(そのレス番に飛ぶ)
  33. //    /f             urlを強制的に問い合わせる
  34. //    /w[:999]       スレを指定時間待って閉じ、開き直す(数値省略時 1200ms)
  35. //                   当該スレが開く前にキー送信がなされると現在開いている別スレが閉じてしまいます
  36. //    /a[ppend]      差分のみ取得
  37. //                  (管理人削除などで既存のログとサーバー上のデータに違いがあっても関知しない)
  38. //    /b[ak][1]      バックアップを取る(Logs\bak\の下に既存のdatファイルをコピーする)
  39. //                   /b1 にするとバックアップファイルが存在しているとコピーしない
  40. //    /redraw[:999]  再描画 スレを開いた後、指定時間待ってからキーコード('ALT','T','G','0')を送る
  41. //                   (数値省略時 1200ms)
  42. //    /z             Jane2ch.exeを終了し、情報(db,idx)設定後、再起動する(この場合 /w は不要)
  43. //                   ※ 高速Merge有効時には、SQLite3 ODBC Driver 又は sqlite3.exe のどちらかが必要
  44. //                       SQLite3 ODBC Driver : http://www.ch-werner.de/sqliteodbc/sqliteodbc.exe
  45. //                                             をインストール(exeを実行し、デフォルトなりで進める)
  46. //                       sqlite3.exe         : http://www.sqlite.org/download.html から、
  47. //                                             sqlite-shell-win32-x86-3080900.zip をダウンロード・
  48. //                                             解凍し、本スクリプトと同じフォルダに格納する
  49. //    "/log[:path]"  トレースログを指定のファイルに追加書き込みします
  50. //                   (ファイルパス名省略時は %temp%\スクリプト名.log)
  51. //    /keep_kako     渡された過去ログURLをそのまま処理する(したらば、next2ch、machi以外で有効)
  52. //    /proxy:server:port PROXYサーバー経由でアクセスする場合に指定する
  53. //    /cfg[:path]    カスタマイズ用定義ファイルの読み込み(path省略はスクリプトフォルダ\スクリプト名.cfg)
  54. //                   内容は、JScriptの文法で記述する
  55. //
  56. //  使い方:
  57. //    以下のa)~c)のいずれかで右クリックして上記登録したコマンドを選択する
  58. //      a) Jane Styleで、過去ログを開いたレス表示欄
  59. //         (2chでサーバーの移転があった場合は板一覧にある最新の移転先URLが受け渡されるので注意)
  60. //      b) 別スレに書かれているリンク
  61. //      c) 書き込みウィンドウ、メモ欄にURLを書き込みプレビューで現れるリンク
  62. //
  63. //  TIPS :
  64. //    ●★「過去ログ」と表示されている場合は、メニュー(スレッド)から「強制過去ログ化」のチェック
  65. //      (又はスレッドツールバーのゴミ箱アイコンをクリックして「強制過去ログ化」)
  66. //      を外すか又は、一旦ログを削除後にリンクから再取得してください
  67. //    ●レスが表示されない場合は、スレッドツールバーの「表示レス数/スレの再描画」アイコンを
  68. //      右クリックしてください
  69. //    ●レス内容が「スレの再描画」でも変わらない場合は、
  70. //        (「お気に入り」を「更新チェック」後に、その中のスレを本スクリプトで取得した場合などが相当)
  71. //      スレ一覧画面の「お気に入りグループ名」タブを閉じてから「スレの再描画」をしてみてください
  72. //      また「検索結果」「ログ一覧」「最近読込」「最近書込」「OpenThreads」の場合も同様です
  73. //    ●未読状態が既読状態にならない場合やレス番リンクのポップアップが表示されなかったり
  74. //      ID抽出回数が0回になる場合は
  75. //      1)ステータスが過去ログ以外の時、所属する板を開いてスレ一覧更新をして、
  76. //        又は、スレを開きなおして、最新レス末尾までスクロールさせる
  77. //      2)過去ログの場合、上記「強制過去ログ化」のチェックを外した後、
  78. //        又は、>>999 などの未読状態のレスを指すリンクをクリックする
  79. //        又は、名前欄1001をクリックする
  80. //        又はアドレスバーに「スレURL/999」のようなレス番付きで入力する
  81. //        「最後までスクロールしたスレを既読にする」「再描画する」の場合は、末尾までスクロールさせる
  82. //      ★なお、新着チェックができる(ボタンが押せる)のなら新着チェックでよい
  83. //    ●2ch.netでftp://問題により520エラー又はデータが途切れてしまう場合の部分的な救済方法
  84. //        webブラウザで問題となるレス番をtrial and errorで目星をつけて、それを避けるように
  85. //        範囲指定してデータを取得する
  86. //        例えば、レス番50が引っ掛かっている場合は、-49 と 51- の2回に分けて取得する
  87. //        コマンド登録時には、+$LINK$URLとし、URL末尾に範囲指定付きのリンクから使うか
  88. //        /f も追加して、実行時にURL末尾にレス番範囲を追加で入力する
  89. //
  90. //  修正履歴:
  91. //    http://pastebin.com/my14uCkr を参照
  92. //==============================================================
  93.  
  94. /*@cc_on
  95.     @set @DEBUG = false;
  96. // 以下をtrueにすると2chで最初にJSONでの取得を試みる(お遊び機能:仕様等一切不明)
  97.     @set @EnabledTest2chJSON = false;
  98. //漢字コード変換にbasp21.dllを使う
  99. // http://www.hi-ho.ne.jp/babaq/basp21.htmlのBASP21-2003-0211.exeのインストールが必要
  100.     @set @EnabledBASP21 = false;
  101. @*/
  102.  
  103. var HTTP_HEADERS = {
  104.   "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
  105. };
  106. var HTTP_TIMEOUT = 120 * 1000;
  107. var IDX_STATE = { "2ch":3 , "jbbs":3 }; //=0:現役 =3:dat落ち =10:過去ログ
  108. var ABN_MSG = "透明あぼ~ん<>透明あぼ~ん<>透明あぼ~ん ID:DELETED<>透明あぼ~ん<>透明あぼ~ん";
  109. var ABN_MSG_PINK = "うふ~ん<>うふ~ん<>うふ~ん ID:DELETED<>うふ~ん<>うふ~ん";
  110. var NOT_GET_MSG = "未取得<>未取得<>未取得 ID:NONE<>未取得<>";
  111.  
  112. var fso   = new ActiveXObject("Scripting.FileSystemObject");
  113. var shell = new ActiveXObject("WScript.Shell");
  114. var sha   = new ActiveXObject("Shell.Application");
  115. var strm  = new ActiveXObject("ADODB.Stream");
  116. var http;
  117. var USED_WINHTTP = true;
  118. try { http = new ActiveXObject("WinHttp.WinHttpRequest.5.1"); } catch(e){}
  119. if (!http) try { http = new ActiveXObject("Msxml2.ServerXMLHTTP.6.0"); } catch(e){}
  120. if (!http) try { http = new ActiveXObject("Msxml2.ServerXMLHTTP.3.0"); } catch(e){}
  121. if (!http) var USED_WINHTTP = false;
  122. if (!http) try { http = new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch(e){}
  123. if (!http) { http  = new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
  124. var basp; /*@if (@EnabledBASP21) try { basp = new ActiveXObject("basp21"); } catch(e){} @end @*/
  125.  
  126. var JANE_PATH = shell.CurrentDirectory.replace(/[^\\]$/,"$&\\");
  127. var SCRIPT_PATH = fso.GetParentFolderName( WScript.ScriptFullName ).replace(/[^\\]$/,"$&\\");
  128. //shell.CurrentDirectory = JANE_PATH;
  129. var JANE_EXE  = "Jane2ch.exe";
  130. var JANE_INI  = "Jane2ch.ini";
  131. var JANE_BRD  = "Jane2ch.brd";
  132. var JANE_LOGS = "Logs\\2ch\\";
  133. var JANE_BAK  = "Logs\\bak\\";
  134. var JANE_BRDCUSTOMIZE_INI = "brdcustomize.ini";
  135. var MY_NAME   = WScript.ScriptName.split(/\.(?=[^.]+$)/)[0];
  136. var TRACE_LOG = replaceEnv("%temp%\\"+MY_NAME+".log").replace(/\\\\/g,"\\");
  137.  
  138. //カスタマイズ用定義ファイル指定(この指定より引数/cfg:での指定の方が優先される)
  139. var CFG_FILE = SCRIPT_PATH + MY_NAME + ".cfg";
  140.  
  141. //欠落しているレス番をあぼーんメッセージで補う(2ch互換BBSのみ)
  142. //   +URL指定時は無効で元のレスのままかあるいは未取得メッセージで穴埋めされる
  143. var FILL_ABONE = 0; //0:問い合わせる 1:常に穴埋めする 2:常に詰める
  144. //既存のdatファイルよりレス数が大きくない場合にdatファイルを上書きするかどうか(差分/aの場合は無効)
  145. var REWRITE_DAT = 0; //0:問い合わせる 1:上書きする 2:一致の場合は中止する 3:常に中止する
  146.  
  147. //BEアイコン以外の<img>タグ(現状はお絵かきのみ)を変換するときのスキームを指定する ("http" or "sssp")
  148. var IMG_TO = "sssp:";
  149.  
  150. //bakusai用
  151. var OPT_BBS = {
  152.     "//bakusai.com": [thread_bakusai, "【仮想板:爆サイ】", /\/\/(bakusai\.com[^\/]*)/ ]
  153. };
  154. var MAX_READ_PAGES_OF_THREAD_LIST_CREATE = 3; //subject.txt作成時
  155. var MAX_READ_PAGES_OF_THREAD_LIST_UPDATE = 1; //subject.txt更新時
  156. var BROWSER_PATH = "";
  157.  
  158. //==============================================================
  159.  
  160. function o(s) { /*@if (@DEBUG) WScript.Echo(s); @end @*/ }
  161. function q() {WScript.Quit();}
  162. String.prototype.expand = function(v) { return this.replace(/\$(\d)/g, function(m, n) { return v[n]; }); }
  163. String.prototype.convDontUseStr = function() {
  164.     var tbl0 = { "CON":"$CON", "AUX":"$AUX", "NUL":"$NUL" /*, "PRN":"$PRN", "COM([0-2])":"$COM$1", "LPT([0-2])":"$LPT$1" */ }
  165.     var tbl1 = { "\\":"$Backslash", "/":"$Slash", ":":"$Colon", "*":"$Asterisk", "?":"$Question" }
  166.     var s = this.replace(/[. ]+$/,"");
  167.     for(var i in tbl0) { s=s.replace( new RegExp("^"+i+"$","i") , tbl0[i]); }
  168.     return s.replace(/[\\/:*?]/g, function (w){return tbl1[w];});
  169. }
  170. var trace = function(){};
  171. function Err(s,t) {s=ErrMsg(s,t); trace("---- "+s+" ----",2); throw new Error(s);}
  172. function ErrMsg(s,t) { return (ERROR_MSG[s]?ERROR_MSG[s]:"Unknown error") + (t?t:"");}
  173.  
  174. var ERROR_MSG_STOP = "処理を中止しました";
  175. var ERROR_MSG = {
  176.      "E_already_there" : "このスレは既に取得しています"
  177.     ,"E_illegal_url" : "このURLでは処理できません"
  178.     ,"E_remained_garbage" : "レスデータのみの抽出に失敗しました"
  179.     ,"E_block_not_found" : "htmlからスレブロックが見つかりません"
  180.     ,"E_title_not_found" : "スレタイトルが見つかりません"
  181.     ,"E_require_sqlite" : "sqlite3.exe 又は SQLite3 ODBC Driver が必要です"
  182.     ,"E_require_sqlODBC" : "SQLite3 ODBC Driver が必要です"
  183.     ,"E_stop" : ERROR_MSG_STOP
  184.     ,"E_no_data" : "取得データはありません"
  185.     ,"E_http_timeout" : "受信タイムアウト"
  186.     ,"E_http_recieve" : "http受信エラー"
  187.     ,"E_bbs_not_found" : "掲示板番号が不正です"
  188.     ,"E_key_not_found" :"スレッド番号が不正です"
  189.     ,"E_thread_not_found" : "スレッドがありません"
  190.     ,"E_brditem_not_found" : "dat格納場所が見つかりません"
  191.     ,"E_loadfile" : "ファイル読み込みエラー"
  192.     ,"E_savefile" : "ファイル書き込みエラー"
  193.     ,"E_not_suported_proxy" : "PROXY指定はできません(IEの設定が使われます)"
  194.     ,"E_data_error" : "レスデータの抽出で異常を検出しました"
  195.     ,"E_data_not_found" : "データがみつかりません"
  196.     ,"E_no_support" : "この機能はサポートしません"
  197. }
  198.  
  199. function object(o) {
  200.     var f = object.f, i, len, n, prop;
  201.     f.prototype = o;
  202.     n = new f;
  203.     for (i=1, len=arguments.length; i<len; ++i) for (prop in arguments[i]) n[prop] = arguments[i][prop];
  204.     return n;
  205. }
  206. object.f = function(){};
  207. //--- http://with-love-from-siberia.blogspot.jp/2009/12/msgbox-inputbox-in-jscript.html -----------
  208. var vb = {};
  209. vb.Function = function(func) { return function() { return vb.Function.eval.call(this, func, arguments); }; };
  210. vb.Function.eval = function(func) {
  211.     var args = Array.prototype.slice.call(arguments[1]);
  212.     for (var i = 0; i < args.length; i++) {
  213.         if ( typeof args[i] != 'string' ) { continue; }
  214.         args[i] = '"' + args[i].replace(/"/g, '" + Chr(34) + "') + '"';
  215.     }
  216.     var vbe;
  217.     vbe = new ActiveXObject('ScriptControl');
  218.     vbe.Language = 'VBScript';
  219.     return vbe.eval(func + '(' + args.join(', ') + ')');
  220. };
  221. var InputBox = vb.Function('InputBox');
  222. var MsgBox = vb.Function('MsgBox');
  223.  
  224. //==============================================================
  225.  
  226. var args = parmParse(WScript.Arguments);
  227.  
  228. var URL_2ch = {
  229.   RE_HOST : new RegExp("^http://\\w+\\.(?:[25]ch\\.net|bbspink\\.com)/")    //この定義はdat取得できないサイトの判別に使う
  230.  ,RE_URL  : new RegExp("^http://((\\w+(?=\\.\\w+\\.))?(\\.?(\\w+)[^\\/]+?))/(?:(?:test|bbs)/.+?/)?(\\w+)/(?:kako/.+?/)?(((\\d+)\\d)\\d{5})(/|\\.html|\\.dat(?:\\.gz)?)?")
  231.  ,range   : ""
  232.  ,set_prm : function(url) { this._set_prm(url, 1, null, 5, 6); this.kako = /\/kako\//.test(url) && delete args.append; }
  233.  ,otherURLType : 0
  234.  ,otherURL : ""
  235.  ,_set_prm: function(url,p1,p2,p3,p4) {
  236.                 var r;
  237.                 this.prm  = url.match(this.RE_URL) || Err("E_illegal_url");
  238.                 if(/\d$/.test(this.prm[0])) this.prm[0] += "/";
  239.                 this.url  = this.prm[0];
  240.                 this.url2 = this.prm[0];
  241.                 switch(this.otherURLType) {
  242.                     case 1: this.url = this.otherURL+this.url; break;
  243.                     case 2: this.url = this.otherURL; break;
  244.                     default: break;
  245.                 }
  246.                 this._setRange(url);
  247.                 this.site     = this.prm[p1];
  248.                 this.category = (p2) ? this.prm[p2] : "";
  249.                 this.brdID    = this.prm[p3];
  250.                 this.thrID    = this.prm[p4];
  251.             }
  252.  ,_setRange :   function(url) {
  253.                     var range;
  254.                     if(this.prm[0].slice(-1)=="/") {
  255.                         range = url.match(/\/n?((\d*)n?(-?)[^\/]*)$/)||[];
  256.                         this.url2 += (args.opt_n||range[2]||"");
  257.                         if(range[3]) this.range = ((range[2]?"":"1")+range[1]||"").replace(/n/ig,"");
  258.                         else if(range[2]) this.range = range[2] + "-" + range[2];
  259.                     }
  260.                 }
  261.  ,appendRange : function(top) {
  262.                     if (args.overwrite) this.url += this.range;
  263.                     else                this.url += ((args.append && top) ? top+"-n" : "");
  264.                 }
  265.  ,getUrl2 : function() { return this.url2; }
  266.  ,entryCheck : function(url) {
  267.                 var r,i;
  268.                
  269.                 var ptn=[
  270.                   [ /^https?((?::\/\/www\.google\.co\.jp\/url\?q=https?)?:\/\/webcache\.googleusercontent\.com\/search(?:\?q=|%3Fq%3D)cache:.+?:.*https?:\/\/([^+&%]+).*=(?:(?!http).)+)/ , 1]
  271.                   ,[ /^https?(:\/\/webcache\.googleusercontent\.com\/search\?q=cache:.+?:([^+&%]+).*=(?:(?!http).)+)/ ,2 ]
  272.                   ,[ /^https?(:\/\/cache\.yahoofs\.jp\/search\/cache\?.*&u=([^&]+)(?:&.*=(?:(?!http).)+)?)/ ,2 ]
  273.                   ,[ /http:(\/\/(bakusai\.com(?:\.[^\/]+)?\/thr_(?:res|tl)\/acode=\d+\/ctgid=\d+(?:\/test\/read\.cgi)?(?:\/(?:\w+=)?[\d-]+)+\/))/ , 3 ]
  274.                 ];
  275.                
  276.                 for(i=0;i<ptn.length;i++) {
  277.                     r = url.match(ptn[i][0]);
  278.                     if(r) {
  279.                         this.otherURLType=ptn[i][1];
  280.                         switch(this.otherURLType) {
  281.                             case 1: this.otherURL = r[1]; return "http"+r[2];
  282.                             case 2: this.otherURL = "http"+r[1]; return "http://"+r[2].replace(/%2F/ig,"/");
  283.                             case 3: this.otherURL = r[0]; return "http://"+r[2];
  284.                         }
  285.                     }
  286.                 }
  287.                 return url.replace(/^https?(:.+?)(?:http.+)?$/,"http$1");
  288.             }
  289.  ,isOPT_BBS : function(url) {
  290.                 var i,r;
  291.                 for(i in OPT_BBS) { if(url.indexOf(i)>0) r=i; }
  292.                 this.optItem = r;
  293.                 return r;
  294.             }
  295. }
  296. var URL_jbbs = object(URL_2ch,{
  297.   RE_HOST : new RegExp("^http://jbbs\\.(?:shitaraba|livedoor)\\.(?:net|jp|com)/")
  298.  ,RE_URL  : new RegExp("^http://(jbbs\\.shitaraba\\.net)/bbs/\\w+\\.cgi/(\\w+)/(\\d+)/(\\d+)/?")
  299.  ,set_prm : function(url) { this._set_prm(url,1,2,3,4); }
  300. });
  301. var URL_machi = object(URL_2ch,{
  302.   RE_HOST : new RegExp("^http://(?:\\w+\\.)?machi\\.to/")
  303.  ,RE_URL  : new RegExp("^http://((?:\\w+\\.)?machi\\.to)/bbs/\\w+\\.cgi/2/(\\w+)/(\\d+)/?")
  304.  ,set_prm : function(url) { this._set_prm(url,1,null,2,3); }
  305.  ,getUrl2 : function() { return this.url2.replace(/\/offlaw\.cgi\/2\//,"/read.cgi/"); }
  306. });
  307. var URL = URL_2ch;
  308.  
  309. var Jane_2ch = {
  310.  ini          : loadFile2(JANE_INI)
  311.  ,brd         : ""
  312.  ,LogBasePath : ""
  313.  ,init        : function() { this._setLogBasePath(); this._loadBRD(); }
  314.  ,getINI      : function(p) { var m=this._search(this.ini, this._re_ini, p); return m && RegExp.$1 || p[3]; }
  315.  ,getBRD      : function(p) {
  316.                     var m = this._search(this.brd
  317.                                          , (/\.[25]ch\.net/.test(p[3]))?this._re_brd2:this._re_brd
  318.                                          , p);
  319.                     var b1=RegExp.$1, b2=RegExp.$4;
  320.                     var host2=RegExp.$3, host1=RegExp.$2;
  321.                     return (m) ? [b1.convDontUseStr()+"\\"+b2.convDontUseStr()+"\\",host1,host2] : null;
  322.                 }
  323.  ,_re_ini     : "^\\[$1\\]\\r\\n(?:.*\\r\\n)*?(?:$2=(.*)|\\[.*\\])\\r\\n"
  324.  ,_re_brd     : "^([ \\S]+).*\\r\\n(?:\\t.*\\r\\n)*\\t(\\w*)($3)\\t$5\\t([^\\t\\r\\n]+?) *\\r\\n"
  325.  ,_re_brd2    : "^([ \\S]+).*\\r\\n(?:\\t.*\\r\\n)*\\t(\\w+)\\.([25]ch\\.net)\\t$5\\t([^\\t\\r\\n]+?) *\\r\\n"
  326.  ,_search     : function(f,s,p) { return f.match(new RegExp(this._expand(s,p),"img")); }
  327.  ,_expand     : function(s,p) { return s.replace(/\$(\d)/g, function(m, n) { return (""+p[n]).replace(/\./g,"\\."); }); }
  328.  ,_setLogBasePath : function() { this.LogBasePath = this.getINI([,"PATH","LogBasePath",""]).replace(/[^\\]$/,"$&\\");}
  329.  ,_loadBRD     : function() { this.brd = loadFile2(this.LogBasePath + JANE_BRD); }
  330. }
  331. var Jane_jbbs = object(Jane_2ch,{
  332.   _re_brd : "^([ \\S]+).*\\r\\n(?:\\t.*\\r\\n)*?\\t(($1/$2))\\t$3\\t([^\\t\\r\\n]+?) *\\r\\n"
  333. });
  334. var Jane_machi = object(Jane_2ch,{
  335.   _re_brd : "^([ \\S]+).*\\r\\n(?:\\t.*\\r\\n)*?\\t(($1))\\t$2\\t([^\\t\\r\\n]+?) *\\r\\n"
  336. });
  337.  
  338. var Jane = Jane_2ch;
  339. Jane.init();
  340.  
  341. if (args.trace !== void 0) { trace = new Trace(); }
  342. trace("--- start ---   "+args.url,1);trace("",1)
  343.  
  344. //----------------------
  345.  
  346. var DatInfo = {
  347.   cnt      : 0
  348.  ,offset   : 0
  349.  ,title    : ""
  350.  ,msg      : (args.overwrite) ? [] : ""
  351.  ,setInfo  : function(dat,bak,brd) {
  352.                 this.dat = dat;
  353.                 this.bak = bak;
  354.                 this.brd = brd;
  355.                 if (fso.FileExists(dat)) {
  356.                     trace();
  357.                     var msg = loadFile(dat);
  358.                     this.cnt  = msg.split("\n").length-1;
  359.                     this.title = (msg.match(/^(?:(?:(?:(?!<>).)*)<>|(?:[^,]*),){4}(.*)$/m)||[])[1];
  360.                     if (args.overwrite) { this.msg = msg.slice(0,-1).split("\n"); }
  361.                     else this.msg="";
  362.                     trace("load file "+dat.replace(/^.*\\(\d+\.dat)/,"$1")+" : "+this.title+" lines="+this.cnt);
  363.                 }
  364.              }
  365.  ,copyDat  : function() {
  366.                 if (fso.FileExists(this.dat)) {
  367.                     path(this.bak);
  368.                     if (args.bak<0 || !fso.FileExists(this.bak) )
  369.                         try{ trace(); fso.CopyFile(this.dat, this.bak, true); trace("copy file:"+this.bak);} catch(e) {}
  370.                 }
  371.              }
  372.  ,setNewDatInfo : function(title, lines) {
  373.                     this.title = title;
  374.                     this.lines = parseInt(lines,10);
  375.                   }
  376.  ,update :  function(msgs) {
  377.                 this.msg = msgs;
  378.                 this.cnt = this.msg.length;
  379.             }
  380. }
  381.  
  382. //================================================================================
  383.  
  384.  
  385. var Thread = {
  386.   re_sel      : -1
  387.  ,REG_PATTERN : [ {} ]
  388.  ,msg         : ""
  389.  ,bin         : null
  390.  ,resNo       : []
  391.  ,resNo_offset : 0
  392.  ,requireFillAbn  : false
  393.  ,prepare     : function() {}
  394.  ,urlReplace  : function(u) { return u; }
  395.  ,preReplace  : function() {}
  396.  ,mainReplace : function() { this.msg = this.msg.replace(this.REG_PATTERN[this.re_sel].msg, this.REG_PATTERN[this.re_sel].rep); }
  397.  ,postReplace : function() {}
  398.  ,createBrdFolder : function() { return null;}
  399.  ,testTBlock : function() {
  400.                  var i,rc,ss;
  401.                  for(i=0; i<this.REG_PATTERN.length; i++) {
  402.                     if(ss=this.msg.match(this.REG_PATTERN[i].thr)) {this.re_sel =i; break;}
  403.                  }
  404.                  rc = ss ? true : false;
  405.                  return rc;
  406.                }
  407.  ,setTBlock : function() {
  408.                 var s;
  409.                 if(this.re_sel>=0) {
  410.                     s=this.msg.match(this.REG_PATTERN[this.re_sel].thr);
  411.                     if(s) { this.msg=s[1]||""; s=s[2]; }
  412.                 }
  413.                 return s;
  414.               }
  415.  ,getTitle    : function(s) { this.title = (s.match(/<title>(.*?)\n*<\/title>/i)||[])[1]; }
  416.  ,readHTTP    : function(url) {
  417.     trace("try URL="+url,9);
  418.     this.msg = readHTTP(url)[0];
  419.     this.testTBlock(); //re_selの設定
  420.     this.preReplace();
  421.     this.getTitle(this.msg);
  422.     if (this.re_sel<0) Err("E_block_not_found","\n\nTITLE="+this.title+"\n文字数="+this.msg.length);
  423.     this.setTBlock();
  424.     return true;
  425.   }
  426.  ,getFirst    : function() { return (args.overwrite) ? parseInt(URL.range,10) : (args.append) ? DatInfo.cnt + 1 : 1; }
  427.  ,getLast     : function() { return parseInt((this.msg.match(/(?:^|\n)(\d+).+\n$/i)||[])[1],10)-this.resNo_offset; }
  428.  ,delContents : function(s) { return s.replace(/^(?:(?:(?!<>).)*<>|(?:[^,]*,)){4}.*$/mg, ""); }
  429.  ,checkData   : function(s) {
  430.                     var ln = this.checkData0(s);
  431.                     if(FILL_ABONE==2) return;
  432.                     var resTbl=this.msg.match(/^(\d+)/mg)||[];
  433.                     for(var i=1; i<resTbl.length; i++) {
  434.                         if(args.overwrite && i==1
  435.                             && parseInt(resTbl[1],10)==this.first && parseInt(resTbl[0],10)==1) continue;
  436.                         if(parseInt(resTbl[i],10)!=parseInt(resTbl[i-1],10)+1) {
  437.                             this.requireFillAbn = true;
  438.                             break;
  439.                         }
  440.                     }
  441.                     if (FILL_ABONE==0 && this.requireFillAbn) {
  442.                         switch(shell.Popup("抽出したレス数が合いません。"
  443.                                             +"(先頭="+this.first+" 末尾="+this.last+" レス数="+ln.length+")"
  444.                                             +"\n抜けているレス番を穴埋めしますか? [はい] [いいえ]"
  445.                                             +"\n又は、ここで処理を中断するなら [キャンセル]"
  446.                                             +"\nを選択してください。", 0, MY_NAME, 3+48)) {
  447.                             case 6: this.requireFillAbn = true; break;
  448.                             case 7: this.requireFillAbn = false; break;
  449.                             default: Err("E_stop"); //case 2
  450.                         }
  451.                     }
  452.                 }
  453.  ,checkData0  : function(s) {
  454.                     var ln = this.delContents(s);
  455.                     if (ln.replace(/\n/g,"").length > 0) Err("E_remained_garbage");
  456.                     return ln;
  457.                 }
  458.  ,addTitle    : function() { this.msg = this.msg.replace(/^.*?$/m, "$&"+this.title); }
  459.  ,delDupData  : function() { //わざと重複して要求した先頭レス番データ削除
  460.                     if ((this.msg.match(/^(\d+)/i)||[])[1] == DatInfo.cnt) { this.msg = this.msg.replace(/^.*\n/m, ""); }
  461.                 }
  462.  ,fillAbn     : function() {
  463.                     // 透明あぼーんレスを挿入する
  464.                     var d = [], n = this.first, m;
  465.                     var s = this.msg.split("\n");
  466.                     for (var i=0; i<s.length; i++) {
  467.                         m = parseInt(s[i].replace(/^(\d+).+$/, "$1"),10);
  468.                         if(n>m) continue; //レス番重複または逆転時はスキップ
  469.                         for (var j=n; j<m; j++) {
  470.                             d.push(j+"<>"+(/\.bbspink\.com$/.test(URL.site)?ABN_MSG_PINK:ABN_MSG));
  471.                             n++;
  472.                         }
  473.                         d.push(s[i]); n++;
  474.                     }
  475.                     this.msg = d.join("\n");
  476.                 }
  477.  ,delResNo    : function() { this.msg = this.msg.replace(/^\d+<>/mg, ""); }
  478.  ,overwriteMsg : function() {
  479.                     var data = this.msg.split("\n");
  480.                     var msg = DatInfo.msg;
  481.                     var i;
  482.                     for (i=0; i<data.length; i++) msg[parseInt(data[i],10)-1] = data[i].replace(/^\d+<>/,"");
  483.                     for (i=0; i<msg.length; i++) if(!msg[i]) msg[i] = NOT_GET_MSG;
  484.                     this.msg = msg.join("\n");
  485.                     if(this.msg.slice(-1)!="\n") this.msg += "\n";
  486.                  }
  487.  ,rebuildSubject : function() { Err("E_no_support"); }
  488.  ,proc        : function() {
  489.                     this.prepare();
  490.                     URL.set_prm(this.urlReplace(args.url));
  491.                     var brd, m = Jane.getBRD(URL.prm);
  492.                     if(/^(\w+)\.[25]ch\.net/.test(URL.site)) {
  493.                         // URLをbrdの登録されているHOST名に置換する(2ch/5ch対策)
  494.                         if(RegExp.$1=="itest") { URL.set_prm(URL.url.replace(/\w+\.[25]ch\.net/,m[1]+"."+m[2])); }
  495.                         else                   { URL.set_prm(URL.url.replace(/[25]ch\.net/,m[2])); }
  496.                     }
  497.                     if (m) brd = m[0];
  498.                     if (!brd) brd = this.createBrdFolder();
  499.                     if (!brd) Err("E_brditem_not_found");
  500.                     var dat = Jane.LogBasePath + JANE_LOGS + brd + URL.thrID + ".dat";
  501.                     var bak = Jane.LogBasePath + JANE_BAK + brd + URL.thrID + ".dat";
  502.                     DatInfo.setInfo(dat, bak, brd);
  503.                     if (args.subject) { this.rebuildSubject(); return; }
  504.                     if (args.browser) { invokeBrowser(URL.url); return; }
  505.                     URL.appendRange(DatInfo.cnt);
  506.                     if (trace()|this.readHTTP(URL.url)|trace("HTTP total read time")) {
  507.                         if(!this.msg) Err("E_block_not_found","\n\nTITLE="+this.title);
  508.                         if (!this.noChkTitle && !this.title) Err("E_title_not_found");
  509.                         trace(); this.mainReplace(); trace("html->dat main Replace");
  510.                         if (args.append) {
  511.                             trace(); this.delDupData(); trace("del dupdata time");
  512.                             if(this.msg=="") Err("E_already_there");
  513.                         }
  514.                         this.first = this.getFirst();
  515.                         this.last  = this.getLast();
  516.                         this.checkData(this.msg);
  517.                         if (this.first==1) { this.addTitle(); }
  518.                         if(this.requireFillAbn) { trace(); this.fillAbn(); trace("透明abnレス挿入"); }
  519.                         trace(); this.postReplace(); trace("post Replace");
  520.                         if(args.overwrite) this.overwriteMsg();
  521.                         else               this.delResNo();
  522.                     }
  523.                     trace("スレタイトル:"+this.title+"\tレス数:"+this.last,9);
  524.                     saveDat(dat, this.msg, this.bin);
  525.                     DatInfo.setNewDatInfo(this.title, this.last);
  526.                     saveIdx("http://"+URL.site+((URL.category)?"/"+URL.category:"")+"/"+URL.brdID);
  527.                     invokeJaneStyle(URL.getUrl2());
  528.                     trace("---- end ----",1);
  529.                 }
  530. }
  531.  
  532. var Thread_2ch = object(Thread,{
  533.   read_cgi_ver : ""
  534.  ,REG_PATTERN : [
  535.    // ver 07.0.0 / ver.07.0.1
  536.    { "thr" : /<div class="thread">(<div class="post"[^>]*><div class="meta">[\s\S]+<\/span><\/div><\/div>)((?:<br>)?<\/div>|[\s\S]*?$)/i
  537.      ,"msg" : /[\s\S]*?<div class="post"[^>]*>(?:<[^>]*?>)*<span class="number">(\d+).+?<span class="name"><b>(?:<a href="mailto:(.+?)">)?(.*?)(?:<\/a>)?<\/b><\/span><span class="date">(.*?)<\/span>(<span class="be[^>]*><a[^>]*>[^<]*<\/a><\/span>)?<\/div><div class="message"><span class="escaped">(.*?)<\/span><\/div><\/div>/ig
  538.      ,"rep" : "$1<>$3<>$2<>$4$5<>$6<>\n"
  539.      ,"preReplace" : [ [/(?:<div class="post"[^>]* id="0"|<div (?:class|id)=[^>]*banner)[\s\S]*?(?=<div class="post"|(?:<br>)?<\/div><div class="push">|$)/ig,""]
  540.                       ,[/<a href="(?:https?:)?\/\/(?!be\.[25]ch\.net\/).*?>([^<]*)<\/a>/ig, "$1"] ]
  541.     ,"postReplace" : [ [/<span class="be[^>]*><a href="[^"]+\/(\d+)"[^>]*>\?(.*?)<\/a><\/span>/ig," BE:$1-$2"]
  542.                       ,[/^(\d+<>.*?<>.*?<>.*? BE:\d+-[^ <]+) BE:\d+-[^ <]+/mg,"$1"]
  543.                       ,[/<\/span><span class="uid"[^>]*>/ig, " "]
  544.                       ,[/<img src="(?:https?:)?(\/\/img\.[25]ch\.net\/.+?)">/ig, "sssp:$1"]
  545.                       ,[/<img src="(?:https?:)?(.*?)">/ig, IMG_TO+"$1"] ]
  546.    }
  547.  
  548.   // ver 06.0.0
  549.   ,{ "thr" : /<div class="thread"[^>]*>((?:<div class="post".+?<\/div><\/div>)+)(<\/div>)?/i
  550.     ,"msg" : /.*?<div class="number">(\d+).+?<\/div><div class="name"><b>(?:<a href="mailto:(.+?)">)?(.*?)(?:<\/a>)?<\/b><\/div><div class="date">(.*?)<\/div><div class="message">(.*?)<\/div><\/div>(?=<div class="post"|$)/ig
  551.     ,"rep" : "$1<>$3<>$2<>$4<>$5<>\n"
  552.     ,"preReplace" : [ [/(?:<div class="post"[^>]* id="0"|<div (?:class|id)=[^>]*banner)[\s\S]*?(?=<div class="post"|<\/div><div class="cLength"|$)/ig,""]
  553.                      ,[/<a href="(?:https?:)?\/\/(?!be\.[25]ch\.net\/).*?>([^<]*)<\/a>/ig, "$1"] ]
  554.      ,"postReplace" : [ [/<\/div><div class="be[^>]*><a href="[^"]+\/(\d+)"[^>]*>\?(.*?)<\/a>/ig," BE:$1-$2"]
  555.                        ,[/<img src="(?:https?:)?(\/\/img\.[25]ch\.net\/.+?)">/ig, "sssp:$1"]
  556.                        ,[/<img src="(?:https?:)?(.*?)">/ig, IMG_TO+"$1"] ]
  557.     }
  558.  
  559.    // ver 06.0.0 ppspink
  560.    ,{ "thr" : /(<section class="thread"[\s\S]+<\/dd><\/dl>)(<\/section>)/i
  561.      ,"msg" : /[\s\S]*?<dl class="post"[^>]*>(?:<[^>]*?>)*<span class="number">(\d+).+?<span class="name"><b>.*?(?:<a href="mailto:(.+?)">)?(.*?)(?:<\/a>)?<\/b><\/span><span class="date">(.*?)<\/span>(<div class="be[^>]*><a[^>]*>[^<]*<\/a><\/div>)?<\/dt><dd class="thread_in">(.*?)(?:<\/dd><\/dl>|$)/ig
  562.      ,"rep" : "$1<>$3<>$2<>$4$5<>$6<>\n"
  563.      ,"preReplace" : [ [/(?:<dl class="post"[^>]* id="0"|<div (?:class|id)=[^>]*banner)[\s\S]*?(?=<section|<dl class="post"|$)/ig,""]
  564.                       ,[/<a href="(?:https?:)?\/\/(?!be\.[25]ch\.net\/).*?>([^<]*)<\/a>/ig, "$1"] ]
  565.     ,"postReplace" : [ [/<div class="be[^>]*><a href="[^"]+\/(\d+)"[^>]*>\?(.*?)<\/a><\/div>/ig," BE:$1-$2"]
  566.                       ,[/^(\d+<>)<font color="green">(.*?)<\/font>(?=<>)/img, "$1$2"]
  567.                       ,[/<img src="(?:https?:)?(\/\/img\.[25]ch\.net\/.+?)">/ig, "sssp:$1"]
  568.                       ,[/<img src="(?:https?:)?(.*?)">/ig, IMG_TO+"$1"] ]
  569.    }
  570.  
  571.   // ver 05.02.02
  572.   ,{ "thr" : /^[\s\S]*?<dl[^>]*>\s*(?:<font[^>]*>.*<\/font>.*\s*)?((?:\s*<dt[^>]*>.+?<dd>.+\r?\n?)+)(\s*<)?/i
  573.     ,"msg" : /[ \t]*<dt.*?>([0-9]+).+?(?:"mailto:(.+?)">)?<b[^>]*>(.*?)<\/b>(?:<\/font>|<\/a>)?(?: ?投稿日)? ?:(.*?)<dd>(.*?)(?:<br>)*(?=<dt.*?>|$)/img
  574.     ,"rep" : "$1<>$3<>$2<>$4<>$5<>"
  575.     ,"preReplace" : [ [/(?:<div (?:class|id)=[^>]*banner)[\s\S]*?(?=<dt|<\/dl>|$)/ig,""]
  576.                      ,[/<a href="(?:https?:)?\/\/.*?>([^<]*)<\/a>/ig, "$1"]
  577.                       ,[/(<a href=)"\/cdn-cgi\/l\/(email-protection\#.*?">)/ig,'$1"mailto:$2'] ]
  578.      ,"postReplace" : [ [/<a href="javascript:be\(([^\)]+?)\)[^>]*>\?(.*?)<\/a>/ig, "BE:$1-$2"]
  579.                       ,[/<img src="(?:https?:)?(\/\/img\.[25]ch\.net\/.+?)">/ig, "sssp:$1"]
  580.                       ,[/<img src="(?:https?:)?(.*?)">/ig, IMG_TO+"$1"] ]
  581.     ,"ln_terminate" : true
  582.    }
  583.  ]
  584. ,urlReplace  :
  585.     function(url) {
  586.         url = url.replace(/^http:\/\/(?:mattari\.plusvip\.jp|jane\.s28\.xrea\.com)\/test\/\w+\.cgi\/(?:jane2ch|bbs)\//, "http://jane2ch.net/test/read.cgi/community/")
  587.                  .replace(/^(http:\/\/next2ch\.net\/)(\w+\/)(?:kako\/\d{3,4}\/(?:\d{5}\/)?)?(\d+)(?:\/|\.html)?$/,"$1test/read.cgi/$2$3/")
  588.                  .replace(/\/read\.cgi\?(?=.*?bbs=(\w+))(?=.*?key=(\d+))(?=.*?st=(\d+))?(?=.*?to=(\d+))?.*$/i, "/read.cgi/$1/$2/$3-$4").replace(/\/-$/,"/")
  589.                  .replace(/^(.*?)itest(\.[25]ch\.net)\/(\w+)(?=\/test\/read\.cgi\/)/,"$1$3$2");
  590.         if(!args.keep_kako)
  591.             url=url.replace(/^(.*?\/)(\w+\/)kako\/\d{3,4}\/(?:\d{5}\/)?(\d+)\.html$/,"$1test/read.cgi/$2$3/");
  592.         return url;
  593.     }
  594.  ,preReplace  :
  595.     function() {
  596.         var i,list;
  597.         if(this.re_sel==-1) this.testTBlock(); //re_selの設定
  598.         if(this.re_sel>=0) {
  599.             trace("");
  600.             list = this.REG_PATTERN[this.re_sel].preReplace;
  601.             for(i=0;i<list.length; i++) {
  602.                 this.msg = this.msg.replace(list[i][0],list[i][1]);
  603.             }
  604.             trace("pre Replace");
  605.         }
  606.     }
  607.  ,postReplace :
  608.     function() {
  609.         var i,list;
  610.         list = this.REG_PATTERN[this.re_sel].postReplace;
  611.         for(i=0;i<list.length; i++) {
  612.             this.msg = this.msg.replace(list[i][0],list[i][1]);
  613.         }
  614.     }
  615.  ,_getKakoUrl :
  616.     function() {
  617.         var p = URL.prm;
  618.         if(URL.thrID<1000000000)
  619.             { url="http://$1/$2/kako/$3/$4.dat.gz".expand([,p[1],p[5],p[6].substr(0,3),p[6]]);}
  620.         else
  621.             {url="http://$1/$2/kako/$3/$4/$5.dat.gz".expand([,p[1],p[5],p[6].substr(0,4),p[6].substr(0,5),p[6]]);}
  622.         return url;
  623.     }
  624.  ,readHTTP  :
  625.     function(url) {
  626.         var s,ss,u,msgLength;
  627.         if(!URL.otherURLType) {
  628.             if(!URL.kako && !URL.RE_HOST.test(url)) {
  629.                 if(!this.readHTTP0(url)) return false; //dat取得
  630.             }
  631.             /*@if (@EnabledTest2chJSON)
  632.                 o("1st try json");
  633.                 this.msg = get2chJSON();
  634.                 if(this.msg=="") Err("E_already_there");
  635.                 if(this.msg) return false;
  636.                 o("2nd try html");
  637.             @end @*/
  638.             this.kakoUrl = this._getKakoUrl();
  639.             // keep_kakoオプションによるkako形式のURIが受け渡される場合には最初に .dat.gz/.dat のアクセスを試行する
  640.             if(URL.kako) if(!this.readHTTP2(this.kakoUrl)) return false;
  641.         }
  642.         trace("try URL="+url,9);
  643.         this.msg = readHTTP(url)[0];
  644.         this.read_cgi_ver = (this.msg.match(/>read\.cgi ver ([\d.]+)/)||[])[1];
  645.         this.preReplace();
  646.         this.getTitle(this.msg);
  647.         msgLength = this.msg.length;
  648.         ss = this.setTBlock();
  649.         if (ss==null && !URL.otherURLType) {
  650.             u = this.kakoUrl.replace(/\.dat\.gz$/,".html");
  651.             switch(this.readHTTP3(u)) {
  652.                 case 0: return false;
  653.                 case 1: url=u;  ss=this.setTBlock(); break;
  654.                 default: break // case -1
  655.             }
  656.         }
  657.         if(ss==null) {
  658.             ss = "<URL>="+url+"\n<TITLE>="+this.title+"\n<文字数>="+msgLength+"\n\n";
  659.             ss += ((this.msg.match(/^((?=.*LoadAverage|人大杉).*$)/im)||[])[1])
  660.                     || ((this.msg.match(/<h1[^>]*>([\s\S]*?)<\/h1>/m)||[])[1])
  661.                     || ((this.msg.match(/<p[^>]*>\n*(.*\s+.*\s*)$/m)||[])[1])
  662.                     || this.msg.substr(0,(msgLength>100)?100:msgLength-1).replace(/<br.*?>/g,"\n");
  663.             Err("E_block_not_found", "\nもしかしたら以下のはメッセージかも\n\n"+ss+"\n");
  664.         }
  665.         if (!ss) {
  666.             if (shell.Popup("途中でデータが途切れているようですが続行しますか?",0,MY_NAME,4+48)!=6) Err("E_stop");
  667.         }
  668.         if(this.REG_PATTERN[this.re_sel].ln_terminate && this.msg && this.msg.slice(-1)!='\n') this.msg += '\n';
  669.         return true;
  670.     }
  671.  ,readHTTP2 :
  672.     function(url) {
  673.         var re1 = /^(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>((?:(?!<>).)*).*\s*$/m;
  674.         var re2 = /^(?:[^,]*),(?:[^,]*),(?:[^,]*),(?:[^,]*),(.*)$/m;
  675.         trace("try URL="+url,9);
  676.         var s="",r="";
  677.         try { s=readHTTP(url); r=s[0].match(re1); if(!r) r=s[0].match(re2); }catch(e){} //9999999999.dat.gz
  678.         if(!r) {
  679.             url=url.replace(/\.gz$/,"");
  680.             trace("try URL="+url,9);
  681.             try{ s=readHTTP(url); r=s[0].match(re1); if(!r) r=s[0].match(re2); }catch(e){} //9999999999.dat
  682.         }
  683.         if(r) {
  684.             this.msg = s[0];
  685.             this.bin = s[2];
  686.             this.last = this.checkData0(s[0]).replace(/^.*$/m,"").length;
  687.             this.title = r[1];
  688.             URL.kako = true;
  689.             delete args.append;
  690.             return false;
  691.         }
  692.         return true;
  693.     }
  694.  ,readHTTP3 :
  695.     function(url) {
  696.         var title=((this.msg.match(/<title>(.*)<\/title>/im)||[])[1]||"");
  697.         trace("HTTP title="+title,9);
  698.         if (/error|隊長|過去ログ倉庫/i.test(title)) {
  699.             if(!URL.kako && !this.readHTTP2(this.kakoUrl)) return 0; //最初に/kako/.../9999999999.dat.gzを試す
  700.             trace("try URL="+url,9);
  701.             var s = ""; try{ s=readHTTP(url)[0]; }catch(e){}  //9999999999.html
  702.             if(/<dl.*>/.test(s)) { // hit
  703.                 this.title = (s.match(/<title>(.*)<\/title>/im)||[])[1]||"";
  704.                 this.msg = s;
  705.                 this.preReplace();
  706.                 URL.kako = true;
  707.                 delete args.append;
  708.                 return 1;
  709.             }
  710.             trace("HTTP title="+((s.match(/<title>(.*)<\/title>/im)||[])[1]||""),9);
  711.         }
  712.         return -1;
  713.     }
  714.  ,readHTTP0 :
  715.     function(url) {
  716.         var re = /^(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>((?:(?!<>).)*).*\s*$/m;
  717.         var s;
  718.         url = "http://"+URL.site+"/"+URL.brdID+"/dat/"+URL.thrID+".dat";
  719.         trace("try URL="+url,9);
  720.         try { s=readHTTP(url); } catch(e) {}
  721.         if(s) {
  722.             this.msg = s[0];
  723.             this.title = (s[0].match(re)||[])[1];
  724.             if(this.title===void 0||/[25]ちゃんねる専用ブラウザをご利用の皆さまへ/.test(this.title)) return true;
  725.             this.bin = s[2];
  726.             this.last = this.checkData0(s[0]).replace(/^.*$/m,"").length;
  727.             delete args.append;
  728.             return false;
  729.         }
  730.         return true;
  731.     }
  732. });
  733.  
  734. var Thread_jbbs = object(Thread,{
  735.   prepare     : function() { Jane=Jane_jbbs; URL=URL_jbbs; }
  736.  ,REG_PATTERN : [
  737.     { "thr" : /^[\s\S]*?<dl\s+(?:class|id)="thread[^>]*?>\s*(<dt>(?:(?!<\/dl>)[\s\S])*?\s*)<\/dl>[\s\S]*$/i
  738.      ,"msg" : /<DT><A.*?>([0-9]+).+?(?:<A HREF="mailto:(.+?)">)?<B> ?(?:<font color.*?>)?(.*?)(?:<\/font>)? ?<\/B>(.*?)(?:<\/A>|<\/FONT>)(?: 投稿日)?: ?(.*?)(?:<BR>)?<DD>(.*) ?<BR><BR>/img
  739.      ,"rep" : "$1<>$3$4<>$2<>$5<>$6<>"
  740.     }
  741.    ,{ "thr" : /^[\s\S]*?<dl[^>]*?>(?:(?!<dt>)[\s\S])*(<dt>(?:(?!<\/dl>)[\s\S])*)<\/dl>[\s\S]*$/i
  742.      ,"msg" : /<DT><A.*?>([0-9]+).+?(?:<A HREF="mailto:(.+?)">)?<B> ?(?:<font color.*?>)?(.*?)(?:<\/font>)? ?<\/B>(.*?)(?:<\/A>|<\/FONT>)(?: 投稿日)?: ?(.*?)(?:<BR>)?<DD>(.*) ?<BR><BR>/img
  743.      ,"rep" : "$1<>$3$4<>$2<>$5<>$6<>"
  744.     }
  745.   ]
  746.  ,urlReplace  : function(u) {
  747.                     //したらばの事前URL変換:旧site名、/storage/ URLを現在のURLに置換
  748.                     return  u.replace(URL.RE_HOST,"http://jbbs.shitaraba.net/")
  749.                              .replace(/\/bbs\/lite\//,"/bbs/")
  750.                              .replace(/\/\w+\.cgi\//, "/read.cgi/")
  751.                              .replace(/^(http:\/\/[^\/]+\/)([^\/]+\/)([^\/]+\/)storage\/(\d+).*$/,"$1bbs/read.cgi/$2$3$4/")
  752.                              .replace(/\/([^\/]+)\/bbs\/read\.cgi\?(?=.*?bbs=(\d+))(?=.*?key=(\d+))(?=.*?start=(\d+))?(?=.*?end=(\d+))?.*$/i, "/bbs/read.cgi/$1/$2/$3/$4-$5").replace(/\/-$/,"/");
  753.                 }
  754.  ,preReplace :  function() {
  755.                     if(URL.otherURLType==1) { // archive.org
  756.                         this.msg = this.msg.replace(/<a href="\/web\/\d+\//g,'<a href="');
  757.                     }
  758.                     if(URL.otherURLType && /<dl.*?>\s+<dt>\s+<a/.test(this.msg)) {
  759.                         //1と100レス毎に入る公告を除去
  760.                         this.msg = this.msg.replace(/^\s*(.*?)\s*\n/mg,"$1")
  761.                                            .replace(/<!--.*?-->/g,"")
  762.                                            .replace(/<br>(?:<\/dd>)?<div[^>]*>.*?<\/div>(?=<dt>|<\/dl>)/ig,"")
  763.                                            .replace(/<table.*?>(?:(?!<dt>).)*?<\/table>(?=<dt>|<\/dl>)/ig,"")
  764.                                            .replace(/<br>\s*<br>(?:<\/dd>)?(?=<dt>|<\/dl>)/ig,"<br><br>\n")
  765.                                            ;
  766.                     }
  767.                 }
  768.  ,mainReplace :
  769.     function() {
  770.         this.msg = this.msg.replace(/^(\d+<>.*?<>.*?<>.*?)(<>.*<>)((?:(?!<>).)*)$/mg, function($0,$1,$2,$3) {return ($3)?$1+" ID:"+$3+$2:$0;});
  771.     }
  772.  ,createBrdFolder :
  773.     function() {
  774.         var s = readHTTP( URL.url.replace( /\/\w+\.cgi(\/\w+\/\w+\/).*$/, "/api/setting.cgi$1") )[0];
  775.         var bbs_title = (s.match(/^BBS_TITLE=(.*)$/m)||[])[1];
  776.         var category = (s.match(/^CATEGORY=(.*)$/m)||[])[1];
  777.         bbs_title || category || Err("E_brditem_not_found");
  778.         brd = [ "【したらば】" + category.convDontUseStr() , bbs_title.convDontUseStr() , ""].join("\\");
  779.         path(Jane.LogBasePath + JANE_LOGS + brd + URL.thrID + ".dat");
  780.         return brd;
  781.     }
  782.  ,checkData   :
  783.     function(s) {
  784.         if (this.last-this.first+1 != s.replace(/^.*$/mg,"").length) {
  785.             this.requireFillAbn = true;
  786.         }
  787.         // 末尾のIDを削除
  788.         this.msg = s.replace(/^(.*?)<>((?!<>).)*$/mg, "$1");
  789.     }
  790.  ,getTitle    : function(s) {
  791.                     this.title = (s.match(/<>((?:(?!<>).)*)<>(?:(?!<>).)*$/m)||[])[1];
  792.                     if(!this.title) this.title=DatInfo.title;
  793.                 }
  794.  ,addTitle    : function() {}
  795.  ,readHTTP  :
  796.     function(url) {
  797.         // web.archive/google cacheからの取得
  798.         if(URL.otherURLType) {
  799.             this.getTitle = this.getTitle2;
  800.             this.delDupData = this.delDupData2
  801.             this.checkData = this.checkData2;
  802.             Thread.readHTTP.call(this, url);
  803.             delete this.mainReplace;
  804.             delete this.addTitle;
  805.             return true;
  806.         }
  807.         //-- 1st try : rawmode read ----
  808.         var raw_url = url.replace(/\/\w+\.cgi\//, "/rawmode.cgi/");
  809.         trace("try URL="+raw_url,9);
  810.         var s = readHTTP(raw_url);
  811.         switch( errmsg = (s[1].match(/^ERROR: *(?! )(.*?)$/m)||[])[1]||"" ) {
  812.             case "BBS NOT FOUND": Err("E_bbs_not_found");
  813.             case "KEY NOT FOUND": Err("E_key_not_found");
  814.             case "THREAD NOT FOUND": Err("E_thread_not_found");
  815.             case "STORAGE IN":
  816.                 this.getTitle = this.getTitle2;
  817.                 this.delDupData = this.delDupData2
  818.                 this.checkData = this.checkData2;
  819.                 Thread.readHTTP.call(this, URL.prm[0]);
  820.                 delete this.mainReplace;
  821.                 delete this.addTitle;
  822.                 break;
  823.             case "": //正常取得
  824.                 s = s[0];
  825.                 this.getTitle(s);
  826.                 IDX_STATE["jbbs"] = 0;
  827.                 this.msg = s;
  828.                 break;
  829.             default: Err("E_http_recieve", "(rawmode) : " + errmsg);
  830.         }
  831.         return true;
  832.     }
  833.  ,getTitle2   :
  834.     function(s) { this.title = (s.match(/<h1 class="thread-title">(.*?)<\/h1>/i)||[])[1]
  835.                   || (s.match(/<title>(.*?)<\/title>/i)||[])[1] || "";
  836.                 }
  837.  ,delDupData2 : //append時、既存のレス数分データを読み飛ばす
  838.     function() { while( (this.msg.match(/^(\d+)/i)||[])[1] < DatInfo.cnt+1)
  839.                     { this.msg = this.msg.replace(/^.*\n/m,""); }
  840.                }
  841.  ,checkData2   :
  842.     function(s) { var ln = this.checkData0(s);
  843.                   if (this.last-this.first+1 != ln.length) { this.requireFillAbn = true; }
  844.                 }
  845. });
  846.  
  847. var Thread_machi = object(Thread,{
  848.   prepare     : function() { Jane=Jane_machi; URL=URL_machi; }
  849.  ,noChkTitle  : true
  850.  ,REG_PATTERN : [
  851.     { "thr" : /^(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>((?:(?!<>).)*)<>((?:(?!<>).)*).*\s*$/m
  852.      ,"msg" : /^((?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*<>(?:(?!<>).)*)(<>(?:(?!<>).)*<>(?:(?!<>).)*)<>((?:(?!<>).)*)(?:<>)?(.*\s*)$/mg
  853.      ,"rep" : "$1 [ $3 ]$2$4"
  854.     }
  855.   ]
  856.  ,urlReplace  : function(u) {
  857.                     if(URL.otherURLType) Err("E_illegal_url");
  858.                     u=u.replace(/\/test\/(?=read)/,"/bbs/")
  859.                        .replace(/\/read\.cgi\//,"/offlaw.cgi/2/");
  860.                     trace("replace url="+u,9);
  861.                     return u;
  862.                 }
  863.  ,getTitle    : function(s) { this.title = (s.match(this.REG_PATTERN[this.re_sel].thr)||[])[1] || DatInfo.title; }
  864.  ,checkData   : function(s) {
  865.                     if(!/^\d+<>.*?<>.+?<>.*?<>.*$/m.test(s)) Err("E_remained_garbage");
  866.                     if (this.last-this.first+1 != s.replace(/^.*$/mg,"").length) { this.requireFillAbn = true; }
  867.                 }
  868.  ,delDupData  : function() {}
  869.  ,addTitle    : function() {}
  870.  ,setTBlock   : function() {}
  871. });
  872.  
  873. //==============================================================
  874. function loadFile(f) {
  875.     var s="";
  876.     try {
  877.         strm.Type = 2;
  878.         strm.Charset = 'shift_jis';
  879.         strm.Open();
  880.         strm.loadFromFile(f);
  881.         s = strm.ReadText(-1);
  882.         strm.Close();
  883.     } catch(e) { strm.Close(); Err("E_loadfile", "\n"+e.message+"\ncurrent="+shell.CurrentDirectory+"\nfile="+f); }
  884.     return s;
  885. }
  886. function loadFile2(f) {
  887.     var s="";
  888.     try {
  889.         var file = fso.OpenTextFile( f, 1, false, 0);
  890.         s = file.ReadAll();
  891.         file.close();
  892.     } catch(e) {
  893.         if(file) file.close();
  894.         s = loadFile(f);
  895.     }
  896.     return s;
  897. }
  898.  
  899. function saveFile(f,s,add) {
  900.     strm.Type = 2;
  901.     strm.Charset = 'shift_jis';
  902.     strm.Open();
  903.     if (add) { try {strm.loadFromFile(f); strm.Position = strm.Size;} catch(e){} }
  904.     strm.WriteText(s);
  905.     try { strm.Savetofile(f, 2); }
  906.     catch(e) { strm.Close(); Err("E_savefile", "\n"+e.message+"\ncurrent="+shell.CurrentDirectory+"\nfile="+f); }
  907.     strm.Close();
  908. }
  909. function saveFile2(f,s,add) {
  910.     try{
  911.         var file = fso.OpenTextFile( f, (add)?8:2 , true, 0);
  912.         file.Write(s);
  913.         file.Close();
  914.     } catch(e) {
  915.         if(file) file.Close();
  916.         saveFile(f,s,add);
  917.     }
  918. }
  919.  
  920. function saveBinaryFile(f,b) {
  921.     strm.Type = 1;
  922.     strm.Open();
  923.     strm.Write(b);
  924.     try { strm.Savetofile(f, 2); }
  925.     catch(e) { strm.Close(); Err("E_savefile", "\n"+e.message+"\ncurrent="+shell.CurrentDirectory+"\nfile="+f+" (binary)"); }
  926.     strm.Close();
  927. }
  928.  
  929. function readHTTP(u) {
  930.     var s="", r="", b;
  931.     if(args.proxy) {
  932.         if(USED_WINHTTP) { http.setProxy(2, args.proxy, ""); }
  933.         else Err("E_not_suported_proxy");
  934.     }
  935.    
  936.     http.open("GET", u, true);
  937.     for(var i in HTTP_HEADERS) { http.setRequestHeader(i, HTTP_HEADERS[i]); }
  938.     http.send();
  939.     var t0=new Date();
  940.     while( (USED_WINHTTP) ? !http.waitForResponse(0) : http.readyState<4 ) {
  941.         if (new Date() - t0 > HTTP_TIMEOUT) { http.abort(); Err("E_http_timeout"); }
  942.         WScript.Sleep(50);
  943.     }
  944. //  if (http.status != 200 && !http.status==0) Err("E_http_recieve", " status="+http.status);
  945.     var header = http.getResponseHeader("Content-Type");
  946.     b = http.responseBody;
  947.     r = http.getAllResponseHeaders();
  948.     try {s = http.responseText;} catch(e) {}
  949.     if (!/charset=/i.test(header)) {
  950.         if(b !== void 0) {
  951.             var cs = (s.match(/<meta[^>]*charset=([^>;"'\s]+)/i)||[])[1] || '_autodetect';
  952.             if(cs=="x-sjis") cs="shift-jis";
  953.             if(basp) {
  954.                 trace("文字コード変換開始 with basp21",9);
  955.                 s = basp.Kconv(b,4,cs=="shift-jis"?1:0);
  956.             } else {
  957.                 trace("文字コード変換開始",9);
  958.                 strm.Type = 1;
  959.                 strm.Open();
  960.                 strm.Write(b);
  961.                 strm.Position = 0;
  962.                 strm.Type = 2;
  963.                 strm.Charset = cs;
  964.                 s = strm.ReadText(-1);
  965.                 strm.Close();
  966.             }
  967.         }
  968.     }
  969.     http.abort();
  970.     if(!s) s="";
  971.     return [s,r,b];
  972. }
  973.  
  974. function saveDat(dat,s,b,add,latest) {
  975.     var cnt;
  976.     if (args.append) {
  977.         if(s=="") Err("E_already_there");
  978.     } else {
  979.         if(s.match(/\n/g)==null) Err("E_no_data");
  980.         cnt = s.match(/\n/g).length+DatInfo.offset;
  981.         if(!args.overwrite) {
  982.             if(DatInfo.cnt>=cnt && (latest==null || latest==true)) {
  983.                 if(REWRITE_DAT==3 || (REWRITE_DAT==2 && DatInfo.cnt==cnt)) Err("E_stop");
  984.                 if (REWRITE_DAT==0
  985.                         && shell.Popup("新規のレスが無いようですが置換を続行しますか? ("
  986.                                         +DatInfo.cnt+" -->"+cnt+")",0,MY_NAME,4+32+256) != 6){
  987.                     Err("E_stop");
  988.                 }
  989.             }
  990.         }
  991.     }
  992.     if(args.bak) DatInfo.copyDat();
  993. //  if((!args.append&&add==0)||args.overwrite) {
  994. //      saveFile(path(dat),""); trace("create file:"+dat,9); //既存のdatを削除
  995. //  }
  996.     if(b===null) { trace(); saveFile2(path(dat), s, args.append||(!args.overwrite&&add) ); trace("save file:"+dat); }
  997.     else         { trace(); saveBinaryFile(path(dat), b); trace("save binary file:"+dat); }
  998. }
  999.  
  1000. function invokeJaneStyle(url) {
  1001.     shell.Exec(JANE_EXE + " " + url);
  1002.     if(args.sendkey && args.sendkey > 0) {
  1003.         WScript.Sleep(args.sendkey);
  1004.         shell.SendKeys('^(w)');
  1005.         WScript.Sleep(200);
  1006.         shell.Exec(JANE_EXE + " " + url);
  1007.     }
  1008.     if(args.redraw) {
  1009.         WScript.Sleep(args.redraw);
  1010.         shell.SendKeys('%');
  1011.         WScript.Sleep(20);
  1012.         shell.SendKeys('T');
  1013.         WScript.Sleep(10);
  1014.         shell.SendKeys('G');
  1015.         WScript.Sleep(10);
  1016.         shell.SendKeys('0');
  1017.     }
  1018. }
  1019.  
  1020. function terminateJaneExe() {
  1021.     var wmi; try { wmi = new WMI(); } catch(e) { wmi=null; }
  1022.     shell.Exec(JANE_EXE);
  1023.     WScript.Sleep(200);
  1024.     shell.SendKeys("%({F4})");
  1025.     if(!wmi) { WScript.Sleep(4000);}
  1026.     else {
  1027.         do {
  1028.             var cnt=40;
  1029.             while(cnt && wmi.isAliveProcess(JANE_EXE, JANE_PATH+JANE_EXE)) { WScript.Sleep(100); cnt--;}
  1030.             if(!cnt) { WScript.Echo(JANE_EXE+"を終了させてください"); }
  1031.         } while(!cnt);
  1032.     }
  1033. }
  1034.  
  1035. function saveIdx(boardUrl,force) {
  1036.     var i,s,r,lines=Math.max(DatInfo.cnt,DatInfo.lines);
  1037.     var use_db = parseInt(Jane.getINI([,"OPTIONS","QuickMerge",""]),10);
  1038.     if(chkJaneProg().indexOf('Xeno')>=0) { use_db = parseInt(Jane.getINI([,"OJVIEW","QuickMerge",""]),10); }
  1039.     var basedir = shell.CurrentDirectory;
  1040.     var dir = fso.GetParentFolderName(fso.GetFile(DatInfo.dat).Path);
  1041.     var datName = DatInfo.dat.replace(/^.*\\([^\\]+)\.dat$/,"$1");
  1042.    
  1043.     var maxRes = getBrdCustomizeIni(boardUrl) || 1000;
  1044.     var state = (lines>=maxRes) ? 0 : IDX_STATE[ (URL===URL_jbbs) ? "jbbs" : "2ch" ];
  1045.     if (URL.kako) state = 10;
  1046.    
  1047.     var last_got = Number(new Date(fso.GetFile(DatInfo.dat).DateLastModified)/1000+9*60*60);
  1048.     var idx = [ datName,DatInfo.title,'',lines,'0','',boardUrl,state,lines,'','','0',last_got,'0','0' ];
  1049.     var idxText = [];
  1050.     for(i=0; i<idx.length; i++) { idxText[i] = "'" + idx[i] + "'"; }
  1051.     var SQL_CMD_1 = "CREATE TABLE IF NOT EXISTS tableversion (tablename PRIMARY KEY, version);"
  1052.                     + 'INSERT OR IGNORE INTO "tableversion" VALUES(' + "'board_db','00000001');"
  1053.                     + 'INSERT OR IGNORE INTO "tableversion" VALUES(' + "'idxlist','00000002');"
  1054.                     + "CREATE TABLE IF NOT EXISTS idxlist ("
  1055.                     + "datname TEXT PRIMARY KEY, title TEXT, last_modified TEXT, lines TEXT, view_pos TEXT, idx_mark TEXT,"
  1056.                     + " uri TEXT, state TEXT,new_lines TEXT, write_name TEXT, write_mail TEXT,"
  1057.                     + " last_wrote TEXT, last_got TEXT, read_pos TEXT, record_be TEXT,"
  1058.                     + " etc1 TEXT, etc2 TEXT);";
  1059.     var SQL_CMD_2 = 'INSERT OR IGNORE INTO idxlist VALUES(' + idxText.join(",") + ",NULL,NULL);";
  1060.     var SQL_CMD_3 = "update idxlist set new_lines=new_lines+$2-lines where datname='$1';".expand([,datName,lines]);
  1061.     var SQL_CMD_4 = "update idxlist set lines=" + idxText[3] + ",uri=" + idxText[6] + ",title=" + idxText[1]
  1062.                      + ",state=" + idxText[7] + ",last_got=" + idxText[12] + " where datname=" + idxText[0] + ";";
  1063.  
  1064.     if (use_db && args.restart) {
  1065.         var sql = new Sql();
  1066.         if(chkJaneProg().substr(0,4)=='Xeno' && !sql.isSqlite_ODBC) Err("E_require_sqlODBC");
  1067.         sql.isSqlite_ODBC || sql.isSqlite_exe || Err("E_require_sqlite");
  1068.     }
  1069.     if(args.restart) terminateJaneExe();
  1070.    
  1071.     if (use_db && args.restart) {
  1072.     switch(chkJaneProg()) {
  1073.         case 'Style' :
  1074.         case 'Style?':
  1075.             shell.CurrentDirectory = dir;
  1076.             r=sql.exec("BoardDB.db",SQL_CMD_1+SQL_CMD_2+SQL_CMD_3+SQL_CMD_4);
  1077.             break;
  1078.         case 'Xeno':
  1079.         case 'Xeno?':
  1080.             var qmerge3 = Jane.LogBasePath+JANE_LOGS+"qmerge3.db";
  1081.             if(!fso.FileExists(qmerge3)) break;
  1082.             idx.splice(14,1,last_got);
  1083.             idxText = [];
  1084.             for(i=0; i<idx.length; i++) { idxText[i] = "'" + idx[i] + "'"; }
  1085.             var brd=DatInfo.brd.slice(0,-1);
  1086.             var SQL_CMD_XENO1 = "select board_id,max(board_id) from board where board_name='"+brd+"';";
  1087.             var SQL_CMD_XENO2 = 'insert into board VALUES((select max(board_id)+1 from board),'+"'"+brd+"'"+');';
  1088.             var SQL_CMD_XENO3 = "select board_id from thread where board_id=$1 and datname='"+datName+"';";
  1089.             var SQL_CMD_XENO4 = "insert into thread values($1,"+idxText.join(",")+");";
  1090.             var SQL_CMD_XENO5 = "update thread set new_lines=new_lines+$2-lines where datname='$1';".expand([,datName,lines]);
  1091.             var SQL_CMD_XENO6 = "update thread set lines=" + idxText[3] + ",uri=" + idxText[6] + ",title=" + idxText[1]
  1092.                                 + ",state=" + idxText[7] + ",last_got=" + idxText[12] + " where datname=" + idxText[0] + ";";
  1093.             var board_id = sql.select(qmerge3,SQL_CMD_XENO1);
  1094.             board_id = (board_id[0].match(/^(\d+)/)||[])[1];
  1095.             if (!board_id) { sql.exec(qmerge3, SQL_CMD_XENO2); board_id=((sql.select(qmerge3,SQL_CMD_XENO1))[0].match(/^(\d+)/)||[])[1]; }
  1096.             if(!((sql.select(qmerge3,SQL_CMD_XENO3.expand([,board_id])))[0].match(/^\d+/))) {
  1097.                 sql.exec(qmerge3, SQL_CMD_XENO4.expand([,board_id]));
  1098.             } else {
  1099.                 sql.exec(qmerge3, SQL_CMD_XENO5);
  1100.                 sql.exec(qmerge3, SQL_CMD_XENO6);
  1101.             }
  1102.             default: break;
  1103.         }
  1104.         sql.finalize();
  1105.     }
  1106.    
  1107.     shell.CurrentDirectory = dir;
  1108.     // idxファイルの更新
  1109.     var isExistIdx = false;
  1110.     if(fso.FileExists(datName+".idx")) {
  1111.         s = loadFile(datName+".idx").replace(/\r/g,"").split("\n");
  1112.         if(s.length>=12) {
  1113.             isExistIdx = true;
  1114.             s[7]  = "" + (parseInt(s[7],10) + lines - parseInt(s[2],10));
  1115.             s[2]  = "" + idx[3];
  1116.             s[5]  = idx[6];
  1117.             s[6]  = "" + idx[7];
  1118.             s[11] = "" + last_got;
  1119.             s[0]  = idx[1];
  1120.         }
  1121.     }
  1122.     if(!isExistIdx) {
  1123.         s = idx;
  1124.         s.splice(0,1);
  1125.         s.push("");
  1126.     }
  1127.     s = s.join("\r\n");
  1128.     try { saveFile(datName+".idx", s); } catch(e) {}
  1129.    
  1130.     // subject.txtの更新
  1131.     var re = new RegExp("^"+datName+"\\..*\\((\\d+)\\)(\\s*)$", "m");
  1132.     s="";
  1133.     r=0;
  1134.     if(fso.FileExists("subject.txt")) try{s=loadFile("subject.txt"); r=1;} catch(e){r=-1;}
  1135.     if(force) {
  1136.         s = s.replace(re,"");
  1137.         s = datName+".dat<>"+DatInfo.title+"("+lines+")\r\n" + s;
  1138.         try {saveSubjectTxt("subject.txt", s, force);}
  1139.         catch(e) {trace("save error (subject.txt): "+e.massage,9);}
  1140.     } else {
  1141.         r = s.match(re);
  1142.         if(r) {
  1143.             s = s.replace(re, r[0].replace(/\((\d+)\)(\s*)$/,"("+lines+")$2"));
  1144.             try { saveFile("subject.txt",s); } catch(e) {}
  1145.         }
  1146.     }
  1147.     shell.CurrentDirectory = basedir;
  1148. }
  1149.  
  1150. function saveSubjectTxt(f,s,force) {
  1151.     if(force) try { if(fso.FileExists(f)) fso.GetFile(f).Attributes &= ~1 } catch(e){};
  1152.     saveFile(path(f),s);
  1153.     if(force) try { fso.GetFile(f).Attributes |= 1; } catch(e){} //read only属性
  1154. }
  1155.  
  1156. function Sql(){
  1157.     var DRIVER = "DRIVER=SQLite3 ODBC Driver;";
  1158.     var db = new ActiveXObject("ADODB.Connection");
  1159.     this.isSqlite_exe  = fso.FileExists(SCRIPT_PATH + "sqlite3.exe");
  1160.     this.isSqlite_ODBC = (function(){var s=true; try {db.Open(DRIVER);db.Close();} catch(e){s=false;}; return s;})();
  1161.     this.finalize = function() { db = null; }
  1162.     var funcTables  = [ "exec", "select" ];
  1163.     var types = (this.isSqlite_ODBC) ? "ODBC" : "exe";
  1164.     for(var i=0; i<funcTables.length; i++) { eval( "this."+ funcTables[i] + "=" + funcTables[i] + "_" + types + ";"); }
  1165.     function exec_ODBC(dbFile,sql) {
  1166.         try {
  1167.             var s="";
  1168.             db.Open(DRIVER+"Database="+dbFile);
  1169.             db.Execute(sql);
  1170.         } catch(e) { s = e.message; }
  1171.         try { db.Close(); } catch(e) { s += " | Connection Close:"+e.message; }
  1172.         return [,s];
  1173.     }
  1174.     function select_ODBC(dbFile,sql) {
  1175.         var r="",s="";
  1176.         var rs = new ActiveXObject("ADODB.Recordset");
  1177.         try {
  1178.             db.Open(DRIVER+"Database="+dbFile);
  1179.             rs.Open(sql,db);
  1180.             while(!rs.Eof) {
  1181.                 var rr=[];
  1182.                 for(var i=0; i<rs.Fields.count; i++) { rr.push(rs(i).Value); }
  1183.                 r += rr.join("|")+"\n";
  1184.                 rs.MoveNext();
  1185.             }
  1186.         } catch(e) { s = e.message; }
  1187.         try { rs.Close(); } catch(e) { s += " | Recordset Close:"+e.message; }
  1188.         try { db.Close(); } catch(e) { s += " | Connection Close:"+e.message; }
  1189.         rs = null;
  1190.         return [r, s];
  1191.     }
  1192.     function exec_exe(dbFile,sql) { return select_exe(dbFile, sql); }
  1193.     function select_exe(dbFile, sql) {
  1194.         var sqlite = shell.Exec(SCRIPT_PATH + "sqlite3.exe");
  1195.         sqlite.StdIn.WriteLine(".open '"+ dbFile+ "'");
  1196.         sqlite.StdIn.WriteLine(sql);
  1197.         sqlite.StdIn.WriteLine(".quit");
  1198.         return [ sqlite.StdOut.ReadAll(), sqlite.StdErr.ReadAll() ];
  1199.     }
  1200. }
  1201.  
  1202. function getBrdCustomizeIni(brdurl) {
  1203.     if(!fso.FileExists(JANE_BRDCUSTOMIZE_INI)) return null;
  1204.     var s=loadFile(JANE_BRDCUSTOMIZE_INI),r,u;
  1205.     brdurl=brdurl.replace(/^http:\/\//,"").replace(/\./g,"\\.");
  1206.     do {
  1207.         if (r = s.match(new RegExp("^\\[$1/?\\]\\s+MaxResNum=(\\d+)".expand([,"http://"+brdurl]),"im"))) break;
  1208.         u = brdurl.split(/\/(?=[^\/]+$)/);
  1209.         brdurl =u[0];
  1210.     } while(u[1])
  1211.     return r&&r[1];
  1212. }
  1213.  
  1214. function chkJaneProg() {
  1215.     var i,n1=0,n2=0, description,productname;
  1216.     var folder=sha.Namespace(JANE_PATH);
  1217.     var items=folder.Items();
  1218.     for(i=0; i<items.Count-1;i++) {
  1219.         if(folder.GetDetailsOf(items, i) == '説明') { n1=i; }
  1220.         if(folder.GetDetailsOf(items, i) == '製品名') { n2=i; }
  1221.     }
  1222.     if(n1>0) description = folder.GetDetailsOf(folder.ParseName("Jane2ch.exe"),n1);
  1223.     if(n2>0) productname = folder.GetDetailsOf(folder.ParseName("Jane2ch.exe"),n2);
  1224.     if (description=="JaneXeno") return "Xeno";
  1225.     if (description=="JaneView") return "View";
  1226.     if (productname=="Jane Style") return "Style";
  1227.     var fileversion = parseFloat(fso.GetFileVersion(JANE_PATH+JANE_EXE));
  1228.     if (fileversion>3.0) return "Style?";
  1229.     if (fileversion<1.0) return "Xeno?";
  1230.     return "Unknown";
  1231. }
  1232.  
  1233. function invokeBrowser(url) {
  1234.     var browser = replaceEnv(args.browser||BROWSER_PATH);
  1235.     shell.Run((browser ? '"'+browser+'" ':"")+url);
  1236.  }
  1237.  
  1238. //==============================================================
  1239. function path(p) {
  1240.     var d = p.match(/[^\\]+\\/g)||[], e = "";
  1241.     for (var i = 0; d[i]; i++) fso.FolderExists(e += d[i].replace(/[. ]+\\/,"\\")) || fso.CreateFolder(e);
  1242.     return p;
  1243. }
  1244.  
  1245. function parmParse(args) {
  1246.     var ptn = [ "^(?:/n:)?(\\d+)$", "^/f$", "^\\+?https?://", "^/w:?(-?\\d+)?$", "^/-w$", "^/a(?:ppend)?$"
  1247.                 , "^/b(?:ak)?(1)?$", "^/z$", "^/redraw:?(\\d+)?$", "^/log:?(.*)$", "^/keep_kako$"
  1248.                 , "^/proxy:(.*)$", "^/cfg:?(.*)$", "^/subject$", "^/browser:?(.*)$", "^/pos:(\\d+)$"
  1249.               ];
  1250.     var i,j,n,p = {};
  1251.     var require_input_url = false;
  1252.     for (i=0; i<args.length; i++) {
  1253.         for (j=0,n=-1; j<ptn.length;j++) {if ((new RegExp(ptn[j],"i")).test(args(i))) { n=j; break;} }
  1254.         switch (n) {
  1255.             case 0: p["opt_n"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||"");
  1256.                     break;
  1257.             case 1: require_input_url = true;
  1258.                     break;
  1259.             case 2: p["url"] = args(i);
  1260.                     break;
  1261.             case 3: p["sendkey"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||1200);
  1262.                     break;
  1263.             case 4: p["sendkey"] = -1;
  1264.                     break;
  1265.             case 5: p["append"] = true;
  1266.                     break;
  1267.             case 6: p["bak"] = -1;
  1268.                     if ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]) { p["bak"] = 1; }
  1269.                     break;
  1270.             case 7: p["restart"] = true;
  1271.                     break;
  1272.             case 8: p["redraw"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||1200);
  1273.                     break;
  1274.             case 9: p["trace"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||"");
  1275.                     break;
  1276.             case 10: p["keep_kako"] = true;
  1277.                     break;
  1278.             case 11: p["proxy"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||"");
  1279.                     break;
  1280.             case 12: p["cfg"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||"");
  1281.                     break;
  1282.             case 13: p["subject"] = true;
  1283.                     break;
  1284.             case 14: p["browser"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||"");
  1285.                     break;
  1286.             case 15: p["pos"] = ((args(i).match(new RegExp(ptn[n],"i"))||[])[1]||"");
  1287.                     break;
  1288.             default: break;
  1289.         }
  1290.     }
  1291.    
  1292.     if (require_input_url || !p.url) p.url = (InputBox("URLを指定してください",MY_NAME, p.url)||"");
  1293.     if(p.url.charAt(0)=='+') { p.url = p.url.substr(1); p["overwrite"] =true; }
  1294.     if(p.overwrite) p.append = false;
  1295.     return p;
  1296. }
  1297.  
  1298. //==============================================================
  1299.  
  1300. function WMI() {
  1301.     var locator = new ActiveXObject("WbemScripting.SWbemLocator");
  1302.     var services = locator.ConnectServer(null, "root\\CIMV2");
  1303.     return {
  1304.         isAliveProcess :
  1305.             function (pName, pPath) {
  1306.                 var query = "SELECT * FROM Win32_Process WHERE name='"+pName+"'";
  1307.                 var props = this.wmiProps(query);
  1308.                 for (var i=0, status=false; i<props.length; i++) { if(props[i].ExecutablePath == pPath) {status = true;} }
  1309.                 return status;
  1310.             }
  1311.         ,wmiProps :
  1312.             function (query) {
  1313.                 var result = [];
  1314.                 try {
  1315.                     var set = services.ExecQuery(query);
  1316.                     var e = new Enumerator(set);
  1317.                     while (!e.atEnd()) { result.push(e.item()); e.moveNext(); }
  1318.                 } catch (e) { throw e; }
  1319.                 return result;
  1320.             }
  1321.          ,getProcessID :
  1322.             function (pName, pPath) {
  1323.                 var query = "SELECT * FROM Win32_Process WHERE name='"+pName+"'";
  1324.                 do {
  1325.                     var props = this.wmiProps(query);
  1326.                     for (var i=0,c=0,pid; i<props.length; i++) {
  1327.                         if (props[i].ExecutablePath == pPath) { c++; pid=props[i].ProcessID; }
  1328.                     }
  1329.                 } while (c>1);
  1330.                 return pid;
  1331.             }
  1332.     }
  1333. }
  1334. function Trace() {
  1335.     var logfile = TRACE_LOG;
  1336.     var flag    = true;
  1337.     var first   = true;
  1338.     var t       = [];
  1339.     if(args.trace) logfile = replaceEnv(args.trace);
  1340.     if(!fso.FileExists(logfile)) {
  1341.         var env = new ENV();
  1342.         try{
  1343.             var f = fso.CreateTextFile(logfile);
  1344.             f.WriteLine("["+MY_NAME+"]");
  1345.             f.WriteLine("Jane Type = "+chkJaneProg()+"\t"+fso.GetFileVersion(JANE_PATH+JANE_EXE));
  1346.             f.WriteLine("current path = "+JANE_PATH);
  1347.             f.WriteLine("Logs path = "+Jane.LogBasePath+JANE_LOGS);
  1348.             f.WriteLine("script path = "+SCRIPT_PATH);
  1349.             f.WriteLine("UAC = "+env.UAC);
  1350.             f.WriteLine("64bit? = "+env.PROCESSOR_ARCHITECTURE+"\t"+env.PROCESSOR_ARCHITEW6432);
  1351.             f.WriteLine("----------------------------------------------------------------------");
  1352.             f.Close();
  1353.         } catch(e) {flag=false; if(f) f.Close();}
  1354.         env = null;
  1355.     }
  1356.     return function(s,n) {
  1357.         if(flag) try{
  1358.             if(!n) n=0;
  1359.             var f = fso.OpenTextFile(logfile,8,true,0);
  1360.             if(first) {first=false; f.WriteLine("");}
  1361.             var tt=new Date();
  1362.             if(!s) { t[n] = new Date();}
  1363.             else   { f.WriteLine(formatDate(tt,3)+"\t"+((n in t)?formatNum(tt-t[n],7)+"ms":"         ")+"\t"+s); }
  1364.             f.Close();
  1365.         } catch(e) {if(f) f.Close();}
  1366.     }
  1367. }
  1368. function getEnv(s) { var wse = shell.Environment("Process"); return wse(s); }
  1369. function replaceEnv(s) { return s.replace(/\%([^%]+)\%/ig, function(w0,w1) {return getEnv(w1);}); }
  1370. function formatDate(d,n) {
  1371.     return [d.getFullYear()
  1372.             , "/"  , ("0"+(d.getMonth()+1)).slice(-2)
  1373.             , "/"  , ("0"+d.getDate()).slice(-2)
  1374.             , "("  , "日月火水木金土".substr(d.getDay(),1)
  1375.             , ") " , ("0"+d.getHours()).slice(-2)
  1376.             , ":"  , ("0"+d.getMinutes()).slice(-2)
  1377.             , ":"  , ("0"+d.getSeconds()).slice(-2)
  1378.             , "."  , ("00"+parseInt(d.getMilliseconds(),10)+" ").slice(-4,n-4)
  1379.           ].join("");
  1380. }
  1381. function formatNum(d,n) {
  1382.     return ("        "+d).slice(-n);
  1383. }
  1384. function ENV() {
  1385.      this.UAC = sha.IsRestricted("system", "EnableLUA");
  1386.      this.PROCESSOR_ARCHITECTURE = getEnv("PROCESSOR_ARCHITECTURE");
  1387.      this.PROCESSOR_ARCHITEW6432 = getEnv("PROCESSOR_ARCHITEW6432")
  1388.      return this;
  1389. }
  1390. //==============================================================
  1391.  
  1392. function main() {
  1393.     var u,thr;
  1394.     args.url = URL.entryCheck(args.url);
  1395.     if(u=URL.isOPT_BBS(args.url)) (new OPT_BBS[u][0](u)).proc();
  1396.     else if (URL_jbbs.RE_HOST.test(args.url)) Thread_jbbs.proc();
  1397.     else if (URL_machi.RE_HOST.test(args.url)) Thread_machi.proc();
  1398.     else Thread_2ch.proc();
  1399. }
  1400.  
  1401.  
  1402. //----- オプションファイルの組み込み (gethtmldat.cfg) -----
  1403. cfg = args.cfg || CFG_FILE;
  1404. if(fso.FileExists(replaceEnv(cfg))) {
  1405.     var code = loadFile(cfg);
  1406.     eval(code);
  1407. }
  1408. //---------------------------------------------------------
  1409.  
  1410. function thread_bakusai(itemName) {
  1411.     var URL = object(URL_2ch,{
  1412.       RE_HOST : OPT_BBS[itemName][2]
  1413.      ,RE_URL  : new RegExp("^http:"+OPT_BBS[itemName][2].source+"/thr_\\w+(?=.*/(acode=\\d))(?=.*/(ctgid=\\d+))(?=.*/(bid=\\d+))(?=.*/(?:tid=)?(\\d+))?")
  1414.      ,itemName  : itemName
  1415.      ,set_prm : function(url) {
  1416.                     var r;
  1417.                     url=url.replace(/\/test\/read\.cgi/,"")
  1418.                            .replace(this.RE_HOST, this.itemName);
  1419.                     this.prm  = url.match(this.RE_URL) || Err("E_illegal_url");
  1420.                     r = "^\\t($1\\/thr_\\w+\\/$2\\/$3)\\t".expand([,this.RE_HOST.source.substr(4),this.prm[2],this.prm[3]]);
  1421.                     r = Jane.brd.match(new RegExp(r,"img"));
  1422.                     this.brdItem = this.prm[0] = RegExp.$1;
  1423.                     if(this.prm[5]) {
  1424.                         this.url  = "http://$1/thr_res/$2/$3/$4/tid=$5/".expand(this.prm);
  1425.                         this.url2 = "http://$0/test/read.cgi/$4/$5/".expand(this.prm);
  1426.                         this._setRange(url);
  1427.                     } else {
  1428.                         this.url  = "http://$1/thr_tl/$2/$3/$4/".expand(this.prm);
  1429.                         this.requireThrList = true;
  1430.                     }
  1431.                     this.site     = this.prm[1];
  1432.                     this.category = this.prm[3];
  1433.                     this.brdID    = this.prm[4];
  1434.                     this.thrID    = this.prm[5]||"";
  1435.                 }
  1436.      ,_setRange : function(url) {
  1437.                     var range;
  1438.                     range = url.match(/\/(rrid=(\d+)(?:-(\d+)|\/|$))/)||[];
  1439.                     this.range = range[1];
  1440.                     this.top   = parseInt(range[2],10)||1;
  1441.                     this.end   = parseInt(range[3],10)||this.top;
  1442.                     this.url2 += (args.opt_n||range[2]||"")
  1443.                 }
  1444.      ,appendRange : function(top) {
  1445.                         this.top2=(args.append&&top)?top:(args.overwrite)?this.top:"1";
  1446.                         if(args.overwrite) this.end2=Math.min(this.end,this.top2+49);
  1447.                         else               this.end2=this.top2+49;
  1448.                         if(args.append||args.overwrite) this.url += "rrid="+this.top2+"-"+this.end2+"/";
  1449.                         else this.url += "tp=1/rw=1/";
  1450.                     }
  1451.      ,nextRange   : function() {
  1452.                         var r;
  1453.                         if(args.append||args.overwrite) {
  1454.                             this.top2 = this.end2+1;
  1455.                             if(args.overwrite)  this.end2=Math.min(this.end,this.top2+49);
  1456.                             else                this.end2=this.top2+49;
  1457.                             this.url=this.url.replace(/rrid=[\d-]+/,"rrid="+this.top2+"-"+this.end2);
  1458.                         } else {
  1459.                             r=this.url.match(/\/p=(\d+)/);
  1460.                             if(r) this.url=this.url.replace(/\/p=\d+/,"/p="+(parseInt(r[1],10)+1));
  1461.                             else  this.url=this.url.replace(/(\/tid=\d+)/,"$1/p=2");
  1462.                         }
  1463.                     }
  1464.     });
  1465.    
  1466.     var Jane = object(Jane_2ch,{
  1467.       _re_brd : "^([ \\S]+).*\\r\\n(?:\\t.*\\r\\n)*?\\t$1[^/]*/thr_\\w+/$2/$3\\t$4\\t([^\\t\\r\\n]+?) *\\r\\n"
  1468.     });
  1469.    
  1470.     this.msg = "";
  1471.     this.msgs = [];
  1472.     this.loop = 0;
  1473.     this.latest = false;
  1474.    
  1475.     this.getHeadline = function() {
  1476.         var RE=/(?:<div id="threadImg">(?:<[^>]+>)*?<img [^?]*? data-original="([^"]+).*?)?<div id="threadBody" itemprop="articlebody">([\s\S]*?)<\/div><\/dd><dd class="name"[^>]*>((?:(?!<\/dd>).)*)\s*<\/dd>/
  1477.         var r = this.msg.match(RE);
  1478.         if(r) {
  1479.             this.headline = "<span>"+r[1]+"<br>"+r[2]+"<br>"+r[3]+"</span>";
  1480.             this.headline = this.headline.replace(/\r?\n/g,"");
  1481.             if(this.headline) this.headline = "<hr><font color=blue><b>"+this.headline+"</b></font><hr><br>";
  1482.         }
  1483.     }
  1484.     this.chkLastPage = function() {
  1485.         var RE=/<span class="paging_next">/
  1486.         if(RE.test(this.msg)) this.latest=true;
  1487.     }
  1488.    
  1489.     this.readHTTP = function(url) {
  1490.         this.msg = readHTTP(url)[0];
  1491.     }
  1492.     this.mainReplaceToArray = function() {
  1493.         var r,s,n=URL.top2;
  1494. var RE = /<div class="article"[^>]*>(?:<[^>]*>)*?<a href=[^>]*>#(\d+)<\/a>.*?(?:<span itemprop="commentTime">([^<]*)(?:(?!<span).)*(<span class="latestCmnt">)?(?:(?!<\/div).)*?<dd class="body">(?:<a href="([^"]+)"[^>]*>)?(?:<img[^>]*? data-original="([^"]+)[^>]*>)?(?:<\/a>)?<div class="resbody[^>]*>((?:(?!<\/div>)[\s\S])*?)<\/div>(?:<[^>]*>)*<dd class="name">\[?((?:(?!<\/dd>).)*?)\]?|.*?<span class="resbody semired">([^<]*)(?:(?!<\/dd>).)*)<\/dd><\/div>/img
  1495.         do {
  1496.             r = RE.exec(this.msg);
  1497.             if(r) {
  1498.                 if(r[8]) { s = (n)+"<>DELETE<>DELETE<>DELETE ID:DELETE<><font color=#ff3333>"+r[8]+"</font><>"; }
  1499.                 else {
  1500.                     s = r[1]+"<>"+r[7]+"<><>"+r[2]+"<>"+(r[5]?r[4]?r[5]+"<br>【 "+r[4]+" 】<br>":r[5]+"【この画像は削除されました】<br>":"")+r[6].replace(/\r?\n/g,"")+"<>";
  1501.                 }
  1502.                 this.msgs.push(s);
  1503.                 if(r[3]) {this.latest=true;}
  1504.             }
  1505.             n++;
  1506.         } while(r)
  1507.     }
  1508.    
  1509.     this.preReplace = function() {
  1510.         this.msg = this.msg.replace(/<span data-tipso='[^']+?' class="resOverlay"><a href=[^>]*>((?:(?!<\/a>).)*)<\/a><\/span>/img,"$1");
  1511.     }
  1512.     this.postReplace = function() {
  1513.         this.msg = this.msg.replace(/(<img src="http:\/\/img2\.bakusai\.com[^"]*\.gif")[^>]*>/ig,"$1>&nbsp;")
  1514.                            .replace(/^<span class="lightred"[^>]*>(.+?)<\/span>/img,"<font color=#ff6666>$1</font>");
  1515.     }
  1516.    
  1517.     this.checkMsg = function() {
  1518.         return (this.msgs.length==this.last-this.first+1);
  1519.     }
  1520.     this.overwriteMsg = function() {
  1521.         var msgs = DatInfo.msg;
  1522.         var i;
  1523.         for (i=0; i<this.msgs.length; i++) {
  1524.             msgs[parseInt(this.msgs[i],10)-1] = this.msgs[i].replace(/^\d+<>/,"");
  1525.         }
  1526.         for (i=0; i<msgs.length; i++) if(!msgs[i]) msgs[i] = NOT_GET_MSG;
  1527.         DatInfo.update(msgs);
  1528.         msgs.push("");
  1529.         this.msg = msgs.join("\n");
  1530.     }
  1531.    
  1532.     this.rebuildSubject = function() {
  1533.         var i,list=[];
  1534.         var path = fso.GetParentFolderName( DatInfo.dat ).replace(/[^\\]$/,"$&\\");
  1535.         var files = fso.GetFolder(path).Files;
  1536.         var e = new Enumerator(files);
  1537.         for ( e.moveFirst(); !e.atEnd(); e.moveNext()) {
  1538.             var file = e.item();
  1539.             if(/^.*\.dat$/.test(file.Name)) {
  1540.                 var s=""; try{s=loadFile(file.Path);} catch(e){}
  1541.                 if(s) {
  1542.                     s = s.split("\n");
  1543.                     title=s[0].split("<>");
  1544.                     title=title[4]||"タイトル不明";
  1545.                     list.push([file.Name,file.DateLastModified-0,title,s.length-1]);
  1546.                 }
  1547.             }
  1548.         }
  1549.         for(i=0;i<list.length;i++) list[i]=list[i][0]+"<>"+list[i][2]+"("+list[i][3]+")";
  1550.         if(list.length) list.push("");
  1551.         saveSubjectTxt(path+"subject.txt", list.join("\r\n"), true);
  1552.     }
  1553.     this.makeSubject = function() {
  1554.         var i,s,r,n=-1,list={},tid,tmsg,tnums,x=0;
  1555.         var file=DatInfo.dat.replace(/[^\\]*$/,"")+"subject.txt";
  1556.         var max_read_pages = fso.FileExists(file) ? MAX_READ_PAGES_OF_THREAD_LIST_UPDATE
  1557.                                                   : MAX_READ_PAGES_OF_THREAD_LIST_CREATE;
  1558.         var re1s=[
  1559.                    /<div class="movelist">(?:(?!<\/div>)[\s\S])*?\[\d+\]&nbsp;<a href="(?=[^"]*tid=(\d+))[^>]*>(.+)<\/a><br \/>レス\((\d+)\)/img
  1560.                   ,/^\s*(?:<span class="thrNumber">)?\[\d+\](?:<\/span>)?[^<]*<a href="(?=[^"]*tid=(\d+))[^>]*>(.+)<\/a>[^(\n]*\((\d+)\)/img
  1561.                   ,/<div class="img_thlist">\s*<table>\s*<tr>\s*<th>\s*<div class="thr_title"[^>]*>\s*<a href="(?=[^"]*tid=(\d+))[^>]*>\s*<strong>(.+)<\/strong>(?:(?!<\/table>)[\s\S])*?<td>\d\d\/\d\d \d\d:\d\d\((\d+)件\)/img
  1562.                  ];
  1563.         var re2=/<span class="paging_nextlink[^>]*>[^<]*<a href="[^"]*\/(p=\d+\/)/
  1564.         while(max_read_pages--) {
  1565.             trace("readHTTP URL="+URL.url);
  1566.             r=readHTTP(URL.url);
  1567.             this.msg = r[0];
  1568.             if(n<0) for(i=0;i<re1s.length; i++) { if(re1s[i].test(this.msg)) {n=i; re1s[i].lastIndex=0; break;} }
  1569.            
  1570.             if(n<0) Err("E_data_not_found");
  1571.             do{
  1572.                 if(s = re1s[i].exec(this.msg)) {
  1573.                     tid=RegExp.$1; tmsg=RegExp.$2; tnums=RegExp.$3;
  1574.                     tmsg = tmsg.replace(/<[^>]*>/g,"?");
  1575.                     list[tid+".dat"] = tmsg + "("+ tnums + ")";
  1576.                     x++;
  1577.                 }
  1578.             } while(s);
  1579.             s=this.msg.match(re2);
  1580.             if(!s) break;
  1581.             URL.url = URL.url.replace(/\/p=\d+\//,"/")+s[1];
  1582.         }
  1583.         if(fso.FileExists(file)) {
  1584.             s="";
  1585.             try {s=loadFile(file);} catch(e){}
  1586.             if(s.length) s=s.substring(0,s.length-2).split("\r\n");
  1587.             else s=[];
  1588.             for(i=0;i<s.length;i++) {
  1589.                 s[i].split("<>");
  1590.                 if(s[i].length==2 && !(s[i][0] in list)) list[s[i][0]]=s[i][1];
  1591.             }
  1592.         }
  1593.         s="", n=0;
  1594.         for(i in list) { n++; s+=i+"<>"+list[i]+"\r\n"; }
  1595.         trace("save :"+file, 9);
  1596.         saveSubjectTxt(file, s, true);
  1597.         trace("スレ一覧取得 end : 取得スレ数="+x, 9);
  1598.     }
  1599.    
  1600.     this.proc = function() {
  1601.         URL.set_prm(args.url);
  1602.         var brd = Jane.getBRD(URL.prm);
  1603.         if (!brd) Err("E_brditem_not_found");
  1604.         brd = brd[0];
  1605.         var dat = Jane.LogBasePath + JANE_LOGS + brd + URL.thrID + ".dat";
  1606.         var bak = Jane.LogBasePath + JANE_BAK + brd + URL.thrID + ".dat";
  1607.         DatInfo.setInfo(dat, bak, brd);
  1608.         if(args.append && DatInfo.cnt==0) delete args.append;
  1609.         if ("browser" in args) {
  1610.             invokeBrowser((args.pos&&!URL.requireThrList)
  1611.                            ? URL.url+"rrid="+args.pos+"/p=1/tp=1/#pos_box"
  1612.                            : URL.url);
  1613.             return;
  1614.         }
  1615.         if (args.subject) { this.rebuildSubject(); return; }
  1616.         if (URL.requireThrList) { this.makeSubject(); return; }
  1617.         URL.appendRange(DatInfo.cnt+1);
  1618.         while(!this.latest) {
  1619.             this.msg = "";
  1620.             this.msgs = [];
  1621.            
  1622.             trace("readHTTP="+URL.url,9);
  1623.             trace() | this.readHTTP(URL.url) | trace("HTTP total read time");
  1624.             this.preReplace();
  1625.             this.title = (this.msg.match(/<div id="title_thr"><strong[^>]+>([^<]*?)<\/strong/)||[])[1]||"";
  1626.             this.getHeadline();
  1627.             this.chkLastPage();
  1628.             trace(); this.mainReplaceToArray(); trace("html->dat main Replace");
  1629.             if(this.msgs.length) { //キャプチャしたレスあり
  1630.                 this.first = parseInt((this.msgs[0].match(/^(\d+)<>/)||[])[1],10);
  1631.                 this.last  = parseInt((this.msgs[this.msgs.length-1].match(/^(\d+)<>/)||[])[1],10);
  1632.                 if(this.first==1) this.msgs[0]=this.msgs[0].replace(/<>((?:(?!<>).)*<>)$/,"<>"+this.headline+"$1"+this.title);
  1633.             } else {
  1634.                 this.latest = true;
  1635.                 break;
  1636.             }
  1637.             if(!this.checkMsg()) Err("E_data_error");
  1638.             if(args.overwrite) this.overwriteMsg();
  1639.             else {
  1640.                 this.msgs.push("");
  1641.                 this.msg = this.msgs.join("\n").replace(/^(\d+)<>/mg,"");
  1642.             }
  1643.             this.postReplace();
  1644.             DatInfo.offset = (this.loop)? this.first-1 : 0;
  1645.             trace("スレタイトル:"+this.title+"\tレス数:"+Math.max(DatInfo.cnt,this.last),9);
  1646.             saveDat(dat, this.msg, null, this.loop,this.latest);
  1647.             DatInfo.setNewDatInfo(this.title, this.last);
  1648.             saveIdx("http://"+URL.brdItem+"/"+URL.brdID+"/", true);
  1649.             URL.nextRange();
  1650.             this.loop++;
  1651.             if(args.overwrite && this.last>=URL.end) this.latest=true;
  1652.         }
  1653.         invokeJaneStyle(URL.getUrl2());
  1654.         trace("---- end ----",1);
  1655.     }
  1656. }
  1657.  
  1658. //---------------------------------------------------------
  1659.  
  1660. var Thread_ex;
  1661. /*@if (@DEBUG)
  1662.     main();
  1663.     @else @*/
  1664.     try {main();} catch (e) {if(e.message!=ERROR_MSG_STOP) WScript.Echo(e.message);}
  1665. /*@end @*/
  1666.  
  1667. //==============================================================
  1668. /*@if (@EnabledTest2chJSON)
  1669. //  日付の秒以下は常に".00"とする
  1670. //  名前のコテ等の修飾タグは無い(<span></b><font>等
  1671. //  本文末尾のRock54:,BBx-MD5:等も修飾タグは付かない
  1672. //
  1673. function get2chJSON() {
  1674.     var urlJSON = "http://itest.5ch.net/public/newapi/client.php?subdomain=$2&board=$5&dat=$6".expand(URL.prm);
  1675.     trace("try URL=" + urlJSON,9);
  1676.     var s = readHTTP(urlJSON)[0];
  1677.     if(!s) return null;
  1678.     var total_count = (s.match(/"total_count":(\d+)/)||[])[1];
  1679.     s=s.replace(/\\u301c/g,"~");
  1680.     var thread      = s.match(/"thread":\[(\d+),(\d+),("[^"]*"),("[^"]*"),("[^"]*"),("[^"]*")/);
  1681.     var comments    = (s.match(/"comments":\[((?:\[(\d+),("[^"]*"),("[^"]*"),"(\d+)",("[^"]*"),("[^"]*"),("[^"]*")(?:[,\d]*)\],?)+)\]/)||[])[1];
  1682.     if (!thread || !comments) return null;
  1683.     eval("var title ="+thread[6]+";");
  1684.     comments = comments.replace(/(\[(?:\d+),(?:"[^"]*"),(?:"[^"]*"),(?:"\d+"),(?:"[^"]*"),(?:"[^"]*"),(?:"[^"]*")(?:[,\d]*)\]),/g,"$1\n").split("\n");
  1685.     Thread_2ch.title = title;
  1686.     Thread_2ch.last = comments.length;
  1687.     if(args.append && DatInfo.cnt>0) { comments.splice(0,DatInfo.cnt); }
  1688.     for(var i=0; i<comments.length; i++) {
  1689.         var res,d,id="",be="";
  1690.         var res = comments[i].substring(1,comments[i].length-1);
  1691.         res = res.match(/^(\d+),("[^"]*"),("[^"]*"),"(\d+)",("[^"]*"),("[^"]*"),("[^"]*")/);
  1692.         var name = eval(res[2]).replace(/(◆.+)$/,"</b>$1<b>")
  1693.                         .replace(/(\([^ )]+ [^ )]{4}-[^ )]{4}\))/,"</b>$1<b>")
  1694.                         .replace(/(転載ダメ&(?:copy|#169);[25]ch\.net)/,"<small>$1</small>");
  1695.         d = new Date( (parseInt(res[4],10)<365*24*60*60*1000) ? parseInt(res[4],10)*1000 : parseInt(res[4],10) );
  1696.         d = (d>0) ? formatDate(d,2) : name;
  1697.         if (res[5]!='""')  id = " ID:" + eval(res[5]);
  1698.         if (res[6]!='""')  be = " BE:" + eval(res[6]);
  1699.         comments[i] = [ name, eval(res[3]), d + id + be, eval(res[7]), ].join("<>");
  1700.     }
  1701.     s = "";
  1702.     if(comments.length>0) {
  1703.         if(!args.append || !DatInfo.cnt) { comments[0] += title; }
  1704.         s = comments.join("\n") + "\n";
  1705.     }
  1706.     return s;
  1707. }
  1708. @end @*/
RAW Paste Data