/**
 * FeedBlog CoreScript
 *
 * @copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
 * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
 * @since 2009/02/27
 * @version 3.1.1.0
 */
// ブログ本体のHTMLファイルの名前を記入してください
var blogUrl = "./index.html"

// 最新の日記を示すパスへの文字列です。最新の日記を置く場所を変えたいときは変更してください。
var latestXml = "./xml/diary.xml";

// ログのリストが書かれたXMLのファイルパスを記入してください
var logXmlUrl = "./xml/loglist.xml";

// Ext jsパネルのサイズを記述してください
var extPanelWidth = 580;

// ログを表示するコンボボックスのサイズを記述してください
var extComboWidth = 150;

// 日記間のスパン(間隔)をPIXEL単位で記述してください
var entrySpan = 3;

// 一画面あたりの表示日記数です
var showLength = 3;

// 検索結果をメモリ上に保持する変数です
var loadedEntries;

/**
 * XMLファイルから読み込んだファイルのバリデートモードを選択します。
 * 0 = 改行コード部分に<br/>を挿入
 * 1 = 改行コード部分に<br/>を挿入しない
 */
var validateMode = "1";

/**
 * Ext jsパネルを実際に生成します。この部分を編集することでデザインを変更可能です。
 * @param {String} title パネルのタイトル部分に表示する文字列
 * @param {String} drawitem パネルの本文を格納したDIV要素のid
 * @param {String} renderto 「タイトル・更新日時・本文」の1日分の日記データを焼き付けるDIV要素のid
 * @param {String} closed (Ext jsパネルオプション)日記をクローズ状態で生成するか否か
 */
function generatePanel(title, drawitem, renderto, closed){
    // HTMLを生成する
    var htmlBuffer = [];
    htmlBuffer.push("<table cellspacing='0' style='border: 0px; padding: 0px;'><tbody style='border: 0px; padding: 0px; margin: 0px;'>");
    htmlBuffer.push("<tr><td class='header' onclick='closePanel(\"" +
    renderto +
    "td\")'>" +
    title +
    "</td></tr><tr><td style='line-height: 1px;'><br/></td></tr><tr><td style='width: " +
    (extPanelWidth - 2) +
    "px;' class='content'><div id='" +
    renderto +
    "td'>" +
    document.getElementById(renderto).innerHTML +
    "</div></td></tr></tbody></table>");
    
    document.getElementById(renderto).innerHTML = htmlBuffer.join("");
}

/**
 * jQueryでのパネル開閉を制御します
 */
function closePanel(id){
    $("#" + id).slideToggle();
}

/**
 * jQueryへのイベント登録です。すべてのDOMが利用可能になった時点で実行されます。
 */
$(document).ready(function(){
    // 特定の過去ログの探索モードか否かを判別するためにハッシュを取得する
    var urlhash = "" + location.hash.substring(1);
    
    // ハッシュが空か、ハッシュ形式の正規表現に一致しないようなら通常モードで実行
    if (urlhash.length == 0) {
        xmlLoader(latestXml);
        logXMLLoader();
    }
    // ハッシュ形式の正規表現に一致したら探索モード
    else {
        searchMode(urlhash);
        logXMLLoader();
    }
});

/**
 * 記事クラス
 * @param {Object} obj entry 要素の DOM オブジェクト
 */
function Entry(obj){
    this.title = $("title:first", obj).text();
    if (this.title == "") 
        requiredElementError(obj, "title");
    this.title = "<span>" + validateText(this.title) + "</span>";
    this.content = $("content:first", obj).text();
    this.content = "<span>" + validateText(this.content) + "</span>";
    this.id = $("id:first", obj).text();
    if (this.id == "") 
        requiredElementError(obj, "id");
    this.date = $("updated:first", obj).text();
    if (this.date == "") 
        requiredElementError(obj, "updated");
    this.date = validateData(this.date);
}

/**
 * 呼び出すとDIV:id名:writeArea上のHTMLを削除し、ロードエフェクトを表示します
 */
function loadingEffect(){
    document.getElementById("writeArea").innerHTML = '<div id="drawPanel"><div id="drawItem" class="code" style="text-align: center;"><\/div><\/div>';
    document.getElementById("drawItem").innerHTML = '<br/><br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';
    
    // ロード表示用のパネルを生成
    generatePanel("Now Loading .....", "drawItem", "drawPanel", false);
}

/**
 * 日記データのエラー時の処理を行います
 */
function requiredElementError(parent, name){
    alert(parent.ownerDocument.URL + ": 必須な要素 " +
    name +
    " が存在しないか空な " +
    parent.tagName +
    " 要素が存在します");
}

function xmlAttrContentEscape(str){
    // return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/^[ ]+/mg, "&nbsp;").replace(/^[\t]+/mg, "");
}

/**
 * 日付のHTML表示用バリデーション処理を行います
 * @param {String} data RFC3339形式のdate-time文字列
 */
function validateData(data){
    var regT = new RegExp("T", "gm");
    data = data.replace(regT, " ");
    
    // 秒数の小数点以下の部分はカットする
    data = data.substring(0, 19);
    
    return data;
}

/**
 * 日記本文のバリデーション処理を行います
 * @param {String} contents 日記の本文が格納されている文字列
 */
function validateText(contents){
    // <br/>タグを挿入する
    if (validateMode == 0) {
        contents = contents.replace(/[\n\r]|\r\n/g, "<br />");
    }
    
    return contents;
}

/**
 * 日記本文に日付を付加します
 * @param {String} contents 日記の本文が格納されている文字列
 * @param {String} id 日記の初公開日を示す日付文字列
 */
function contentsWithid(contents, id){
    // リンク用文末作成
    var hashTag = '<br/><div style="text-align: right;"><a href="' +
    xmlAttrContentEscape(blogUrl) +
    '#' +
    xmlAttrContentEscape(id) +
    '" target="_blank">- この日の記事にリンクする -<\/a><\/span>';
    return contents + hashTag;
}

/**
 * ログファイル選択用のコンボボックスをid名:logSelecterに生成します
 */
function logXMLLoader(){
    // ログ用のXMLを読み込みます
    jQuery.ajax({
        url: logXmlUrl,
        method: "GET",
        error: showError,
        success: function(xmlData){
            var separateTag = xmlData.getElementsByTagName("file");
            var fileList = new Array(separateTag.length);
            
            // 読み込んだ要素をStoreに格納して表示
            var boxBuffer = [];
            boxBuffer.push("<form name='logform'><select name='logbox' style='width: " + extComboWidth + "px' onchange='xmlLoader(this.options[this.selectedIndex].value)'>");
            for (var i = 0; i < separateTag.length; i++) {
                boxBuffer.push("<option value='" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + "'/>" + separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue + "</option>");
            }
            boxBuffer.push("</select></form>");
            
            // コンボボックス要素を生成
            document.getElementById("logSelecter").innerHTML = boxBuffer.join("");
        }
    });
}

/**
 * 日記のデータが記述されたXMLデータを読み込むロジックを生成します
 * @param {String} fileName 読み込み日記のデータが記述されているXMLファイルのパス
 */
function xmlLoader(fileName){
    // ロードエフェクトに切り替え
    loadingEffect()
    
    var url = fileName;
    
    // 日記をロードします
    var loader = new jQuery.ajax({
        url: url,
        method: "POST",
        success: writeHtml,
        error: showError
    });
}

/**
 * 日記データのエラー時の処理を行います
 */
function showError(){
    document.getElementById("writeArea").innerHTML = '<div id="drawPanel"><div id="drawItem" class="code" style="text-align: center;"><\/div><\/div>';
    document.getElementById("drawItem").innerHTML = '<br/>日記ファイルのロードに失敗しました！<br/><br/>';
    
    // エラー内容をパネルに描画
    generatePanel("Error!", "drawItem", "drawPanel", false);
    
    alert("日記ファイルが読み込めません！");
}

/**
 * 渡された文字列と一致するfeed1.0:updated要素を持った日記を検索し、表示します
 * @param {String} urlhash feed1.0:updated要素と一致する文字列
 */
function searchMode(urlhash){
    // ロードエフェクト表示
    loadingEffect();
    
    // ログXMLファイルを読み込む
    var loader = new jQuery.ajax({
        url: logXmlUrl,
        method: "POST",
        error: showError,
        success: function(xmlData){
            // ファイルパスの要素のみを抽出する
            var separateTag = xmlData.getElementsByTagName("file");
            var filelist = new Array(separateTag.length);
            
            // すべてのファイルパスを配列に格納する
            for (var i = 0; i < separateTag.length; i++) {
                // "path"ノードの値を格納
                filelist[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
            }
            
            // 終端まで検索した日記XMLファイルの数を格納する変数
            var searchFileNumbers = 0;
            
            // ファイルパス配列に格納されているすべての日記に対し、探索を開始する
            for (var i = 0; i < separateTag.length; i++) {
                // ファイルパス配列の要素からリクエストを生成し、対象データをロードする
                var searchlog = new jQuery.ajax({
                    url: filelist[i],
                    method: "POST",
                    success: function(xmlData){
                        // entry要素のみを切り出す
                        var searchSeparateTag = xmlData.getElementsByTagName("entry");
                        
                        for (var j = 0; j < searchSeparateTag.length; j++) {
                            // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う
                            var id = searchSeparateTag[j].getElementsByTagName("id")[0].firstChild.nodeValue;
                            
                            // idの値と比較を行う
                            if (urlhash == id) {
                                var entry = new Entry(searchSeparateTag[j]);
                                
                                document.getElementById("writeArea").innerHTML = '<div><table class="pager" width="' + extPanelWidth + '" cellspacing="1"><tbody>' +
                                '<tr><td class="pager" colspan="3">1件～1件(全1件)目の記事を表示中<br/></td></tr>' +
                                '<tr><td align="left"><<< 前の3件を表示</td><td align="center">[ 0 ]</td><td align="right">次の3件を表示 >>></td></tr></tbody></table></div>' +
                                '<div style="line-height: ' +
                                entrySpan +
                                'px;"><br/></div>' +
                                '<div id="drawPanel"><div id="drawItem" class="code"><\/div><\/div>' +
                                '<div style="line-height: ' +
                                entrySpan +
                                'px;"><br/></div>' +
                                '<div><table class="pager" width="' +
                                extPanelWidth +
                                '" cellspacing="1"><tbody>' +
                                '<tr><td align="left"><<< 前の3件を表示</td><td align="center">[ 0 ]</td><td align="right">次の3件を表示 >>></td></tr>' +
                                '<tr><td class="pager" colspan="3">1件～1件(全1件)目の記事を表示中<br/></td></tr></tbody></table></div>';
                                document.getElementById("drawItem").innerHTML = contentsWithid(entry.content, entry.id);
                                
                                // 探索されたパネルはオープン状態で展開する
                                generatePanel(entry.title + " / " + entry.date, "drawItem", "drawPanel", false);
                                
                                return true;
                            }
                            else 
                                if (j == searchSeparateTag.length - 1) {
                                    // 日記を最後まで検索してもファイルが見つからなかった場合、検索済み日記ファイル数に1加算する
                                    searchFileNumbers = searchFileNumbers + 1;
                                    
                                    // もうファイルがない場合はエラーを表示する
                                    if (searchFileNumbers == separateTag.length) {
                                        // ファイルパス配列から日記が見つからなかった場合の処理
                                        document.getElementById("writeArea").innerHTML = '<div id="drawPanel"><div id="drawItem" class="code"><\/div><\/div>';
                                        document.getElementById("drawItem").innerHTML = "指定された日記は存在しません。";
                                        
                                        // エラー内容を表示する
                                        generatePanel("Search Failed.", "drawItem", "drawPanel", false);
                                    }
                                }
                        }
                    }
                });
            }
        }
    });
}

/**
 * 検索結果を分割して表示します
 * @param {int} showLength 一回の画面に表示する記事数
 * @param {int} startIndex 表示を開始する日記のインデックス
 */
function showEntriesRange(showLength, startIndex){
    // メモリ上から日記データをロード
    var entries = loadedEntries;
    
    // 表示インデックスが範囲外の場合はエラーパネルを表示して終了
    if (startIndex < 0 || (entries.length <= startIndex && entries.length != 0)) {
        showError();
        return;
    }
    
    var stringBuffer = [];
    
    // リミッターを設定する
    var loopLimit = (showLength + startIndex > entries.length) ? entries.length : showLength + startIndex;
    var indexShowEntries = loopLimit + 1;
    
    // 情報メニュー表示用バッファです
    var menuInfoBuffer = [];
    menuInfoBuffer.push("<tr><td colspan='3' class='pager'>");
    var viewStartIndex = 0;
    entries.length == 0 ? viewStartIndex = 0 : viewStartIndex = startIndex + 1
    menuInfoBuffer.push(viewStartIndex + "件～" + loopLimit + "件(全" + entries.length + "件)目の記事を表示中<br/>");
    menuInfoBuffer.push("</td></tr>");
    
    // ページ移動メニュー表示用バッファです
    var menuMoveBuffer = [];
    menuMoveBuffer.push("<tr>");
    // 左パネルの表示制御
    if (startIndex - showLength >= 0) {
        menuMoveBuffer.push("\<td align='left'><a href='' onclick='showEntriesRange(" +
        showLength +
        ", " +
        (startIndex - showLength) +
        "); return false;'>\<\<\< 前の" +
        showLength +
        "件を表示</a\></td>");
    }
    else {
        menuMoveBuffer.push("\<td align='left'>\<\<\< 前の" +
        showLength +
        "件を表示</a\></td>");
    }
    
    // 中央のパネルの表示制御
    menuMoveBuffer.push("<td align='center'>[ ");
    var menuNumbers = Math.ceil(entries.length / showLength);
    for (i = 0; i < menuNumbers; i++) {
        if (startIndex / showLength == i) {
            menuMoveBuffer.push(i + " ");
        }
        else {
            menuMoveBuffer.push("<a href='' onclick='showEntriesRange(" +
            showLength +
            ", " +
            (i * showLength) +
            "); return false;'>");
            menuMoveBuffer.push(i);
            menuMoveBuffer.push("</a> ");
        }
    }
    menuMoveBuffer.push("]</td>");
    
    // 右パネルの表示制御
    if (entries.length > startIndex + showLength) {
        menuMoveBuffer.push("\<td align='right'><a href='' onclick='showEntriesRange(" +
        showLength +
        ", " +
        (startIndex + showLength) +
        "); return false;'>\次の" +
        showLength +
        "件を表示 \>\>\></a\></td>");
    }
    else {
        menuMoveBuffer.push("\<td align='right'>次の" +
        showLength +
        "件を表示 \>\>\></a\></td>");
    }
    menuMoveBuffer.push("</tr>");
    
    // メニューを結合してパネル（前方）に組み込みます
    stringBuffer.push("<div><table cellspacing='1' width='" +
    extPanelWidth +
    "px' class='pager'><tbody>");
    stringBuffer.push(menuInfoBuffer.join(""));
    stringBuffer.push(menuMoveBuffer.join(""));
    stringBuffer.push("</tbody></table></div>");
    
    stringBuffer.push('<div style="line-height: ' + entrySpan + 'px;"><br/></div>');
    
    // 日記描画部分のパネルを生成します
    for (var i = startIndex; i < loopLimit; i++) {
        stringBuffer.push('<div id="drawPanel');
        stringBuffer.push(i);
        stringBuffer.push('"><div id="drawItem');
        stringBuffer.push(i);
        stringBuffer.push('" class="code"><\/div><\/div><div style="line-height: ' + entrySpan + 'px;"><br/></div>')
    }
    
    // メニューを結合してパネル（後方）に組み込みます
    stringBuffer.push("<div><table cellspacing='1' width='" +
    extPanelWidth +
    "px' class='pager'><tbody>");
    stringBuffer.push(menuMoveBuffer.join(""));
    stringBuffer.push(menuInfoBuffer.join(""));
    stringBuffer.push("</tbody></table></div>");
    
    document.getElementById("writeArea").innerHTML = stringBuffer.join("");
    
    for (var i = startIndex; i < loopLimit; i++) {
        // 各要素をオブジェクトに格納します
        var entry = entries[i];
        document.getElementById("drawItem" + i).innerHTML = contentsWithid(entry.content, entry.id);
        
        // すべてのパネルをオープン状態で生成します
        generatePanel(entry.title + " / " + entry.date, "drawItem" + i, "drawPanel" + i, false);
    }
}

/**
 * 日記のログファイルデータが記述されているXMLファイルを読み込んで表示します。DIV:id名:writeArea上に読み込んだ日記の内容を表示します
 * @param {Object} xmlData 日記が記述されたXMLファイル(feed 1.0準拠)
 */
function writeHtml(xmlData){
    var separateTag = xmlData.getElementsByTagName("entry");
    var stringBuffer = [];
    // メモリ上での保持変数を初期化します
    loadedEntries = [];
    
    // メモリ上の変数に全ての日記要素を格納します
    for (var i = 0; i < separateTag.length; i++) {
        loadedEntries.push(new Entry(separateTag[i]));
    }
    
    // 表示ロジック呼び出し
    showEntriesRange(showLength, 0);
}

