-/**\r
- * FeedBlog CoreScript\r
- *\r
- * @copyright 2013 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)\r
- * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)\r
- * @since 2009/02/27\r
- * @version 4.2.0.0\r
- */\r
-\r
-// ブログ本体のHTMLファイルのURL\r
-var mainPageUrl;\r
-\r
-// 検索用ページURL\r
-var searchPageUrl;\r
-\r
-// 最新の記事を示すパスへの文字列\r
-var latestXml;\r
-\r
-// ログのリストが書かれたXMLのファイルパス\r
-var logXmlUrl;\r
-\r
-// 一画面あたりの表示記事数\r
-var showLength;\r
-\r
-/**\r
- * XMLファイルから読み込んだファイルのバリデートモード\r
- * 0 = 改行コード部分に<br/>を挿入\r
- * 1 = 改行コード部分に<br/>を挿入しない\r
- */\r
-var validateMode;\r
-\r
-// 検索結果をメモリ上に保持する変数\r
-var loadedEntries;\r
-\r
-// fetchEntries 用のセマフォ\r
-var fetchEntriesSemaphore = new Semaphore();\r
-\r
-// URL末尾用文字列(スクリプトを開いた瞬間のミリ秒を記録)\r
-var urlSuffix;\r
-\r
-/**\r
- * 記事を実際に生成します。この部分を編集することでデザインを変更可能です。\r
- * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト\r
- * @param {String} drawitem 「本文」を描画すべきパネルのDIV要素のid\r
- * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを最終的に描画すべきパネルのDIV要素のid\r
- * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か\r
- */\r
-function generatePanel(entry, drawitem, renderto, closed) {\r
- // プラグインを実行\r
- if ( typeof ($("#" + renderto).feedblog_contents_plugin) == "function") {\r
- $("#" + renderto).feedblog_contents_plugin({\r
- entry : entry\r
- });\r
- }\r
-\r
- // HTML用の配列を用意する\r
- var htmlBuffer = [];\r
-\r
- // 内部的に描画先IDを生成\r
- var feedblogContentId = "" + renderto + "_content_div";\r
-\r
- // 各要素をオブジェクトに描画\r
- $("#" + drawitem).html(entry.content);\r
-\r
- // ヘッダパネルを生成 class= .feedblog_header\r
- htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +\r
-\r
- // 本体記事を作成 class= .feedblog_content\r
- "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");\r
-\r
- // 最終描画実施\r
- $("#" + renderto).html(htmlBuffer.join(""));\r
-}\r
-\r
-/**\r
- * システム表示画面を実際に生成します。この部分を編集することでデザインを変更可能です。\r
- * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト\r
- * @param {String} drawitem パネルの本文を格納したDIV要素のid\r
- * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを焼き付けるDIV要素のid\r
- * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か\r
- */\r
-function generateSystemPanel(entry, drawitem, renderto, closed) {\r
- // HTMLを生成する\r
- var htmlBuffer = [];\r
-\r
- // 描画先IDを生成\r
- var feedblogContentId = "" + renderto + "_content_div";\r
-\r
- // 各要素をオブジェクトに描画\r
- $("#" + drawitem).html(entry.content);\r
-\r
- // ヘッダパネルを生成 class= .feedblog_header\r
- htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +\r
-\r
- // 本体記事を作成 class= .feedblog_content\r
- "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");\r
-\r
- $("#" + renderto).html(htmlBuffer.join(""));\r
-}\r
-\r
-/**\r
- * 全ての定数を取得・セットします\r
- */\r
-function initialize() {\r
- // 初期値をhiddenパラメータより読み込みます\r
- mainPageUrl = $("#feedblog_mainpageurl").val();\r
- searchPageUrl = $("#feedblog_searchpageurl").val();\r
- latestXml = $("#feedblog_latestxml").val();\r
- logXmlUrl = $("#feedblog_loglistxmlurl").val();\r
- showLength = parseInt($("#feedblog_showlength").val());\r
- if (isNaN(showLength)) {\r
- showLength = 1;\r
- }\r
- validateMode = $("#feedblog_validatemode").val();\r
- \r
- // 初期値を設定します\r
- urlSuffix = +new Date();\r
-\r
- // 必要な環境を確認します\r
- var errorBuf = [];\r
- // 変数確認\r
- if (mainPageUrl === undefined) {\r
- errorBuf.push("設定値「feedblog_mainpageurl」が欠落しています。");\r
- }\r
- if (searchPageUrl === undefined) {\r
- errorBuf.push("設定値「feedblog_searchpageurl」が欠落しています。");\r
- }\r
- if (latestXml === undefined) {\r
- errorBuf.push("設定値「feedblog_latestxml」が欠落しています。");\r
- }\r
- if (logXmlUrl === undefined) {\r
- errorBuf.push("設定値「feedblog_loglistxmlurl」が欠落しています。");\r
- }\r
- if (showLength === undefined) {\r
- errorBuf.push("設定値「feedblog_showlength」が欠落しています。");\r
- }\r
- if (validateMode === undefined) {\r
- errorBuf.push("設定値「feedblog_validatemode」が欠落しています。");\r
- }\r
- // SHA-1関数確認\r
- try {\r
- if ( typeof (CryptoJS.SHA1) != "function") {\r
- errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");\r
- }\r
- } catch (ex) {\r
- errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");\r
- }\r
-\r
- // 描画エリアチェック\r
- if ($("#feedblog_writearea").length == 0) {\r
- errorBuf.push("描画エリア「feedblog_writearea」が存在しません。");\r
- }\r
- if ($("#feedblog_logselecter").length == 0) {\r
- errorBuf.push("描画エリア「feedblog_logselecter」が存在しません。");\r
- }\r
-\r
- // エラーがある場合は以降の処理を継続しない\r
- if (errorBuf.length > 0) {\r
- alert("初期設定値に誤りがあります。\n詳細:\n" + errorBuf.join("\n"));\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-/**\r
- * jQueryへのイベント登録です。すべてのDOMが利用可能になった時点で実行されます。\r
- */\r
-$(document).ready(function() {\r
- // 初期処理を実施\r
- if (!initialize()) {\r
- return false;\r
- }\r
-\r
- // 制御に必要な各種パラメタを取得する\r
- var tag = getParamFromUrl("tag");\r
- var id = getParamFromUrl("id");\r
- var urlhash = getHashFromUrl();\r
-\r
- // ハッシュが空か、ハッシュ形式の正規表現に一致しないようなら通常モードで実行\r
- if (urlhash.length == 0 && tag.length == 0 && id.length == 0) {\r
- fullWriteMode(latestXml);\r
- logXMLLoader();\r
- } else if (urlhash.length == 0 && id.length == 0) {\r
- // タグが指定されているのでタグ探索モード\r
- searchTagMode(tag);\r
- logXMLLoader();\r
- } else if (urlhash.length == 0) {\r
- // IDが指定されているのでID探索モード\r
- searchIdMode(id);\r
- logXMLLoader();\r
- } else {\r
- // ハッシュ形式の正規表現に一致したら探索モード\r
- searchHashMode(urlhash);\r
- logXMLLoader();\r
- }\r
-});\r
-\r
-/**\r
- * jQueryでのパネル開閉を制御します\r
- */\r
-function closePanel(id) {\r
- $("#" + id).slideToggle();\r
-}\r
-\r
-/**\r
- * 記事クラス\r
- * @param {Object} obj entry 要素の DOM オブジェクト\r
- */\r
-function Entry(obj) {\r
- this.title = $("title:first", obj).text();\r
- if (this.title == "")\r
- requiredElementError(obj, "title");\r
- this.title = validateText(this.title);\r
- this.content = $("content:first", obj).text();\r
- this.content = validateText(this.content);\r
- this.id = $("id:first", obj).text();\r
- if (this.id == "")\r
- requiredElementError(obj, "id");\r
- this.date = $("updated:first", obj).text();\r
- if (this.date == "")\r
- requiredElementError(obj, "updated");\r
- this.date = validateData(this.date);\r
- this.category = $("category", obj);\r
-}\r
-\r
-/**\r
- * システム用記事クラス\r
- * @param {Object} obj entry 要素の DOM オブジェクト\r
- */\r
-function SystemEntry(obj) {\r
- this.title = $("title:first", obj).text();\r
- this.title = validateText(this.title);\r
- this.content = $("content:first", obj).text();\r
- this.content = validateText(this.content);\r
- this.id = $("id:first", obj).text();\r
- this.date = $("updated:first", obj).text();\r
- this.date = validateData(this.date);\r
- this.category = $("category", obj);\r
-}\r
-\r
-/**\r
- * 呼び出すとDIV:id名:feedblog_writearea上のHTMLを削除し、ロードエフェクトを表示します\r
- */\r
-function loadingEffect() {\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
-\r
- // ロード表示用のパネルを生成\r
- var systemEntry = new SystemEntry();\r
- systemEntry.title = "Now Loading .....";\r
- systemEntry.content = '<br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';\r
- generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-}\r
-\r
-/**\r
- * 記事データのエラー時の処理を行います\r
- */\r
-function showError() {\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
-\r
- // エラー内容をパネルに描画\r
- var systemEntry = new SystemEntry();\r
- systemEntry.title = "エラー";\r
- var errorContent = [];\r
- errorContent.push('<br/>記事ファイル(XML)の取得に失敗しました。以下のような原因が考えられます。<br/><br/>');\r
- errorContent.push('・設定値「feedblog_latestxml」に正しいパスが設定されていない。<br/>');\r
- errorContent.push('・設定値「feedblog_loglistxmlurl」に正しいパスが設定されていない。<br/>');\r
- errorContent.push('・ローカル環境で起動している(必ずサーバにアップロードして実行してください)。<br/>');\r
- errorContent.push('<br/>');\r
- systemEntry.content = errorContent.join("\n");\r
- generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-\r
- // 結果表示エリアをリセット\r
- $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");\r
-}\r
-\r
-/**\r
- * 記事データのエラー時の処理を行います\r
- */\r
-function notFoundError() {\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
-\r
- // エラー内容をパネルに描画\r
- var systemEntry = new SystemEntry();\r
- systemEntry.title = "検索失敗";\r
- systemEntry.content = '<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>';\r
- generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-}\r
-\r
-/**\r
- * 記事データのエラー時の処理を行います\r
- */\r
-function requiredElementError(parent, name) {\r
- alert(parent.ownerDocument.URL + ": 必須な要素 " + name + " が存在しないか空な " + parent.tagName + " 要素が存在します");\r
-}\r
-\r
-function xmlAttrContentEscape(str) {\r
- return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").replace(/^[\t]+/mg, "");\r
-}\r
-\r
-/**\r
- * 日付のHTML表示用バリデーション処理を行います\r
- * @param {String} data RFC3339形式のdate-time文字列\r
- */\r
-function validateData(data) {\r
- var regT = new RegExp("T", "gm");\r
- data = data.replace(regT, " ");\r
-\r
- // 秒数の小数点以下の部分はカットする\r
- data = data.substring(0, 19);\r
-\r
- return data;\r
-}\r
-\r
-/**\r
- * 記事本文のバリデーション処理を行います\r
- * @param {String} contents 記事の本文が格納されている文字列\r
- */\r
-function validateText(contents) {\r
- // <br/>タグを挿入する\r
- if (validateMode == 0) {\r
- contents = contents.replace(/[\n\r]|\r\n/g, "<br />");\r
- }\r
-\r
- return contents;\r
-}\r
-\r
-/**\r
- * URLからパラメタを取得するための関数\r
- */\r
-function getParamFromUrl(paramName) {\r
- // GETパラメタよりタグを取得する\r
- var tag = "";\r
- if (location.search.length > 1) {\r
- var queries = location.search.substring(1).split('&');\r
- for (var i = 0; i < queries.length; i++) {\r
- if (("" + queries[i].split('=')[0]) == paramName) {\r
- tag = "" + queries[i].split('=')[1];\r
- }\r
- }\r
- }\r
-\r
- return tag;\r
-}\r
-\r
-/**\r
- * URLからハッシュを取得するための関数\r
- */\r
-function getHashFromUrl() {\r
- return "" + location.hash.substring(1);\r
-}\r
-\r
-/**\r
- * 長い順に並べるための比較関数です\r
- * @param {String} a 比較対象(1)\r
- * @param {String} b 比較対象(2)\r
- */\r
-function compareLengthDecrease(a, b) {\r
- a = a.length;\r
- b = b.length;\r
- return a > b ? -1 : a < b ? 1 : 0;\r
-}\r
-\r
-/**\r
- * セマフォ制御用のオブジェクトです\r
- */\r
-function Semaphore() {\r
- this.id = null;\r
- this.count = 0;\r
- this.buf = [];\r
- this.xhrs = [];\r
-}\r
-\r
-/**\r
- * セマフォ初期化用の関数です\r
- */\r
-Semaphore.prototype.init = function() {\r
- while (this.xhrs.length > 0) {\r
- this.xhrs.shift().abort();\r
- }\r
- this.id = Math.random();\r
- this.count = 0;\r
- this.buf = [];\r
-};\r
-\r
-/**\r
- * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します\r
- * class - .feedblog_logform, .feedblog_logselecter\r
- */\r
-function logXMLLoader() {\r
- // ログ用のXMLを読み込みます\r
- jQuery.ajax({\r
- url : logXmlUrl + '?time=' + urlSuffix,\r
- method : "GET",\r
- error : showError,\r
- success : function(xmlData) {\r
- var separateTag = xmlData.getElementsByTagName("file");\r
-\r
- // 読み込んだ要素をStoreに格納して表示\r
- var boxBuffer = [];\r
- boxBuffer.push("<form class='feedblog_logselecter' name='feedblog_logform'><select class='feedblog_logselecter' id='feedblog_logbox' onchange='fullWriteMode(this.options[this.selectedIndex].value)'>");\r
- for (var i = 0; i < separateTag.length; i++) {\r
- boxBuffer.push("<option value='" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + "'>" + separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue + "</option>");\r
- }\r
- boxBuffer.push("</select></form>");\r
-\r
- // コンボボックス要素を生成\r
- $("#feedblog_logselecter").html(boxBuffer.join(""));\r
- }\r
- });\r
-}\r
-\r
-/**\r
- * 記事のデータが記述されたXMLデータを読み込むロジックを生成します\r
- * @param {String} fileName 読み込み記事のデータが記述されているXMLファイルのパス\r
- */\r
-function fullWriteMode(fileName) {\r
- // ロードエフェクトに切り替え\r
- loadingEffect();\r
-\r
- var url = fileName;\r
-\r
- // 記事をロードします\r
- var loader = new jQuery.ajax({\r
- url : url + '?time=' + urlSuffix,\r
- method : "GET",\r
- success : function(xmlData) {\r
- var separateTag = xmlData.getElementsByTagName("entry");\r
- var stringBuffer = [];\r
- // メモリ上での保持変数を初期化します\r
- loadedEntries = [];\r
-\r
- // メモリ上の変数に全ての記事要素を格納します\r
- for (var i = 0; i < separateTag.length; i++) {\r
- loadedEntries.push(new Entry(separateTag[i]));\r
- }\r
-\r
- // 表示ロジック呼び出し\r
- showEntriesRange(showLength, 0);\r
- },\r
- error : showError\r
- });\r
-}\r
-\r
-/**\r
- * 渡された文字列と一致するfeed1.0:updated要素を持った記事を検索し、表示します\r
- * @param {String} urlhash feed1.0:updated要素と一致する文字列\r
- */\r
-function searchHashMode(urlhash) {\r
- // ロードエフェクト表示\r
- loadingEffect();\r
-\r
- // ログXMLファイルを読み込む\r
- var loader = new jQuery.ajax({\r
- url : logXmlUrl + '?time=' + urlSuffix,\r
- method : "GET",\r
- error : showError,\r
- success : function(xmlData) {\r
- // ファイルパスの要素のみを抽出する\r
- var separateTag = xmlData.getElementsByTagName("file");\r
- var urls = new Array(separateTag.length);\r
-\r
- // すべてのファイルパスを配列に格納する\r
- for (var i = 0; i < separateTag.length; i++) {\r
- // "path"ノードの値を格納\r
- urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
- }\r
-\r
- // セマフォを初期化\r
- fetchEntriesSemaphore.init();\r
- fetchEntriesSemaphore.urls = urls;\r
- fetchEntriesSemaphore.count = urls.length;\r
-\r
- // ファイルパス配列に格納されているすべての記事に対し、探索を開始する\r
- for (var i = 0; i < separateTag.length; i++) {\r
- // ファイルパス配列の要素からリクエストを生成し、対象データをロードする\r
- var xhr = new jQuery.ajax({\r
- url : urls[i],\r
- method : "GET",\r
- success : fetchHashEntries\r
- });\r
- fetchEntriesSemaphore.xhrs.push(xhr);\r
- }\r
- }\r
- });\r
-}\r
-\r
-/**\r
- * URLハッシュ検索用のjQueryコールバック関数\r
- */\r
-function fetchHashEntries(xmlData) {\r
- // 既に検索結果が算出されていた場合は、何もしない\r
- if (fetchEntriesSemaphore.buf > 0) {\r
- return true;\r
- }\r
-\r
- // ハッシュを取得\r
- var urlhash = getHashFromUrl();\r
-\r
- // entry要素のみを切り出す\r
- var entries = xmlData.getElementsByTagName("entry");\r
-\r
- for (var i = 0; i < entries.length; i++) {\r
- // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う\r
- var entry = new Entry(entries[i]);\r
-\r
- // idの値と比較を行う\r
- if (urlhash == entry.id) {\r
- // 一致した場合は該当記事を表示する\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
- generatePanel(entry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-\r
- fetchEntriesSemaphore.buf.push(entry);\r
- return true;\r
- }\r
-\r
- // セマフォのカウンタを減少させます (Ajaxとの同期のため)\r
- fetchEntriesSemaphore.count--;\r
-\r
- // 最後のファイルまで探索しても記事が見つからなかった場合はエラーを表示します。\r
- if (fetchEntriesSemaphore.count == 0) {\r
- var entries = fetchEntriesSemaphore.buf;\r
-\r
- if (entries.length == 0) {\r
- notFoundError();\r
- return false;\r
- }\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * 渡された文字列と一致するfeed1.0:id(sha-1)要素を持った記事を検索し、表示します\r
- * @param {String} urlhash feed1.0:id(sha-1)要素と一致する文字列\r
- */\r
-function searchIdMode(urlhash) {\r
- // ロードエフェクト表示\r
- loadingEffect();\r
-\r
- // ログXMLファイルを読み込む\r
- var loader = new jQuery.ajax({\r
- url : logXmlUrl + '?time=' + urlSuffix,\r
- method : "GET",\r
- error : showError,\r
- success : function(xmlData) {\r
- // ファイルパスの要素のみを抽出する\r
- var separateTag = xmlData.getElementsByTagName("file");\r
- var urls = new Array(separateTag.length);\r
-\r
- // すべてのファイルパスを配列に格納する\r
- for (var i = 0; i < separateTag.length; i++) {\r
- // "path"ノードの値を格納\r
- urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
- }\r
-\r
- // セマフォを初期化\r
- fetchEntriesSemaphore.init();\r
- fetchEntriesSemaphore.urls = urls;\r
- fetchEntriesSemaphore.count = urls.length;\r
-\r
- // ファイルパス配列に格納されているすべての記事に対し、探索を開始する\r
- for (var i = 0; i < separateTag.length; i++) {\r
- // ファイルパス配列の要素からリクエストを生成し、対象データをロードする\r
- var xhr = new jQuery.ajax({\r
- url : urls[i],\r
- method : "GET",\r
- success : fetchIdEntries\r
- });\r
- fetchEntriesSemaphore.xhrs.push(xhr);\r
- }\r
- }\r
- });\r
-}\r
-\r
-/**\r
- * ID検索用のjQueryコールバック関数\r
- */\r
-function fetchIdEntries(xmlData) {\r
- // 既に検索結果が算出されていた場合は、何もしない\r
- if (fetchEntriesSemaphore.buf > 0) {\r
- return true;\r
- }\r
-\r
- // IDを取得\r
- var id = getParamFromUrl("id");\r
-\r
- // entry要素のみを切り出す\r
- var entries = xmlData.getElementsByTagName("entry");\r
-\r
- for (var i = 0; i < entries.length; i++) {\r
- // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う\r
- var entry = new Entry(entries[i]);\r
-\r
- // idの値と比較を行う\r
- if (id == CryptoJS.SHA1(entry.id).toString()) {\r
- // 一致した場合は該当記事を表示する\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
- generatePanel(entry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-\r
- fetchEntriesSemaphore.buf.push(entry);\r
- return true;\r
- }\r
-\r
- // セマフォのカウンタを減少させます (Ajaxとの同期のため)\r
- fetchEntriesSemaphore.count--;\r
-\r
- // 最後のファイルまで探索しても記事が見つからなかった場合はエラーを表示します。\r
- if (fetchEntriesSemaphore.count == 0) {\r
- var entries = fetchEntriesSemaphore.buf;\r
-\r
- if (entries.length == 0) {\r
- notFoundError();\r
- return false;\r
- }\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * 渡された文字列と一致するfeed1.0:category:termタグ要素を持った記事を検索し、表示します\r
- * @param {String} urlhash feed1.0:category:term要素と一致する文字列\r
- */\r
-function searchTagMode(tag) {\r
- // ロードエフェクト表示\r
- loadingEffect();\r
-\r
- // ログXMLファイルを読み込む\r
- var loader = new jQuery.ajax({\r
- url : logXmlUrl + '?time=' + urlSuffix,\r
- method : "GET",\r
- error : showError,\r
- success : function(xmlData) {\r
- // ファイルパスの要素のみを抽出する\r
- var separateTag = xmlData.getElementsByTagName("file");\r
- var urls = new Array(separateTag.length);\r
-\r
- // すべてのファイルパスを配列に格納する\r
- for (var i = 0; i < separateTag.length; i++) {\r
- // "path"ノードの値を格納\r
- urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
- }\r
-\r
- // セマフォを初期化\r
- fetchEntriesSemaphore.init();\r
- fetchEntriesSemaphore.urls = urls;\r
- fetchEntriesSemaphore.count = urls.length;\r
-\r
- // ファイルパス配列に格納されているすべての記事に対し、探索を開始する\r
- for (var i = 0; i < separateTag.length; i++) {\r
- // ファイルパス配列の要素からリクエストを生成し、対象データをロードする\r
- var xhr = new jQuery.ajax({\r
- url : urls[i],\r
- method : "GET",\r
- success : fetchTagEntries\r
- });\r
- fetchEntriesSemaphore.xhrs.push(xhr);\r
- }\r
- }\r
- });\r
-}\r
-\r
-/**\r
- * タグ検索用のjQueryコールバック関数\r
- */\r
-function fetchTagEntries(xmlData) {\r
- // 既に記事の表示が行われている場合、何も実施しない\r
- if (fetchEntriesSemaphore.buf.length > 0) {\r
- return true;\r
- }\r
-\r
- // タグを取得する\r
- var tag = getParamFromUrl("tag");\r
-\r
- // entry要素のみを切り出す\r
- var entries = xmlData.getElementsByTagName("entry");\r
-\r
- for (var j = 0; j < entries.length; j++) {\r
- var entry = new Entry(entries[j]);\r
-\r
- for (var k = 0; k < entry.category.length; k++) {\r
- // タグのIDが一致したら格納\r
- if (tag == entry.category.eq(k).attr("term")) {\r
- // entryを格納する\r
- fetchEntriesSemaphore.buf.push(entry);\r
- }\r
- }\r
- }\r
-\r
- // セマフォのカウンタを減少させます (Ajaxとの同期のため)\r
- fetchEntriesSemaphore.count--;\r
-\r
- if (fetchEntriesSemaphore.count == 0) {\r
- var entries = fetchEntriesSemaphore.buf;\r
-\r
- if (entries.length == 0) {\r
- notFoundError();\r
- return false;\r
- }\r
-\r
- // entryをidでソート\r
- entries = entries.sort(function(a, b) {\r
- a = a.id;\r
- b = b.id;\r
- return a > b ? -1 : a < b ? 1 : 0;\r
- });\r
-\r
- loadedEntries = entries;\r
-\r
- // 表示ロジック呼び出し\r
- showEntriesRange(showLength, 0);\r
- }\r
-}\r
-\r
-/**\r
- * 検索結果を分割して表示します\r
- * class - div.feedblog_pager, ul.feedblog_pager, li.feedblog_pager などなど\r
- * @param {int} showLength 一回の画面に表示する記事数\r
- * @param {int} startIndex 表示を開始する記事のインデックス\r
- */\r
-function showEntriesRange(showLength, startIndex) {\r
- // メモリ上から記事データをロード\r
- var entries = loadedEntries;\r
-\r
- // 表示インデックスが範囲外の場合はエラーパネルを表示して終了\r
- if (startIndex < 0 || (entries.length <= startIndex && entries.length != 0)) {\r
- showError();\r
- return;\r
- }\r
-\r
- var stringBuffer = [];\r
-\r
- // リミッターを設定する\r
- var loopLimit = (showLength + startIndex > entries.length) ? entries.length : showLength + startIndex;\r
- var indexShowEntries = loopLimit + 1;\r
-\r
- // 情報メニュー表示用バッファです\r
- var menuInfoBuffer = [];\r
- menuInfoBuffer.push("<div class='feedblog_pager_shownumber'>");\r
- var viewStartIndex = 0;\r
- entries.length == 0 ? viewStartIndex = 0 : viewStartIndex = startIndex + 1;\r
- menuInfoBuffer.push(viewStartIndex + "件~" + loopLimit + "件(全" + entries.length + "件)目の記事を表示中<br/>");\r
- menuInfoBuffer.push("</div>");\r
-\r
- // ページ移動メニュー表示用バッファです\r
- var menuMoveBuffer = [];\r
- menuMoveBuffer.push("<ul class='feedblog_pager'>");\r
-\r
- // ブランクエリアを挟む\r
- menuMoveBuffer.push("<li class='feedblog_pager_blank'></li>");\r
-\r
- // 左パネルの表示制御\r
- if (startIndex - showLength >= 0) {\r
- menuMoveBuffer.push("\<li class='feedblog_pager_goback'><span class='feedblog_pager_goback' onclick='showEntriesRange(" + showLength + ", " + (startIndex - showLength) + "); return false;'>\< 前の" + showLength + "件を表示</span\></li>");\r
- } else {\r
- menuMoveBuffer.push("\<li class='feedblog_pager_goback'>\< 前の" + showLength + "件を表示</a\></li>");\r
- }\r
-\r
- // 中央のパネルの表示制御\r
- menuMoveBuffer.push("<li class='feedblog_pager_center'>[ ");\r
- var menuNumbers = Math.ceil(entries.length / showLength);\r
- for ( i = 0; i < menuNumbers; i++) {\r
- if (startIndex / showLength == i) {\r
- menuMoveBuffer.push(i + " ");\r
- } else {\r
- menuMoveBuffer.push("<span class='feedblog_pager_center' onclick='showEntriesRange(" + showLength + ", " + (i * showLength) + "); return false;'>");\r
- menuMoveBuffer.push(i);\r
- menuMoveBuffer.push("</span> ");\r
- }\r
- }\r
- menuMoveBuffer.push("]</li>");\r
-\r
- // 右パネルの表示制御\r
- if (entries.length > startIndex + showLength) {\r
- menuMoveBuffer.push("\<li class='feedblog_pager_gonext'><span class='feedblog_pager_gonext' onclick='showEntriesRange(" + showLength + ", " + (startIndex + showLength) + "); return false;'>\次の" + showLength + "件を表示 \></span\></li>");\r
- } else {\r
- menuMoveBuffer.push("\<li class='feedblog_pager_gonext'>次の" + showLength + "件を表示 \></li>");\r
- }\r
-\r
- // ブランクエリアを挟む\r
- menuMoveBuffer.push("<li class='feedblog_pager_blank'></li>");\r
-\r
- menuMoveBuffer.push("</ul>");\r
-\r
- // メニューを結合してパネル(前方)に組み込みます\r
- stringBuffer.push("<div class='feedblog_pager_wrapper'>");\r
- stringBuffer.push(menuInfoBuffer.join(""));\r
- stringBuffer.push(menuMoveBuffer.join(""));\r
- stringBuffer.push("</div>");\r
-\r
- // 記事描画部分のパネルを生成します\r
- for (var i = startIndex; i < loopLimit; i++) {\r
- stringBuffer.push('<div class="feedblog_drawpanel" id="feedblog_drawpanel');\r
- stringBuffer.push(i);\r
- stringBuffer.push('"><div class="feedblog_drawitem" id="feedblog_drawitem');\r
- stringBuffer.push(i);\r
- stringBuffer.push('"><\/div><\/div>');\r
- }\r
-\r
- // メニューを結合してパネル(後方)に組み込みます\r
- stringBuffer.push("<div class='feedblog_pager_wrapper'>");\r
- stringBuffer.push(menuMoveBuffer.join(""));\r
- stringBuffer.push(menuInfoBuffer.join(""));\r
- stringBuffer.push("</div>");\r
-\r
- $("#feedblog_writearea").html(stringBuffer.join(""));\r
-\r
- for (var i = startIndex; i < loopLimit; i++) {\r
- // 各要素をオブジェクトに格納します\r
- var entry = entries[i];\r
-\r
- // すべてのパネルをオープン状態で生成します\r
- generatePanel(entry, "feedblog_drawitem" + i, "feedblog_drawpanel" + i, false);\r
- }\r
-}\r
-\r
+/**
+ * FeedBlog CoreScript
+ *
+ * @copyright 2013 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 4.2.1.0
+ */
+
+// ブログ本体のHTMLファイルのURL
+var mainPageUrl;
+
+// 検索用ページURL
+var searchPageUrl;
+
+// 最新の記事を示すパスへの文字列
+var latestXml;
+
+// ログのリストが書かれたXMLのファイルパス
+var logXmlUrl;
+
+// 一画面あたりの表示記事数
+var showLength;
+
+/**
+ * XMLファイルから読み込んだファイルのバリデートモード
+ * 0 = 改行コード部分に<br/>を挿入
+ * 1 = 改行コード部分に<br/>を挿入しない
+ */
+var validateMode;
+
+// 検索結果をメモリ上に保持する変数
+var loadedEntries;
+
+// fetchEntries 用のセマフォ
+var fetchEntriesSemaphore = new Semaphore();
+
+// URL末尾用文字列(スクリプトを開いた瞬間のミリ秒を記録)
+var urlSuffix;
+
+/**
+ * 記事を実際に生成します。この部分を編集することでデザインを変更可能です。
+ * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト
+ * @param {String} drawitem 「本文」を描画すべきパネルのDIV要素のid
+ * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを最終的に描画すべきパネルのDIV要素のid
+ * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か
+ */
+function generatePanel(entry, drawitem, renderto, closed) {
+ // プラグインを実行
+ if ( typeof ($("#" + renderto).feedblog_contents_plugin) == "function") {
+ $("#" + renderto).feedblog_contents_plugin({
+ entry : entry
+ });
+ }
+
+ // HTML用の配列を用意する
+ var htmlBuffer = [];
+
+ // 内部的に描画先IDを生成
+ var feedblogContentId = "" + renderto + "_content_div";
+
+ // 各要素をオブジェクトに描画
+ $("#" + drawitem).html(entry.content);
+
+ // ヘッダパネルを生成 class= .feedblog_header
+ htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +
+
+ // 本体記事を作成 class= .feedblog_content
+ "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");
+
+ // 最終描画実施
+ $("#" + renderto).html(htmlBuffer.join(""));
+}
+
+/**
+ * システム表示画面を実際に生成します。この部分を編集することでデザインを変更可能です。
+ * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト
+ * @param {String} drawitem パネルの本文を格納したDIV要素のid
+ * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを焼き付けるDIV要素のid
+ * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か
+ */
+function generateSystemPanel(entry, drawitem, renderto, closed) {
+ // HTMLを生成する
+ var htmlBuffer = [];
+
+ // 描画先IDを生成
+ var feedblogContentId = "" + renderto + "_content_div";
+
+ // 各要素をオブジェクトに描画
+ $("#" + drawitem).html(entry.content);
+
+ // ヘッダパネルを生成 class= .feedblog_header
+ htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +
+
+ // 本体記事を作成 class= .feedblog_content
+ "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");
+
+ $("#" + renderto).html(htmlBuffer.join(""));
+}
+
+/**
+ * 全ての定数を取得・セットします
+ */
+function initialize() {
+ // 初期値をhiddenパラメータより読み込みます
+ mainPageUrl = $("#feedblog_mainpageurl").val();
+ searchPageUrl = $("#feedblog_searchpageurl").val();
+ latestXml = $("#feedblog_latestxml").val();
+ logXmlUrl = $("#feedblog_loglistxmlurl").val();
+ showLength = parseInt($("#feedblog_showlength").val());
+ if (isNaN(showLength)) {
+ showLength = 1;
+ }
+ validateMode = $("#feedblog_validatemode").val();
+
+ // 初期値を設定します
+ urlSuffix = +new Date();
+
+ // 必要な環境を確認します
+ var errorBuf = [];
+ // 変数確認
+ if (mainPageUrl === undefined) {
+ errorBuf.push("設定値「feedblog_mainpageurl」が欠落しています。");
+ }
+ if (searchPageUrl === undefined) {
+ errorBuf.push("設定値「feedblog_searchpageurl」が欠落しています。");
+ }
+ if (latestXml === undefined) {
+ errorBuf.push("設定値「feedblog_latestxml」が欠落しています。");
+ }
+ if (logXmlUrl === undefined) {
+ errorBuf.push("設定値「feedblog_loglistxmlurl」が欠落しています。");
+ }
+ if (showLength === undefined) {
+ errorBuf.push("設定値「feedblog_showlength」が欠落しています。");
+ }
+ if (validateMode === undefined) {
+ errorBuf.push("設定値「feedblog_validatemode」が欠落しています。");
+ }
+ // SHA-1関数確認
+ try {
+ if ( typeof (CryptoJS.SHA1) != "function") {
+ errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");
+ }
+ } catch (ex) {
+ errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");
+ }
+
+ // 描画エリアチェック
+ if ($("#feedblog_writearea").length == 0) {
+ errorBuf.push("描画エリア「feedblog_writearea」が存在しません。");
+ }
+ if ($("#feedblog_logselecter").length == 0) {
+ errorBuf.push("描画エリア「feedblog_logselecter」が存在しません。");
+ }
+
+ // エラーがある場合は以降の処理を継続しない
+ if (errorBuf.length > 0) {
+ alert("初期設定値に誤りがあります。\n詳細:\n" + errorBuf.join("\n"));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * jQueryへのイベント登録です。すべてのDOMが利用可能になった時点で実行されます。
+ */
+$(document).ready(function() {
+ // 初期処理を実施
+ if (!initialize()) {
+ return false;
+ }
+
+ // 制御に必要な各種パラメタを取得する
+ var tag = getParamFromUrl("tag");
+ var id = getParamFromUrl("id");
+ var urlhash = getHashFromUrl();
+
+ // ハッシュが空か、ハッシュ形式の正規表現に一致しないようなら通常モードで実行
+ if (urlhash.length == 0 && tag.length == 0 && id.length == 0) {
+ fullWriteMode(latestXml);
+ logXMLLoader();
+ } else if (urlhash.length == 0 && id.length == 0) {
+ // タグが指定されているのでタグ探索モード
+ searchTagMode(tag, true);
+ logXMLLoader();
+ } else if (urlhash.length == 0) {
+ // IDが指定されているのでID探索モード
+ searchIdMode(id);
+ logXMLLoader();
+ } else {
+ // ハッシュ形式の正規表現に一致したら探索モード
+ searchHashMode(urlhash);
+ logXMLLoader();
+ }
+});
+
+/**
+ * jQueryでのパネル開閉を制御します
+ */
+function closePanel(id) {
+ $("#" + id).slideToggle();
+}
+
+/**
+ * 記事クラス
+ * @param {Object} obj entry 要素の DOM オブジェクト
+ */
+function Entry(obj) {
+ this.title = $("title:first", obj).text();
+ if (this.title == "")
+ requiredElementError(obj, "title");
+ this.title = validateText(this.title);
+ this.content = $("content:first", obj).text();
+ this.content = validateText(this.content);
+ 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);
+ this.category = $("category", obj);
+}
+
+/**
+ * システム用記事クラス
+ * @param {Object} obj entry 要素の DOM オブジェクト
+ */
+function SystemEntry(obj) {
+ this.title = $("title:first", obj).text();
+ this.title = validateText(this.title);
+ this.content = $("content:first", obj).text();
+ this.content = validateText(this.content);
+ this.id = $("id:first", obj).text();
+ this.date = $("updated:first", obj).text();
+ this.date = validateData(this.date);
+ this.category = $("category", obj);
+}
+
+/**
+ * 呼び出すとDIV:id名:feedblog_writearea上のHTMLを削除し、ロードエフェクトを表示します
+ */
+function loadingEffect() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // ロード表示用のパネルを生成
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "Now Loading .....";
+ systemEntry.content = '<br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);
+}
+
+/**
+ * 呼び出すとDIV:id名:feedblog_writearea上の先頭に、処理中の記事を表示します
+ */
+function executingEffect() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel_info" class="feedblog_drawpanel"><div id="feedblog_drawitem_info" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // ロード表示用のパネルを生成
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "検索処理中";
+ systemEntry.content = '<br/>該当するタグに属する記事を検索中です。<br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem_info", "feedblog_drawpanel_info", false);
+}
+
+/**
+ * 呼び出すとDIV:id名:feedblog_drawpanel_info上に、処理完了の記事を表示します
+ */
+function executingEffectComplete() {
+ $("#feedblog_drawpanel_info").html('<div id="feedblog_drawitem_info" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // 検索処理完了用のパネルを生成
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "検索完了";
+ systemEntry.content = '<br/>検索処理が完了しました。(該当記事:' + fetchEntriesSemaphore.entryCount + '件)<br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem_info", "feedblog_drawpanel_info", false);
+
+ // 状況に応じて「全件表示」パネルを生成
+ if (fetchEntriesSemaphore.showLimit == true && fetchEntriesSemaphore.entryIndex != fetchEntriesSemaphore.entryCount) {
+ $("#feedblog_writearea").html($("#feedblog_writearea").html() + '<div id="feedblog_drawpanel_abort" class="feedblog_drawpanel"><div id="feedblog_drawitem_abort" class="feedblog_drawitem"> <\/div><\/div>');
+ systemEntry = new SystemEntry();
+ systemEntry.title = "以降、" + (fetchEntriesSemaphore.entryCount - fetchEntriesSemaphore.entryIndex) + "件の記事が省略されています。";
+ systemEntry.content = '<br/><a style="cursor: pointer;" onclick="javascript: var tag = getParamFromUrl(\'tag\'); searchTagMode(tag, false);">全ての記事を表示する。</a><br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem_abort", "feedblog_drawpanel_abort", false);
+ }
+}
+
+/**
+ * 記事データのエラー時の処理を行います
+ */
+function showError() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // エラー内容をパネルに描画
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "エラー";
+ var errorContent = [];
+ errorContent.push('<br/>記事ファイル(XML)の取得に失敗しました。以下のような原因が考えられます。<br/><br/>');
+ errorContent.push('・設定値「feedblog_latestxml」に正しいパスが設定されていない。<br/>');
+ errorContent.push('・設定値「feedblog_loglistxmlurl」に正しいパスが設定されていない。<br/>');
+ errorContent.push('・ローカル環境で起動している(必ずサーバにアップロードして実行してください)。<br/>');
+ errorContent.push('<br/>');
+ systemEntry.content = errorContent.join("\n");
+ generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);
+
+ // 結果表示エリアをリセット
+ $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");
+}
+
+/**
+ * 記事データのエラー時の処理を行います
+ */
+function notFoundError() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // エラー内容をパネルに描画
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "検索失敗";
+ systemEntry.content = '<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);
+}
+
+/**
+ * 記事データのエラー時の処理を行います
+ */
+function requiredElementError(parent, name) {
+ alert(parent.ownerDocument.URL + ": 必須な要素 " + name + " が存在しないか空な " + parent.tagName + " 要素が存在します");
+}
+
+function xmlAttrContentEscape(str) {
+ return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").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;
+}
+
+/**
+ * URLからパラメタを取得するための関数
+ */
+function getParamFromUrl(paramName) {
+ // GETパラメタよりタグを取得する
+ var tag = "";
+ if (location.search.length > 1) {
+ var queries = location.search.substring(1).split('&');
+ for (var i = 0; i < queries.length; i++) {
+ if (("" + queries[i].split('=')[0]) == paramName) {
+ tag = "" + queries[i].split('=')[1];
+ }
+ }
+ }
+
+ return tag;
+}
+
+/**
+ * URLからハッシュを取得するための関数
+ */
+function getHashFromUrl() {
+ return "" + location.hash.substring(1);
+}
+
+/**
+ * 長い順に並べるための比較関数です
+ * @param {String} a 比較対象(1)
+ * @param {String} b 比較対象(2)
+ */
+function compareLengthDecrease(a, b) {
+ a = a.length;
+ b = b.length;
+ return a > b ? -1 : a < b ? 1 : 0;
+}
+
+/**
+ * セマフォ制御用のオブジェクトです
+ */
+function Semaphore() {
+ this.id = null;
+ this.count = 0;
+ this.buf = [];
+ this.xhrs = [];
+}
+
+/**
+ * セマフォ初期化用の関数です
+ */
+Semaphore.prototype.init = function() {
+ while (this.xhrs.length > 0) {
+ this.xhrs.shift().abort();
+ }
+ this.id = Math.random();
+ this.count = 0;
+ this.buf = [];
+};
+
+/**
+ * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します
+ * class - .feedblog_logform, .feedblog_logselecter
+ */
+function logXMLLoader() {
+ // ログ用のXMLを読み込みます
+ jQuery.ajax({
+ url : logXmlUrl + '?time=' + urlSuffix,
+ method : "GET",
+ error : showError,
+ success : function(xmlData) {
+ var separateTag = xmlData.getElementsByTagName("file");
+
+ // 読み込んだ要素をStoreに格納して表示
+ var boxBuffer = [];
+ boxBuffer.push("<form class='feedblog_logselecter' name='feedblog_logform'><select class='feedblog_logselecter' id='feedblog_logbox' onchange='fullWriteMode(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>");
+
+ // コンボボックス要素を生成
+ $("#feedblog_logselecter").html(boxBuffer.join(""));
+ }
+ });
+}
+
+/**
+ * 記事のデータが記述されたXMLデータを読み込むロジックを生成します
+ * @param {String} fileName 読み込み記事のデータが記述されているXMLファイルのパス
+ */
+function fullWriteMode(fileName) {
+ // ロードエフェクトに切り替え
+ loadingEffect();
+
+ var url = fileName;
+
+ // 記事をロードします
+ var loader = new jQuery.ajax({
+ url : url + '?time=' + urlSuffix,
+ method : "GET",
+ success : function(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);
+ },
+ error : showError
+ });
+}
+
+/**
+ * 渡された文字列と一致するfeed1.0:updated要素を持った記事を検索し、表示します
+ * @param {String} urlhash feed1.0:updated要素と一致する文字列
+ */
+function searchHashMode(urlhash) {
+ // ロードエフェクト表示
+ loadingEffect();
+
+ // ログXMLファイルを読み込む
+ var loader = new jQuery.ajax({
+ url : logXmlUrl + '?time=' + urlSuffix,
+ method : "GET",
+ error : showError,
+ success : function(xmlData) {
+ // ファイルパスの要素のみを抽出する
+ var separateTag = xmlData.getElementsByTagName("file");
+ var urls = new Array(separateTag.length);
+
+ // すべてのファイルパスを配列に格納する
+ for (var i = 0; i < separateTag.length; i++) {
+ // "path"ノードの値を格納
+ urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
+ }
+
+ // セマフォを初期化
+ fetchEntriesSemaphore.init();
+ fetchEntriesSemaphore.urls = urls;
+ fetchEntriesSemaphore.count = urls.length;
+
+ // ファイルパス配列に格納されているすべての記事に対し、探索を開始する
+ for (var i = 0; i < separateTag.length; i++) {
+ // ファイルパス配列の要素からリクエストを生成し、対象データをロードする
+ var xhr = new jQuery.ajax({
+ url : urls[i],
+ method : "GET",
+ success : fetchHashEntries
+ });
+ fetchEntriesSemaphore.xhrs.push(xhr);
+ }
+ }
+ });
+}
+
+/**
+ * URLハッシュ検索用のjQueryコールバック関数
+ */
+function fetchHashEntries(xmlData) {
+ // 既に検索結果が算出されていた場合は、何もしない
+ if (fetchEntriesSemaphore.buf > 0) {
+ return true;
+ }
+
+ // ハッシュを取得
+ var urlhash = getHashFromUrl();
+
+ // entry要素のみを切り出す
+ var entries = xmlData.getElementsByTagName("entry");
+
+ for (var i = 0; i < entries.length; i++) {
+ // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う
+ var entry = new Entry(entries[i]);
+
+ // idの値と比較を行う
+ if (urlhash == entry.id) {
+ // 一致した場合は該当記事を表示する
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+ generatePanel(entry, "feedblog_drawitem", "feedblog_drawpanel", false);
+
+ fetchEntriesSemaphore.buf.push(entry);
+ return true;
+ }
+ }
+
+ // セマフォのカウンタを減少させます (Ajaxとの同期のため)
+ fetchEntriesSemaphore.count--;
+
+ // 最後のファイルまで探索しても記事が見つからなかった場合はエラーを表示します。
+ if (fetchEntriesSemaphore.count == 0) {
+ var entries = fetchEntriesSemaphore.buf;
+
+ if (entries.length == 0) {
+ notFoundError();
+ return false;
+ }
+ }
+}
+
+/**
+ * 渡された文字列と一致するfeed1.0:id(sha-1)要素を持った記事を検索し、表示します
+ * @param {String} urlhash feed1.0:id(sha-1)要素と一致する文字列
+ */
+function searchIdMode(urlhash) {
+ // ロードエフェクト表示
+ loadingEffect();
+
+ // ログXMLファイルを読み込む
+ var loader = new jQuery.ajax({
+ url : logXmlUrl + '?time=' + urlSuffix,
+ method : "GET",
+ error : showError,
+ success : function(xmlData) {
+ // ファイルパスの要素のみを抽出する
+ var separateTag = xmlData.getElementsByTagName("file");
+ var urls = new Array(separateTag.length);
+
+ // すべてのファイルパスを配列に格納する
+ for (var i = 0; i < separateTag.length; i++) {
+ // "path"ノードの値を格納
+ urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
+ }
+
+ // セマフォを初期化
+ fetchEntriesSemaphore.init();
+ fetchEntriesSemaphore.urls = urls;
+ fetchEntriesSemaphore.count = urls.length;
+
+ // ファイルパス配列に格納されているすべての記事に対し、探索を開始する
+ for (var i = 0; i < separateTag.length; i++) {
+ // ファイルパス配列の要素からリクエストを生成し、対象データをロードする
+ var xhr = new jQuery.ajax({
+ url : urls[i],
+ method : "GET",
+ success : fetchIdEntries
+ });
+ fetchEntriesSemaphore.xhrs.push(xhr);
+ }
+ }
+ });
+}
+
+/**
+ * ID検索用のjQueryコールバック関数
+ */
+function fetchIdEntries(xmlData) {
+ // 既に検索結果が算出されていた場合は、何もしない
+ if (fetchEntriesSemaphore.buf > 0) {
+ return true;
+ }
+
+ // IDを取得
+ var id = getParamFromUrl("id");
+
+ // entry要素のみを切り出す
+ var entries = xmlData.getElementsByTagName("entry");
+
+ for (var i = 0; i < entries.length; i++) {
+ // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う
+ var entry = new Entry(entries[i]);
+
+ // idの値と比較を行う
+ if (id == CryptoJS.SHA1(entry.id).toString()) {
+ // 一致した場合は該当記事を表示する
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+ generatePanel(entry, "feedblog_drawitem", "feedblog_drawpanel", false);
+
+ fetchEntriesSemaphore.buf.push(entry);
+ return true;
+ }
+
+ }
+
+ // セマフォのカウンタを減少させます (Ajaxとの同期のため)
+ fetchEntriesSemaphore.count--;
+
+ // 最後のファイルまで探索しても記事が見つからなかった場合はエラーを表示します。
+ if (fetchEntriesSemaphore.count == 0) {
+ var entries = fetchEntriesSemaphore.buf;
+
+ if (entries.length == 0) {
+ notFoundError();
+ return false;
+ }
+ }
+
+}
+
+/**
+ * 渡された文字列と一致するfeed1.0:category:termタグ要素を持った記事を検索し、表示します
+ * @param {String} tag feed1.0:category:term要素と一致する文字列
+ * @param {boolean} showLimit 省略表示をする場合はtrue,全件表示する場合はfalse
+ */
+function searchTagMode(tag, showLimit) {
+ // 処理中エフェクト表示
+ executingEffect();
+
+ // ログXMLファイルを読み込む
+ var loader = new jQuery.ajax({
+ url : logXmlUrl + '?time=' + urlSuffix,
+ method : "GET",
+ error : showError,
+ success : function(xmlData) {
+ // ファイルパスの要素のみを抽出する
+ var separateTag = xmlData.getElementsByTagName("file");
+ var urls = new Array(separateTag.length);
+
+ // すべてのファイルパスを配列に格納する
+ for (var i = 0; i < separateTag.length; i++) {
+ // "path"ノードの値を格納
+ urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
+ }
+
+ // セマフォを初期化
+ fetchEntriesSemaphore.init();
+ fetchEntriesSemaphore.urls = urls;
+ fetchEntriesSemaphore.count = urls.length;
+ fetchEntriesSemaphore.entryIndex = 0;
+ fetchEntriesSemaphore.entryCount = 0;
+ fetchEntriesSemaphore.showLimit = showLimit;
+
+ // ファイルパス配列に格納されているすべての記事に対し、探索を開始する
+ for (var i = 0; i < separateTag.length; i++) {
+ // ファイルパス配列の要素からリクエストを生成し、対象データをロードする
+ var xhr = new jQuery.ajax({
+ url : urls[i],
+ method : "GET",
+ success : fetchTagEntries
+ });
+ fetchEntriesSemaphore.xhrs.push(xhr);
+ }
+ }
+ });
+}
+
+/**
+ * タグ検索用のjQueryコールバック関数
+ */
+function fetchTagEntries(xmlData) {
+ // 既に記事の表示が行われている場合、何も実施しない
+ if (fetchEntriesSemaphore.buf.length > 0) {
+ return true;
+ }
+
+ // タグを取得する
+ var tag = getParamFromUrl("tag");
+
+ // entry要素のみを切り出す
+ var entries = xmlData.getElementsByTagName("entry");
+
+ for (var j = 0; j < entries.length; j++) {
+ var entry = new Entry(entries[j]);
+
+ for (var k = 0; k < entry.category.length; k++) {
+ // タグのIDが一致したら格納
+ if (tag == entry.category.eq(k).attr("term")) {
+ // 表示数限界を超えていた場合、表示処理を実施しない
+ if (fetchEntriesSemaphore.showLimit == true && fetchEntriesSemaphore.entryIndex >= showLength) {
+ // 検索一致件数をカウント
+ fetchEntriesSemaphore.entryCount++;
+ } else {
+ // 検索一致件数をカウント
+ fetchEntriesSemaphore.entryCount++;
+ // entryを格納する
+ fetchEntriesSemaphore.entryIndex = showEntriesAdd(entry, fetchEntriesSemaphore.entryIndex);
+ }
+ }
+ }
+ }
+
+ // セマフォのカウンタを減少させます (Ajaxとの同期のため)
+ fetchEntriesSemaphore.count--;
+
+ if (fetchEntriesSemaphore.count == 0) {
+ if (fetchEntriesSemaphore.entryIndex == 0) {
+ notFoundError();
+ return false;
+ } else {
+ executingEffectComplete();
+ return true;
+ }
+ }
+}
+
+/**
+ * 検索結果を分割して表示します
+ * class - div.feedblog_pager, ul.feedblog_pager, li.feedblog_pager などなど
+ * @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("<div class='feedblog_pager_shownumber'>");
+ var viewStartIndex = 0;
+ entries.length == 0 ? viewStartIndex = 0 : viewStartIndex = startIndex + 1;
+ menuInfoBuffer.push(viewStartIndex + "件~" + loopLimit + "件(全" + entries.length + "件)目の記事を表示中<br/>");
+ menuInfoBuffer.push("</div>");
+
+ // ページ移動メニュー表示用バッファです
+ var menuMoveBuffer = [];
+ menuMoveBuffer.push("<ul class='feedblog_pager'>");
+
+ // ブランクエリアを挟む
+ menuMoveBuffer.push("<li class='feedblog_pager_blank'></li>");
+
+ // 左パネルの表示制御
+ if (startIndex - showLength >= 0) {
+ menuMoveBuffer.push("\<li class='feedblog_pager_goback'><span class='feedblog_pager_goback' onclick='showEntriesRange(" + showLength + ", " + (startIndex - showLength) + "); return false;'>\< 前の" + showLength + "件を表示</span\></li>");
+ } else {
+ menuMoveBuffer.push("\<li class='feedblog_pager_goback'>\< 前の" + showLength + "件を表示</a\></li>");
+ }
+
+ // 中央のパネルの表示制御
+ menuMoveBuffer.push("<li class='feedblog_pager_center'>[ ");
+ var menuNumbers = Math.ceil(entries.length / showLength);
+ for ( i = 0; i < menuNumbers; i++) {
+ if (startIndex / showLength == i) {
+ menuMoveBuffer.push(i + " ");
+ } else {
+ menuMoveBuffer.push("<span class='feedblog_pager_center' onclick='showEntriesRange(" + showLength + ", " + (i * showLength) + "); return false;'>");
+ menuMoveBuffer.push(i);
+ menuMoveBuffer.push("</span> ");
+ }
+ }
+ menuMoveBuffer.push("]</li>");
+
+ // 右パネルの表示制御
+ if (entries.length > startIndex + showLength) {
+ menuMoveBuffer.push("\<li class='feedblog_pager_gonext'><span class='feedblog_pager_gonext' onclick='showEntriesRange(" + showLength + ", " + (startIndex + showLength) + "); return false;'>\次の" + showLength + "件を表示 \></span\></li>");
+ } else {
+ menuMoveBuffer.push("\<li class='feedblog_pager_gonext'>次の" + showLength + "件を表示 \></li>");
+ }
+
+ // ブランクエリアを挟む
+ menuMoveBuffer.push("<li class='feedblog_pager_blank'></li>");
+
+ menuMoveBuffer.push("</ul>");
+
+ // メニューを結合してパネル(前方)に組み込みます
+ stringBuffer.push("<div class='feedblog_pager_wrapper'>");
+ stringBuffer.push(menuInfoBuffer.join(""));
+ stringBuffer.push(menuMoveBuffer.join(""));
+ stringBuffer.push("</div>");
+
+ // 記事描画部分のパネルを生成します
+ for (var i = startIndex; i < loopLimit; i++) {
+ stringBuffer.push('<div class="feedblog_drawpanel" id="feedblog_drawpanel');
+ stringBuffer.push(i);
+ stringBuffer.push('"><div class="feedblog_drawitem" id="feedblog_drawitem');
+ stringBuffer.push(i);
+ stringBuffer.push('"><\/div><\/div>');
+ }
+
+ // メニューを結合してパネル(後方)に組み込みます
+ stringBuffer.push("<div class='feedblog_pager_wrapper'>");
+ stringBuffer.push(menuMoveBuffer.join(""));
+ stringBuffer.push(menuInfoBuffer.join(""));
+ stringBuffer.push("</div>");
+
+ $("#feedblog_writearea").html(stringBuffer.join(""));
+
+ for (var i = startIndex; i < loopLimit; i++) {
+ // 各要素をオブジェクトに格納します
+ var entry = entries[i];
+
+ // すべてのパネルをオープン状態で生成します
+ generatePanel(entry, "feedblog_drawitem" + i, "feedblog_drawpanel" + i, false);
+ }
+}
+
+/**
+ * Entry要素を追加表示する為の関数
+ * class - div.feedblog_pager, ul.feedblog_pager, li.feedblog_pager などなど
+ * @param {Entry} 末尾に追加するEntryオブジェクト
+ * @param {int} 現在のEntry数
+ * @return {int} 新規記事を追加し終えた後のEntry数
+ */
+function showEntriesAdd(entry, entryIndex) {
+
+ var stringBuffer = [];
+
+ // 記事描画部分のパネルを生成します
+ var newIndex = entryIndex + 1;
+ stringBuffer.push('<div class="feedblog_drawpanel" id="feedblog_drawpanel');
+ stringBuffer.push(newIndex);
+ stringBuffer.push('"><div class="feedblog_drawitem" id="feedblog_drawitem');
+ stringBuffer.push(newIndex);
+ stringBuffer.push('"><\/div><\/div>');
+
+ // 記事をパネルに描画します
+ $("#feedblog_writearea").html($("#feedblog_writearea").html() + stringBuffer.join(""));
+ generatePanel(entry, "feedblog_drawitem" + newIndex, "feedblog_drawpanel" + newIndex, false);
+
+ return newIndex;
+}
+
-/**\r
- * FeedBlog Generator\r
- *\r
- * @copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)\r
- * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)\r
- * @since 2009/06/03\r
- * @version 4.2.0.0\r
- */\r
-\r
-// Feex XMLの<content>要素で、<br>を使用しているか?\r
-var inputValidateMode = 1;\r
-// 出力時に<content>要素に<br>を付加するか否かを格納する変数\r
-var outputValidateMode = 1;\r
-// ログのリストが書かれたXMLのファイルパスを記入してください\r
-var logXmlUrl;\r
-\r
-// フィードの基本情報を記録する変数\r
-var feedInfo;\r
-// 記事リストを格納する変数\r
-var entryList;\r
-// 現在編集中の記事の位置を示す変数\r
-var editIndex;\r
-// feedblogの設置アドレスを格納する変数\r
-var pageAddr;\r
-\r
-/**\r
- * 編集中の内容を反映し、画面に出力します\r
- */\r
-function applyChange() {\r
- feedInfo = applyFeedinfo();\r
- pageAddr = feedInfo.alternate;\r
-\r
- if (document.getElementById("entry_title").value == "" || document.getElementById("entry_stdin").value == "") {\r
- if (confirm("タイトルか記事が空白です!FeedBlogでの表示時にエラーが出ますがよろしいですか?") == false) {\r
- return;\r
- }\r
- }\r
-\r
- if (editIndex < 0) {\r
- var entry = new Object();\r
- var dateRfc3339 = getDate();\r
- entry.id = pageAddr + "?" + dateRfc3339;\r
- entry.title = document.getElementById("entry_title").value;\r
- entry.summary = "";\r
- entry.published = dateRfc3339;\r
- entry.updated = dateRfc3339;\r
- entry.link = pageAddr + "#" + entry.id;\r
- entry.content = document.getElementById("entry_stdin").value.replace(/\r\n/g, "\n");\r
- entry.category = getTags();\r
- entryList.unshift(entry);\r
-\r
- // ログ一覧を更新する\r
- refleshEntrylistBox();\r
-\r
- // 更新後、選択されている項目を、先刻追加した日記に移動する\r
- document.getElementById("logBox").selectedIndex = 1;\r
- editIndex = 0;\r
- } else {\r
- entryList[editIndex].title = document.getElementById("entry_title").value;\r
- entryList[editIndex].updated = getDate();\r
- entryList[editIndex].content = document.getElementById("entry_stdin").value.replace(/\r\n/g, "\n");\r
- entryList[editIndex].category = getTags();\r
-\r
- document.getElementById("logBox").options[parseInt(editIndex) + 1].text = entryList[editIndex].title;\r
- }\r
-\r
- document.getElementById("stdout").value = toXml(feedInfo, entryList);\r
-\r
- // プレビューエリアにHTMLを表示します\r
- document.getElementById("previewArea").innerHTML = entryList[editIndex].content.replace(/\n/g, "<br>");\r
-}\r
-\r
-/**\r
- * mixi用に記事を変換し、ウインドウを立ち上げて表示します\r
- */\r
-function openMixiWindow() {\r
- // 日記が選択されていない場合は何もしない\r
- if (editIndex < 0) {\r
- return;\r
- }\r
-\r
- // HTMLを生成します\r
- var window_str = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>FeedBlog Mixi用変換</title><style type="text/css">body {font-size: 12px;line-height: 18px;color: #004488;margin-top: 10px; margin-bottom: 10px;}td.default {font-size: 12px;line-height: 18px;color: #004488;border: 1px solid #8888ff;text-align: left;vertical-align: top;}div {text-align: left;}div.code {background: #f8f8ff;border: 1px solid #c8c8ff;padding: 10px;margin: 10px;margin-left: 0px;border-left: 5px solid #e8e8ff;font-size: 12px;line-height: 18px;}table.pager {padding: 0px;margin: 0px;border: 1px solid #8888ff;}td.pager {border: 1px solid #8888ff;padding-left: 3px;}td.formheader {font-size: 9pt;line-height: 13pt;font-family: sans-serif;font-weight: bold;padding: 3px;border: 1px solid #C5C5DE;background-color: #FAFAFF;}td.forminput {font-size: 9pt;line-height: 13pt;font-family: sans-serif;padding: 5px;border: 1px dotted #C5C5DE;}input {font-size: 9pt;line-height: 13pt;font-family: sans-serif;color: #1E4080;border: 1px dotted #BABAFE;background-color: #F5F5FF;}textarea {font-size: 9pt;line-height: 13pt;font-family: sans-serif;color: #1E4080;border: 1px dotted #BABAFE;background-color: #F5F5FF;}</style></head><body><center><table align="center" style="width: 800px;"><tbody><tr><td class="default" colspan="2" style="padding: 5px; text-align: center; vertical-align: bottom;"><table style="width: 100%"><tbody><tr><td class="formheader" style="text-align: center; width: 100px;">タイトル</td><td class="forminput" style="padding: 0px 5px 0px 3px;"><input type="text" id="entry_title" style="width: 100%" value="' + entryList[editIndex].title + '"></td></tr><tr><td class="formheader" style="text-align: center;">本文</td><td class="forminput" style="padding: 0px 5px 0px 3px;"><textarea id="entry_stdin" rows="20" style="width: 100%">' + mixiWrapper(entryList[editIndex].content) + '</textarea></td></tr></tbody></table></td></tr></tbody></table><br><input type="button" value="ウインドウを閉じる" onclick="javascript: window.close();"></center></body></html>';\r
-\r
- // ウインドウに書き出します\r
- var mixi_win = window.open('', 'feedblog_mixi_wrapper', 'width = 850, height = 490, scrollbars=yes');\r
- mixi_win.document.open();\r
- mixi_win.document.write(window_str);\r
- mixi_win.document.close();\r
-}\r
-\r
-/**\r
- * mixi用に通常のHTMLを変換します\r
- * @param {String} contents 日記の本文が格納されている文字列\r
- */\r
-function mixiWrapper(contents) {\r
- // 置換に使用する変数\r
- var target_tag;\r
- var target_element;\r
-\r
- // ニコニコ動画のタグを置換\r
- while ( target_tag = contents.match(/<iframe[^>]*src=["']http:\/\/ext.nicovideo.jp\/thumb\/[A-Za-z]*\d*["'][^>]*>[^<]*<\/iframe>/i)) {\r
- // src="..."の部分だけ抜き出す\r
- target_element = target_tag[0].match(/src=["'][^"']*["']/i);\r
- target_element = '<externalvideo src="NC:' + target_element[0].replace(/src=["']http:\/\/ext.nicovideo.jp\/thumb\/|["']/ig, "") + ':D">';\r
- // iframeタグ全体を置換する\r
- contents = contents.replace(target_tag, target_element);\r
- }\r
-\r
- // Aタグを変換する\r
- while (contents.match(/<a[^>]*>/)) {\r
- // 置換対象のAタグを抽出する\r
- target_tag = contents.match(/<a[^>]*>/i);\r
- // href="..."の部分のみを抜き出す\r
- target_element = target_tag[0].match(/href=["'][^"']*["']/i);\r
- // 相対URIが検出された場合、フルに置換する\r
- var baseUri = document.location.href.replace(/[^\/]+$/, '');\r
- target_element = target_element[0].replace(/\.\//, baseUri).replace(/\.\.\//g, "");\r
- // Aタグ全体を消去し、再度Aクローズタグの置換を行う\r
- contents = contents.replace(target_tag, "");\r
- contents = contents.replace(/<\/a>/i, " ( " + target_element.replace(/href=|["']/g, "") + " ) ");\r
- }\r
-\r
- // ブロック要素のタグが存在した場合、改行をその後に挿入します。\r
- if (document.getElementById("isCoverBlockTag").checked) {\r
- contents = contents.replace(/<(div|h\d)[^>]*>/ig, "-----------------------------------------------------------------------------\n");\r
- contents = contents.replace(/(\n|)<\/(div|h\d)>/ig, "\n-----------------------------------------------------------------------------\n");\r
- } else {\r
- contents = contents.replace(/<\/(div|h\d|p)>/ig, "\n");\r
- }\r
-\r
- // li要素を置換する\r
- contents = contents.replace(/<[\/]{0,1}ul>/ig, "");\r
- contents = contents.replace(/<li>/ig, "・");\r
- contents = contents.replace(/<\/li>/ig, "\n");\r
-\r
- // 通常のタグすべてを削除する\r
- contents = contents.replace(/<[^>]*>|<\/[^>]*>/ig, "");\r
-\r
- // 通常のタグ置換後、ニコニコ動画のタグを元に戻す\r
- contents = contents.replace(/<externalvideo src="NC:/g, "<externalvideo src='NC:");\r
- contents = contents.replace(/:D">/, ":D'>");\r
-\r
- // 半角を置換する\r
- contents = contents.replace(/ /g, " ");\r
-\r
- return contents;\r
-}\r
-\r
-/**\r
- * 選択中の記事を削除します\r
- * @param {int} index entryListから削除される記事のインデックス\r
- */\r
-function removeEntry(index) {\r
- if (editIndex == 0) {\r
- entryList.splice(editIndex, 1);\r
- refleshEntrylistBox();\r
- editIndex = -1;\r
-\r
- document.getElementById("stdout").value = "";\r
- document.getElementById("entry_title").value = "";\r
- $("#entry_stdin").jqte();\r
- document.getElementById("entry_stdin").value = "";\r
- $("#entry_stdin").jqte();\r
- }\r
- if (editIndex > 0) {\r
- var prevIndex = document.getElementById("logBox").selectedIndex - 1;\r
- entryList.splice(editIndex, 1);\r
- refleshEntrylistBox();\r
- document.getElementById("logBox").selectedIndex = prevIndex;\r
- editIndex = editIndex - 1;\r
-\r
- entryLoader(editIndex);\r
- }\r
-\r
- // プレビューエリアをクリアします\r
- document.getElementById("previewArea").innerHTML = "";\r
-}\r
-\r
-/**\r
- * すべての記事を削除します\r
- */\r
-function allRemoveEntry() {\r
- entryList = [];\r
-\r
- editIndex = -1;\r
- refleshEntrylistBox();\r
-\r
- document.getElementById("stdout").value = "";\r
- document.getElementById("entry_title").value = "";\r
- $("#entry_stdin").jqte();\r
- document.getElementById("entry_stdin").value = "";\r
- $("#entry_stdin").jqte();\r
-}\r
-\r
-/**\r
- * 全ての定数を取得・セットします\r
- */\r
-function initialize() {\r
- inputValidateMode = $("#feedblog_inputvalidatemode").val();\r
- outputValidateMode = $("#feedblog_outputvalidatemode").val();\r
- logXmlUrl = $("#feedblog_loglistxmlurl").val();\r
-\r
- if (outputValidateMode == "1") {\r
- document.getElementById("feedblog_addcontentbr").checked = true;\r
- } else {\r
- document.getElementById("feedblog_addcontentbr").checked = false;\r
- }\r
-}\r
-\r
-/**\r
- * 全DOMが使用可能になり次第、自動的に呼ばれる関数\r
- */\r
-$(document).ready(function() {\r
- // 初期処理を実施\r
- initialize();\r
-\r
- // jquery-teを初期として適用します\r
- $("#entry_stdin").jqte();\r
-\r
- // ログ一覧のXMLをロードします\r
- logXMLLoader();\r
-});\r
-\r
-/**\r
- * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します\r
- */\r
-function logXMLLoader() {\r
- // ログ用のXMLを読み込みます\r
- jQuery.ajax({\r
- url : logXmlUrl + "?time=" + (+new Date()),\r
- method : "GET",\r
- success : function(xmlData) {\r
- var separateTag = xmlData.getElementsByTagName("file");\r
- var urls = new Array(separateTag.length);\r
- var initUrl;\r
-\r
- // 読み込んだ要素をStoreに格納して表示\r
- var boxBuffer = [];\r
- boxBuffer.push("<form name='logform'><select name='logbox' onchange='xmlLoader(this.options[this.selectedIndex].value)'>");\r
- for (var i = 0; i < separateTag.length; i++) {\r
- if (i == 0) {\r
- initUrl = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
- }\r
- boxBuffer.push("<option value='" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + "'/>" + separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue + " (" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + ")" + "</option>");\r
- }\r
- boxBuffer.push("</select></form>");\r
-\r
- // コンボボックス要素を生成\r
- document.getElementById("feedblog_logselecter").innerHTML = boxBuffer.join("");\r
-\r
- // 最新の日記をローディングする\r
- xmlLoader(initUrl);\r
- },\r
- error : showError\r
- });\r
-}\r
-\r
-/**\r
- * URLを指定し、指定されたFeedXmlを読み込み、解析を行います\r
- * @param {String} url\r
- */\r
-function xmlLoader(url) {\r
- // 記事本体のデータをローディングする\r
- var loader = new jQuery.ajax({\r
- url : url + "?time=" + (+new Date()),\r
- method : "GET",\r
- success : analyzeTargetXml,\r
- error : showError\r
- });\r
-}\r
-\r
-/**\r
- * 引数に存在するXMLデータを解析し、画面に反映します\r
- * @param {Object} xmlData\r
- */\r
-function analyzeTargetXml(xmlData) {\r
- var rootTag = xmlData.getElementsByTagName("feed");\r
- var entryTag = xmlData.getElementsByTagName("entry");\r
-\r
- // グローバル変数を初期化\r
- feedInfo = null;\r
- entryList = [];\r
-\r
- feedInfo = new FeedInfo(rootTag[0]);\r
- for (var i = 0; i < entryTag.length; i++) {\r
- entryList.push(new Entry(entryTag[i]));\r
- }\r
-\r
- feedinfoLoader(feedInfo);\r
- pageAddr = feedInfo.alternate;\r
-\r
- refleshEntrylistBox();\r
- document.getElementById("entry_title").value = "";\r
- $("#entry_stdin").jqte();\r
- document.getElementById("entry_stdin").value = "";\r
- $("#entry_stdin").jqte();\r
- document.getElementById("entry_category").innerHTML = "";\r
- document.getElementById("stdout").value = "";\r
- editIndex = -1;\r
-\r
- // プレビューエリアをクリアします\r
- document.getElementById("previewArea").innerHTML = "";\r
-}\r
-\r
-/**\r
- * feedInfo変数の内容をHTMLに反映します\r
- * @param {FeedInfo} finfo 反映するfeedInfo変数\r
- */\r
-function feedinfoLoader(finfo) {\r
- document.getElementById("feed_title").value = finfo.title;\r
- document.getElementById("feed_subtitle").value = finfo.subtitle;\r
- document.getElementById("feed_self").value = finfo.self;\r
- document.getElementById("feed_alternate").value = finfo.alternate;\r
- document.getElementById("feed_id").value = finfo.id;\r
- document.getElementById("feed_rights").value = finfo.rights;\r
- document.getElementById("feed_authorname").value = finfo.authorname;\r
- document.getElementById("feed_authoremail").value = finfo.authoremail;\r
-}\r
-\r
-/**\r
- * HTMLの内容をFeedInfoに変換します\r
- */\r
-function applyFeedinfo() {\r
- var finfo = new Object();\r
- finfo.title = document.getElementById("feed_title").value;\r
- finfo.subtitle = document.getElementById("feed_subtitle").value;\r
- finfo.self = document.getElementById("feed_self").value;\r
- finfo.alternate = document.getElementById("feed_alternate").value;\r
- finfo.id = document.getElementById("feed_id").value;\r
- finfo.rights = document.getElementById("feed_rights").value;\r
- finfo.authorname = document.getElementById("feed_authorname").value;\r
- finfo.authoremail = document.getElementById("feed_authoremail").value;\r
-\r
- return finfo;\r
-}\r
-\r
-/**\r
- * 指定したEntryList上のインデックスの記事をロードします\r
- * @param {int} index\r
- */\r
-function entryLoader(index) {\r
- if (index < 0) {\r
- document.getElementById("entry_title").value = "";\r
- $("#entry_stdin").jqte();\r
- document.getElementById("entry_stdin").value = "";\r
- $("#entry_stdin").jqte();\r
- addTagSelectBoxFromCategory([]);\r
- editIndex = -1;\r
- } else {\r
- document.getElementById("entry_title").value = entryList[index].title;\r
- $("#entry_stdin").jqte();\r
- document.getElementById("entry_stdin").value = entryList[index].content;\r
- $("#entry_stdin").jqte();\r
- addTagSelectBoxFromCategory(entryList[index].category);\r
- editIndex = index;\r
- }\r
-\r
- // プレビューエリアをクリアします\r
- document.getElementById("previewArea").innerHTML = "";\r
-}\r
-\r
-/**\r
- * 記事一覧の情報を表示するセレクトボックスにentryListの情報を反映させます\r
- */\r
-function refleshEntrylistBox() {\r
- var stringBuffer = [];\r
- stringBuffer.push("<form name='logform'><select id='logBox' onchange='entryLoader(this.options[this.selectedIndex].value)'>");\r
- stringBuffer.push("<option value='-1'>新規作成</option>");\r
- for (var i = 0; i < entryList.length; i++) {\r
- stringBuffer.push("<option value='" + i + "'/>" + entryList[i].title + "</option>");\r
- }\r
- stringBuffer.push("</select></form>");\r
- // コンボボックス要素を生成\r
- document.getElementById("feedblog_entryselect").innerHTML = stringBuffer.join("");\r
-}\r
-\r
-/**\r
- * Feed基本情報保持クラス\r
- * @param {Object} obj\r
- */\r
-function FeedInfo(obj) {\r
- this.title = $("title:first", obj).text();\r
- this.subtitle = $("subtitle:first", obj).text();\r
- this.self = $("link[rel=self]", obj).attr("href");\r
- this.alternate = $("link[rel=alternate]", obj).attr("href");\r
- this.updated = $("updated:first", obj).text();\r
- this.id = $("id:first", obj).text();\r
- this.rights = $("rights:first", obj).text();\r
- this.authorname = $("author>name", obj).text();\r
- this.authoremail = $("author>email", obj).text();\r
-}\r
-\r
-/**\r
- * 記事クラス\r
- * @param {Object} obj entry 要素の DOM オブジェクト\r
- */\r
-function Entry(obj) {\r
- this.id = $("id:first", obj).text();\r
- this.title = $("title:first", obj).text();\r
- this.summary = $("summary:first", obj).text();\r
- this.published = $("published:first", obj).text();\r
- this.updated = $("updated:first", obj).text();\r
- this.link = $("link:first", obj).attr("href");\r
- this.content = $("content:first", obj).text();\r
-\r
- var categoryList = [];\r
- var categories = $("category", obj);\r
- var tempCategory = {};\r
- for (var i = 0; i < categories.length; i++) {\r
- tempCategory = {\r
- "term" : categories.eq(i).attr("term"),\r
- "label" : categories.eq(i).attr("label")\r
- };\r
- categoryList.push(tempCategory);\r
- }\r
- this.category = categoryList;\r
-\r
- if (inputValidateMode == 1) {\r
- this.content = this.content.replace(/[\r\n]|\r\n/g, "");\r
- this.content = this.content.replace(/<br[ \/]*>/ig, "\n");\r
- this.content = this.content.replace(/^[ \t]*/mg, "");\r
- }\r
-}\r
-\r
-/**\r
- * グローバル変数からXMLを生成し、返却します\r
- * @param {FeedInfo} finfo\r
- * @param {Entry[]} elist\r
- */\r
-function toXml(finfo, elist) {\r
- var stringBuffer = [];\r
-\r
- stringBuffer.push('<?xml version="1.0" encoding="utf-8"?>');\r
- stringBuffer.push('<feed xml:lang="ja-jp" xmlns="http://www.w3.org/2005/Atom">');\r
- stringBuffer.push('');\r
-\r
- stringBuffer.push('<title type="text">' + finfo.title + '</title>');\r
- stringBuffer.push('<subtitle type="text">' + finfo.subtitle + '</subtitle>');\r
- stringBuffer.push('<link rel="self" type="application/atom+xml" href="' + finfo.self + '" />');\r
- stringBuffer.push('<link rel="alternate" type="text/html" href="' + finfo.alternate + '" />');\r
- stringBuffer.push('<updated>' + getDate() + '</updated>');\r
- stringBuffer.push('<id>' + finfo.id + '</id>');\r
- stringBuffer.push('<rights type="text">' + finfo.rights + '</rights>');\r
- stringBuffer.push('<author>');\r
- stringBuffer.push(' <name>' + finfo.authorname + '</name>');\r
- stringBuffer.push(' <email>' + finfo.authoremail + '</email>');\r
- stringBuffer.push('</author>');\r
- stringBuffer.push('');\r
-\r
- for (var i = 0; i < elist.length; i++) {\r
- var temp_content;\r
- if (document.getElementById("feedblog_addcontentbr").checked) {\r
- temp_content = xmlAttrContentEscape(convertContent(elist[i].content));\r
- } else {\r
- temp_content = xmlAttrContentEscape(elist[i].content);\r
- }\r
-\r
- stringBuffer.push('<entry>');\r
- stringBuffer.push('<id>' + elist[i].id + '</id>');\r
- stringBuffer.push('<title>' + elist[i].title + '</title>');\r
- stringBuffer.push('<summary>' + elist[i].summary + '</summary>');\r
- stringBuffer.push('<published>' + elist[i].published + '</published>');\r
- stringBuffer.push('<updated>' + elist[i].updated + '</updated>');\r
- stringBuffer.push('<link href="' + elist[i].link + '" />');\r
- stringBuffer.push('<content type="html">' + temp_content + '</content>');\r
-\r
- for (var j = 0; j < elist[i].category.length; j++) {\r
- var tmpElist = elist[i].category[j];\r
- stringBuffer.push('<category term="' + tmpElist["term"] + '" label="' + tmpElist["label"] + '"/>');\r
- }\r
-\r
- stringBuffer.push('</entry>');\r
- stringBuffer.push('');\r
- }\r
-\r
- stringBuffer.push('</feed>');\r
-\r
- return stringBuffer.join("\n");\r
-}\r
-\r
-/**\r
- * <content>要素の変換を行います\r
- * @param {String} content\r
- */\r
-function convertContent(content) {\r
- if (document.getElementById("feedblog_addcontentbr").checked) {\r
- content = content.replace(/[\n\r]|\r\n/g, "<br>\n");\r
- } else {\r
- content = content.replace(/<br>/ig, "\n");\r
- }\r
-\r
- return content;\r
-}\r
-\r
-/**\r
- * XMLのエスケープを行う関数\r
- * @param {String} str エスケープを行う文字列\r
- */\r
-function xmlAttrContentEscape(str) {\r
- return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").replace(/^[\t]+/mg, "");\r
-}\r
-\r
-/**\r
- * XMLの逆エスケープを行う関数\r
- * @param {String} str 逆エスケープを行う文字列\r
- */\r
-function xmlAttrContentUnescape(str) {\r
- return str.replace(/^[\t]+/mg, "").replace(/^[ ]+/mg, " ").replace(/"/g, '"').replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&");\r
-}\r
-\r
-/**\r
- * jQueryでのパネル開閉を制御します\r
- * @param {String} id 開閉するオブジェクトのid\r
- */\r
-function closePanel(id) {\r
- $("#" + id).slideToggle();\r
-}\r
-\r
-/**\r
- * エラー画面を表示します\r
- */\r
-function showError() {\r
- alert("XMLファイルのローディング中にエラーが発生しました!");\r
-}\r
-\r
-/**\r
- * RFC3339形式の日時を出力します\r
- */\r
-function getDate() {\r
- var dNow = new Date();\r
- var sYear = dNow.getFullYear();\r
- var sMonth = dNow.getMonth() + 1;\r
- var sDate = dNow.getDate();\r
- var sHour = dNow.getHours();\r
- var sMinute = dNow.getMinutes();\r
- var sSecond = dNow.getSeconds();\r
-\r
- // 10以下の時は頭に"0"を挿入\r
- if (sMonth < 10)\r
- sMonth = "0" + sMonth;\r
- if (sDate < 10)\r
- sDate = "0" + sDate;\r
- if (sHour < 10)\r
- sHour = "0" + sHour;\r
- if (sMinute < 10)\r
- sMinute = "0" + sMinute;\r
- if (sSecond < 10)\r
- sSecond = "0" + sSecond;\r
-\r
- return sYear + "-" + sMonth + "-" + sDate + "T" + sHour + ":" + sMinute + ":" + sSecond + "+09:00";\r
-}\r
-\r
-/**\r
- * タグ情報一覧を返却する関数です\r
- */\r
-function getTags() {\r
- var tagList = [];\r
- var tagListHtml = $("*[name=tag]");\r
- var tagTemp = {};\r
- for (var i = 0; i < tagListHtml.length; i++) {\r
- // 各tag要素を取得して配列に格納\r
- if (tagListHtml.eq(i).val() != "") {\r
- tagTemp = {\r
- "term" : tagListHtml.eq(i).val(),\r
- "label" : tagListHtml.eq(i).find(":selected").text()\r
- };\r
- tagList.push(tagTemp);\r
- }\r
- }\r
-\r
- return tagList;\r
-}\r
-\r
-/**\r
- * デフォルトのタグ一覧を取得する関数です\r
- */\r
-function getDefaultTags() {\r
- // デフォルトのタグ一覧を取得\r
- // $term1,$label1|$term2,$label2 ... で定義されている\r
- var plainTagText = $("#feedblog_tagdefine").val();\r
- var tagList = [];\r
- var tagTemp = {};\r
- // 値が空のタグを初期値として先頭に追加\r
- tagList.push({\r
- "term" : "",\r
- "label" : "タグを選択してください"\r
- });\r
- for (var i = 0; i < plainTagText.split('|').length; i++) {\r
- // 各tag要素を取得して配列に格納\r
- tagTemp = {\r
- "term" : plainTagText.split('|')[i].split(',')[0],\r
- "label" : plainTagText.split('|')[i].split(',')[1]\r
- };\r
- tagList.push(tagTemp);\r
- }\r
-\r
- return tagList;\r
-}\r
-\r
-/**\r
- * HTMLにタグ選択用のセレクトボックスを追加します\r
- */\r
-function addTagSelectBox() {\r
- var tagList = getDefaultTags();\r
- var addHtml = [];\r
-\r
- addHtml.push('<select name="tag">');\r
- for (var i = 0; i < tagList.length; i++) {\r
- addHtml.push('<option value="' + tagList[i]["term"] + '">' + tagList[i]["label"] + '</option>');\r
- }\r
- addHtml.push('</select>');\r
-\r
- $("#entry_category").append(addHtml.join('') + "<br>");\r
-}\r
-\r
-/**\r
- * 現在のカテゴリーオブジェクトに従いセレクトボックスを追加します\r
- */\r
-function addTagSelectBoxFromCategory(categoryList) {\r
- $("#entry_category").html("");\r
- for (var j = 0; j < categoryList.length; j++) {\r
- // 選択対象を取得\r
- var selectedTagTerm = categoryList[j]["term"];\r
- var selectedTagLabel = categoryList[j]["label"];\r
- var selectedTagSetFlag = false;\r
-\r
- var tagList = getDefaultTags();\r
- var addHtml = [];\r
- addHtml.push('<select name="tag">');\r
- for (var i = 0; i < tagList.length; i++) {\r
- if (tagList[i]["term"] == selectedTagTerm && tagList[i]["label"] == selectedTagLabel) {\r
- addHtml.push('<option value="' + tagList[i]["term"] + '" selected="selected">' + tagList[i]["label"] + '</option>');\r
- selectedTagSetFlag = true;\r
- } else {\r
- addHtml.push('<option value="' + tagList[i]["term"] + '">' + tagList[i]["label"] + '</option>');\r
- }\r
- }\r
-\r
- if (!selectedTagSetFlag) {\r
- addHtml.push('<option value="' + selectedTagTerm + '" selected="selected">' + selectedTagLabel + '</option>');\r
- }\r
-\r
- addHtml.push('</select>');\r
- $("#entry_category").append(addHtml.join('') + "<br>");\r
- }\r
-}\r
+/**
+ * FeedBlog Generator
+ *
+ * @copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
+ * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
+ * @since 2009/06/03
+ * @version 4.2.1.0
+ */
+
+// Feex XMLの<content>要素で、<br>を使用しているか?
+var inputValidateMode = 1;
+// 出力時に<content>要素に<br>を付加するか否かを格納する変数
+var outputValidateMode = 1;
+// ログのリストが書かれたXMLのファイルパスを記入してください
+var logXmlUrl;
+
+// フィードの基本情報を記録する変数
+var feedInfo;
+// 記事リストを格納する変数
+var entryList;
+// 現在編集中の記事の位置を示す変数
+var editIndex;
+// feedblogの設置アドレスを格納する変数
+var pageAddr;
+
+/**
+ * 編集中の内容を反映し、画面に出力します
+ */
+function applyChange() {
+ feedInfo = applyFeedinfo();
+ pageAddr = feedInfo.alternate;
+
+ if (document.getElementById("entry_title").value == "" || document.getElementById("entry_stdin").value == "") {
+ if (confirm("タイトルか記事が空白です!FeedBlogでの表示時にエラーが出ますがよろしいですか?") == false) {
+ return;
+ }
+ }
+
+ if (editIndex < 0) {
+ var entry = new Object();
+ var dateRfc3339 = getDate();
+ entry.id = pageAddr + "?" + dateRfc3339;
+ entry.title = document.getElementById("entry_title").value;
+ entry.summary = "";
+ entry.published = dateRfc3339;
+ entry.updated = dateRfc3339;
+ entry.link = pageAddr + "#" + entry.id;
+ entry.content = document.getElementById("entry_stdin").value.replace(/\r\n/g, "\n");
+ entry.category = getTags();
+ entryList.unshift(entry);
+
+ // ログ一覧を更新する
+ refleshEntrylistBox();
+
+ // 更新後、選択されている項目を、先刻追加した日記に移動する
+ document.getElementById("logBox").selectedIndex = 1;
+ editIndex = 0;
+ } else {
+ entryList[editIndex].title = document.getElementById("entry_title").value;
+ entryList[editIndex].updated = getDate();
+ entryList[editIndex].content = document.getElementById("entry_stdin").value.replace(/\r\n/g, "\n");
+ entryList[editIndex].category = getTags();
+
+ document.getElementById("logBox").options[parseInt(editIndex) + 1].text = entryList[editIndex].title;
+ }
+
+ document.getElementById("stdout").value = toXml(feedInfo, entryList);
+
+ // プレビューエリアにHTMLを表示します
+ document.getElementById("previewArea").innerHTML = entryList[editIndex].content.replace(/\n/g, "<br>");
+}
+
+/**
+ * mixi用に記事を変換し、ウインドウを立ち上げて表示します
+ */
+function openMixiWindow() {
+ // 日記が選択されていない場合は何もしない
+ if (editIndex < 0) {
+ return;
+ }
+
+ // HTMLを生成します
+ var window_str = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>FeedBlog Mixi用変換</title><style type="text/css">body {font-size: 12px;line-height: 18px;color: #004488;margin-top: 10px; margin-bottom: 10px;}td.default {font-size: 12px;line-height: 18px;color: #004488;border: 1px solid #8888ff;text-align: left;vertical-align: top;}div {text-align: left;}div.code {background: #f8f8ff;border: 1px solid #c8c8ff;padding: 10px;margin: 10px;margin-left: 0px;border-left: 5px solid #e8e8ff;font-size: 12px;line-height: 18px;}table.pager {padding: 0px;margin: 0px;border: 1px solid #8888ff;}td.pager {border: 1px solid #8888ff;padding-left: 3px;}td.formheader {font-size: 9pt;line-height: 13pt;font-family: sans-serif;font-weight: bold;padding: 3px;border: 1px solid #C5C5DE;background-color: #FAFAFF;}td.forminput {font-size: 9pt;line-height: 13pt;font-family: sans-serif;padding: 5px;border: 1px dotted #C5C5DE;}input {font-size: 9pt;line-height: 13pt;font-family: sans-serif;color: #1E4080;border: 1px dotted #BABAFE;background-color: #F5F5FF;}textarea {font-size: 9pt;line-height: 13pt;font-family: sans-serif;color: #1E4080;border: 1px dotted #BABAFE;background-color: #F5F5FF;}</style></head><body><center><table align="center" style="width: 800px;"><tbody><tr><td class="default" colspan="2" style="padding: 5px; text-align: center; vertical-align: bottom;"><table style="width: 100%"><tbody><tr><td class="formheader" style="text-align: center; width: 100px;">タイトル</td><td class="forminput" style="padding: 0px 5px 0px 3px;"><input type="text" id="entry_title" style="width: 100%" value="' + entryList[editIndex].title + '"></td></tr><tr><td class="formheader" style="text-align: center;">本文</td><td class="forminput" style="padding: 0px 5px 0px 3px;"><textarea id="entry_stdin" rows="20" style="width: 100%">' + mixiWrapper(entryList[editIndex].content) + '</textarea></td></tr></tbody></table></td></tr></tbody></table><br><input type="button" value="ウインドウを閉じる" onclick="javascript: window.close();"></center></body></html>';
+
+ // ウインドウに書き出します
+ var mixi_win = window.open('', 'feedblog_mixi_wrapper', 'width = 850, height = 490, scrollbars=yes');
+ mixi_win.document.open();
+ mixi_win.document.write(window_str);
+ mixi_win.document.close();
+}
+
+/**
+ * mixi用に通常のHTMLを変換します
+ * @param {String} contents 日記の本文が格納されている文字列
+ */
+function mixiWrapper(contents) {
+ // 置換に使用する変数
+ var target_tag;
+ var target_element;
+
+ // ニコニコ動画のタグを置換
+ while ( target_tag = contents.match(/<iframe[^>]*src=["']http:\/\/ext.nicovideo.jp\/thumb\/[A-Za-z]*\d*["'][^>]*>[^<]*<\/iframe>/i)) {
+ // src="..."の部分だけ抜き出す
+ target_element = target_tag[0].match(/src=["'][^"']*["']/i);
+ target_element = '<externalvideo src="NC:' + target_element[0].replace(/src=["']http:\/\/ext.nicovideo.jp\/thumb\/|["']/ig, "") + ':D">';
+ // iframeタグ全体を置換する
+ contents = contents.replace(target_tag, target_element);
+ }
+
+ // Aタグを変換する
+ while (contents.match(/<a[^>]*>/)) {
+ // 置換対象のAタグを抽出する
+ target_tag = contents.match(/<a[^>]*>/i);
+ // href="..."の部分のみを抜き出す
+ target_element = target_tag[0].match(/href=["'][^"']*["']/i);
+ // 相対URIが検出された場合、フルに置換する
+ var baseUri = document.location.href.replace(/[^\/]+$/, '');
+ target_element = target_element[0].replace(/\.\//, baseUri).replace(/\.\.\//g, "");
+ // Aタグ全体を消去し、再度Aクローズタグの置換を行う
+ contents = contents.replace(target_tag, "");
+ contents = contents.replace(/<\/a>/i, " ( " + target_element.replace(/href=|["']/g, "") + " ) ");
+ }
+
+ // ブロック要素のタグが存在した場合、改行をその後に挿入します。
+ if (document.getElementById("isCoverBlockTag").checked) {
+ contents = contents.replace(/<(div|h\d)[^>]*>/ig, "-----------------------------------------------------------------------------\n");
+ contents = contents.replace(/(\n|)<\/(div|h\d)>/ig, "\n-----------------------------------------------------------------------------\n");
+ } else {
+ contents = contents.replace(/<\/(div|h\d|p)>/ig, "\n");
+ }
+
+ // li要素を置換する
+ contents = contents.replace(/<[\/]{0,1}ul>/ig, "");
+ contents = contents.replace(/<li>/ig, "・");
+ contents = contents.replace(/<\/li>/ig, "\n");
+
+ // 通常のタグすべてを削除する
+ contents = contents.replace(/<[^>]*>|<\/[^>]*>/ig, "");
+
+ // 通常のタグ置換後、ニコニコ動画のタグを元に戻す
+ contents = contents.replace(/<externalvideo src="NC:/g, "<externalvideo src='NC:");
+ contents = contents.replace(/:D">/, ":D'>");
+
+ // 半角を置換する
+ contents = contents.replace(/ /g, " ");
+
+ return contents;
+}
+
+/**
+ * 選択中の記事を削除します
+ * @param {int} index entryListから削除される記事のインデックス
+ */
+function removeEntry(index) {
+ if (editIndex == 0) {
+ entryList.splice(editIndex, 1);
+ refleshEntrylistBox();
+ editIndex = -1;
+
+ document.getElementById("stdout").value = "";
+ document.getElementById("entry_title").value = "";
+ $("#entry_stdin").jqte();
+ document.getElementById("entry_stdin").value = "";
+ $("#entry_stdin").jqte();
+ }
+ if (editIndex > 0) {
+ var prevIndex = document.getElementById("logBox").selectedIndex - 1;
+ entryList.splice(editIndex, 1);
+ refleshEntrylistBox();
+ document.getElementById("logBox").selectedIndex = prevIndex;
+ editIndex = editIndex - 1;
+
+ entryLoader(editIndex);
+ }
+
+ // プレビューエリアをクリアします
+ document.getElementById("previewArea").innerHTML = "";
+}
+
+/**
+ * すべての記事を削除します
+ */
+function allRemoveEntry() {
+ entryList = [];
+
+ editIndex = -1;
+ refleshEntrylistBox();
+
+ document.getElementById("stdout").value = "";
+ document.getElementById("entry_title").value = "";
+ $("#entry_stdin").jqte();
+ document.getElementById("entry_stdin").value = "";
+ $("#entry_stdin").jqte();
+}
+
+/**
+ * 全ての定数を取得・セットします
+ */
+function initialize() {
+ inputValidateMode = $("#feedblog_inputvalidatemode").val();
+ outputValidateMode = $("#feedblog_outputvalidatemode").val();
+ logXmlUrl = $("#feedblog_loglistxmlurl").val();
+
+ if (outputValidateMode == "1") {
+ document.getElementById("feedblog_addcontentbr").checked = true;
+ } else {
+ document.getElementById("feedblog_addcontentbr").checked = false;
+ }
+}
+
+/**
+ * 全DOMが使用可能になり次第、自動的に呼ばれる関数
+ */
+$(document).ready(function() {
+ // 初期処理を実施
+ initialize();
+
+ // jquery-teを初期として適用します
+ $("#entry_stdin").jqte();
+
+ // ログ一覧のXMLをロードします
+ logXMLLoader();
+});
+
+/**
+ * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します
+ */
+function logXMLLoader() {
+ // ログ用のXMLを読み込みます
+ jQuery.ajax({
+ url : logXmlUrl + "?time=" + (+new Date()),
+ method : "GET",
+ success : function(xmlData) {
+ var separateTag = xmlData.getElementsByTagName("file");
+ var urls = new Array(separateTag.length);
+ var initUrl;
+
+ // 読み込んだ要素をStoreに格納して表示
+ var boxBuffer = [];
+ boxBuffer.push("<form name='logform'><select name='logbox' onchange='xmlLoader(this.options[this.selectedIndex].value)'>");
+ for (var i = 0; i < separateTag.length; i++) {
+ if (i == 0) {
+ initUrl = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
+ }
+ boxBuffer.push("<option value='" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + "'/>" + separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue + " (" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + ")" + "</option>");
+ }
+ boxBuffer.push("</select></form>");
+
+ // コンボボックス要素を生成
+ document.getElementById("feedblog_logselecter").innerHTML = boxBuffer.join("");
+
+ // 最新の日記をローディングする
+ xmlLoader(initUrl);
+ },
+ error : showError
+ });
+}
+
+/**
+ * URLを指定し、指定されたFeedXmlを読み込み、解析を行います
+ * @param {String} url
+ */
+function xmlLoader(url) {
+ // 記事本体のデータをローディングする
+ var loader = new jQuery.ajax({
+ url : url + "?time=" + (+new Date()),
+ method : "GET",
+ success : analyzeTargetXml,
+ error : showError
+ });
+}
+
+/**
+ * 引数に存在するXMLデータを解析し、画面に反映します
+ * @param {Object} xmlData
+ */
+function analyzeTargetXml(xmlData) {
+ var rootTag = xmlData.getElementsByTagName("feed");
+ var entryTag = xmlData.getElementsByTagName("entry");
+
+ // グローバル変数を初期化
+ feedInfo = null;
+ entryList = [];
+
+ feedInfo = new FeedInfo(rootTag[0]);
+ for (var i = 0; i < entryTag.length; i++) {
+ entryList.push(new Entry(entryTag[i]));
+ }
+
+ feedinfoLoader(feedInfo);
+ pageAddr = feedInfo.alternate;
+
+ refleshEntrylistBox();
+ document.getElementById("entry_title").value = "";
+ $("#entry_stdin").jqte();
+ document.getElementById("entry_stdin").value = "";
+ $("#entry_stdin").jqte();
+ document.getElementById("entry_category").innerHTML = "";
+ document.getElementById("stdout").value = "";
+ editIndex = -1;
+
+ // プレビューエリアをクリアします
+ document.getElementById("previewArea").innerHTML = "";
+}
+
+/**
+ * feedInfo変数の内容をHTMLに反映します
+ * @param {FeedInfo} finfo 反映するfeedInfo変数
+ */
+function feedinfoLoader(finfo) {
+ document.getElementById("feed_title").value = finfo.title;
+ document.getElementById("feed_subtitle").value = finfo.subtitle;
+ document.getElementById("feed_self").value = finfo.self;
+ document.getElementById("feed_alternate").value = finfo.alternate;
+ document.getElementById("feed_id").value = finfo.id;
+ document.getElementById("feed_rights").value = finfo.rights;
+ document.getElementById("feed_authorname").value = finfo.authorname;
+ document.getElementById("feed_authoremail").value = finfo.authoremail;
+}
+
+/**
+ * HTMLの内容をFeedInfoに変換します
+ */
+function applyFeedinfo() {
+ var finfo = new Object();
+ finfo.title = document.getElementById("feed_title").value;
+ finfo.subtitle = document.getElementById("feed_subtitle").value;
+ finfo.self = document.getElementById("feed_self").value;
+ finfo.alternate = document.getElementById("feed_alternate").value;
+ finfo.id = document.getElementById("feed_id").value;
+ finfo.rights = document.getElementById("feed_rights").value;
+ finfo.authorname = document.getElementById("feed_authorname").value;
+ finfo.authoremail = document.getElementById("feed_authoremail").value;
+
+ return finfo;
+}
+
+/**
+ * 指定したEntryList上のインデックスの記事をロードします
+ * @param {int} index
+ */
+function entryLoader(index) {
+ if (index < 0) {
+ document.getElementById("entry_title").value = "";
+ $("#entry_stdin").jqte();
+ document.getElementById("entry_stdin").value = "";
+ $("#entry_stdin").jqte();
+ addTagSelectBoxFromCategory([]);
+ editIndex = -1;
+ } else {
+ document.getElementById("entry_title").value = entryList[index].title;
+ $("#entry_stdin").jqte();
+ document.getElementById("entry_stdin").value = entryList[index].content;
+ $("#entry_stdin").jqte();
+ addTagSelectBoxFromCategory(entryList[index].category);
+ editIndex = index;
+ }
+
+ // プレビューエリアをクリアします
+ document.getElementById("previewArea").innerHTML = "";
+}
+
+/**
+ * 記事一覧の情報を表示するセレクトボックスにentryListの情報を反映させます
+ */
+function refleshEntrylistBox() {
+ var stringBuffer = [];
+ stringBuffer.push("<form name='logform'><select id='logBox' onchange='entryLoader(this.options[this.selectedIndex].value)'>");
+ stringBuffer.push("<option value='-1'>新規作成</option>");
+ for (var i = 0; i < entryList.length; i++) {
+ stringBuffer.push("<option value='" + i + "'/>" + entryList[i].title + "</option>");
+ }
+ stringBuffer.push("</select></form>");
+ // コンボボックス要素を生成
+ document.getElementById("feedblog_entryselect").innerHTML = stringBuffer.join("");
+}
+
+/**
+ * Feed基本情報保持クラス
+ * @param {Object} obj
+ */
+function FeedInfo(obj) {
+ this.title = $("title:first", obj).text();
+ this.subtitle = $("subtitle:first", obj).text();
+ this.self = $("link[rel=self]", obj).attr("href");
+ this.alternate = $("link[rel=alternate]", obj).attr("href");
+ this.updated = $("updated:first", obj).text();
+ this.id = $("id:first", obj).text();
+ this.rights = $("rights:first", obj).text();
+ this.authorname = $("author>name", obj).text();
+ this.authoremail = $("author>email", obj).text();
+}
+
+/**
+ * 記事クラス
+ * @param {Object} obj entry 要素の DOM オブジェクト
+ */
+function Entry(obj) {
+ this.id = $("id:first", obj).text();
+ this.title = $("title:first", obj).text();
+ this.summary = $("summary:first", obj).text();
+ this.published = $("published:first", obj).text();
+ this.updated = $("updated:first", obj).text();
+ this.link = $("link:first", obj).attr("href");
+ this.content = $("content:first", obj).text();
+
+ var categoryList = [];
+ var categories = $("category", obj);
+ var tempCategory = {};
+ for (var i = 0; i < categories.length; i++) {
+ tempCategory = {
+ "term" : categories.eq(i).attr("term"),
+ "label" : categories.eq(i).attr("label")
+ };
+ categoryList.push(tempCategory);
+ }
+ this.category = categoryList;
+
+ if (inputValidateMode == 1) {
+ this.content = this.content.replace(/[\r\n]|\r\n/g, "");
+ this.content = this.content.replace(/<br[ \/]*>/ig, "\n");
+ this.content = this.content.replace(/^[ \t]*/mg, "");
+ }
+}
+
+/**
+ * グローバル変数からXMLを生成し、返却します
+ * @param {FeedInfo} finfo
+ * @param {Entry[]} elist
+ */
+function toXml(finfo, elist) {
+ var stringBuffer = [];
+
+ stringBuffer.push('<?xml version="1.0" encoding="utf-8"?>');
+ stringBuffer.push('<feed xml:lang="ja-jp" xmlns="http://www.w3.org/2005/Atom">');
+ stringBuffer.push('');
+
+ stringBuffer.push('<title type="text">' + finfo.title + '</title>');
+ stringBuffer.push('<subtitle type="text">' + finfo.subtitle + '</subtitle>');
+ stringBuffer.push('<link rel="self" type="application/atom+xml" href="' + finfo.self + '" />');
+ stringBuffer.push('<link rel="alternate" type="text/html" href="' + finfo.alternate + '" />');
+ stringBuffer.push('<updated>' + getDate() + '</updated>');
+ stringBuffer.push('<id>' + finfo.id + '</id>');
+ stringBuffer.push('<rights type="text">' + finfo.rights + '</rights>');
+ stringBuffer.push('<author>');
+ stringBuffer.push(' <name>' + finfo.authorname + '</name>');
+ stringBuffer.push(' <email>' + finfo.authoremail + '</email>');
+ stringBuffer.push('</author>');
+ stringBuffer.push('');
+
+ for (var i = 0; i < elist.length; i++) {
+ var temp_content;
+ if (document.getElementById("feedblog_addcontentbr").checked) {
+ temp_content = xmlAttrContentEscape(convertContent(elist[i].content));
+ } else {
+ temp_content = xmlAttrContentEscape(elist[i].content);
+ }
+
+ stringBuffer.push('<entry>');
+ stringBuffer.push('<id>' + elist[i].id + '</id>');
+ stringBuffer.push('<title>' + elist[i].title + '</title>');
+ stringBuffer.push('<summary>' + elist[i].summary + '</summary>');
+ stringBuffer.push('<published>' + elist[i].published + '</published>');
+ stringBuffer.push('<updated>' + elist[i].updated + '</updated>');
+ stringBuffer.push('<link href="' + elist[i].link + '" />');
+ stringBuffer.push('<content type="html">' + temp_content + '</content>');
+
+ for (var j = 0; j < elist[i].category.length; j++) {
+ var tmpElist = elist[i].category[j];
+ stringBuffer.push('<category term="' + tmpElist["term"] + '" label="' + tmpElist["label"] + '"/>');
+ }
+
+ stringBuffer.push('</entry>');
+ stringBuffer.push('');
+ }
+
+ stringBuffer.push('</feed>');
+
+ return stringBuffer.join("\n");
+}
+
+/**
+ * <content>要素の変換を行います
+ * @param {String} content
+ */
+function convertContent(content) {
+ if (document.getElementById("feedblog_addcontentbr").checked) {
+ content = content.replace(/[\n\r]|\r\n/g, "<br>\n");
+ } else {
+ content = content.replace(/<br>/ig, "\n");
+ }
+
+ return content;
+}
+
+/**
+ * XMLのエスケープを行う関数
+ * @param {String} str エスケープを行う文字列
+ */
+function xmlAttrContentEscape(str) {
+ return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").replace(/^[\t]+/mg, "");
+}
+
+/**
+ * XMLの逆エスケープを行う関数
+ * @param {String} str 逆エスケープを行う文字列
+ */
+function xmlAttrContentUnescape(str) {
+ return str.replace(/^[\t]+/mg, "").replace(/^[ ]+/mg, " ").replace(/"/g, '"').replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&");
+}
+
+/**
+ * jQueryでのパネル開閉を制御します
+ * @param {String} id 開閉するオブジェクトのid
+ */
+function closePanel(id) {
+ $("#" + id).slideToggle();
+}
+
+/**
+ * エラー画面を表示します
+ */
+function showError() {
+ alert("XMLファイルのローディング中にエラーが発生しました!");
+}
+
+/**
+ * RFC3339形式の日時を出力します
+ */
+function getDate() {
+ var dNow = new Date();
+ var sYear = dNow.getFullYear();
+ var sMonth = dNow.getMonth() + 1;
+ var sDate = dNow.getDate();
+ var sHour = dNow.getHours();
+ var sMinute = dNow.getMinutes();
+ var sSecond = dNow.getSeconds();
+
+ // 10以下の時は頭に"0"を挿入
+ if (sMonth < 10)
+ sMonth = "0" + sMonth;
+ if (sDate < 10)
+ sDate = "0" + sDate;
+ if (sHour < 10)
+ sHour = "0" + sHour;
+ if (sMinute < 10)
+ sMinute = "0" + sMinute;
+ if (sSecond < 10)
+ sSecond = "0" + sSecond;
+
+ return sYear + "-" + sMonth + "-" + sDate + "T" + sHour + ":" + sMinute + ":" + sSecond + "+09:00";
+}
+
+/**
+ * タグ情報一覧を返却する関数です
+ */
+function getTags() {
+ var tagList = [];
+ var tagListHtml = $("*[name=tag]");
+ var tagTemp = {};
+ for (var i = 0; i < tagListHtml.length; i++) {
+ // 各tag要素を取得して配列に格納
+ if (tagListHtml.eq(i).val() != "") {
+ tagTemp = {
+ "term" : tagListHtml.eq(i).val(),
+ "label" : tagListHtml.eq(i).find(":selected").text()
+ };
+ tagList.push(tagTemp);
+ }
+ }
+
+ return tagList;
+}
+
+/**
+ * デフォルトのタグ一覧を取得する関数です
+ */
+function getDefaultTags() {
+ // デフォルトのタグ一覧を取得
+ // $term1,$label1|$term2,$label2 ... で定義されている
+ var plainTagText = $("#feedblog_tagdefine").val();
+ var tagList = [];
+ var tagTemp = {};
+ // 値が空のタグを初期値として先頭に追加
+ tagList.push({
+ "term" : "",
+ "label" : "タグを選択してください"
+ });
+ for (var i = 0; i < plainTagText.split('|').length; i++) {
+ // 各tag要素を取得して配列に格納
+ tagTemp = {
+ "term" : plainTagText.split('|')[i].split(',')[0],
+ "label" : plainTagText.split('|')[i].split(',')[1]
+ };
+ tagList.push(tagTemp);
+ }
+
+ return tagList;
+}
+
+/**
+ * HTMLにタグ選択用のセレクトボックスを追加します
+ */
+function addTagSelectBox() {
+ var tagList = getDefaultTags();
+ var addHtml = [];
+
+ addHtml.push('<select name="tag">');
+ for (var i = 0; i < tagList.length; i++) {
+ addHtml.push('<option value="' + tagList[i]["term"] + '">' + tagList[i]["label"] + '</option>');
+ }
+ addHtml.push('</select>');
+
+ $("#entry_category").append(addHtml.join('') + "<br>");
+}
+
+/**
+ * 現在のカテゴリーオブジェクトに従いセレクトボックスを追加します
+ */
+function addTagSelectBoxFromCategory(categoryList) {
+ $("#entry_category").html("");
+ for (var j = 0; j < categoryList.length; j++) {
+ // 選択対象を取得
+ var selectedTagTerm = categoryList[j]["term"];
+ var selectedTagLabel = categoryList[j]["label"];
+ var selectedTagSetFlag = false;
+
+ var tagList = getDefaultTags();
+ var addHtml = [];
+ addHtml.push('<select name="tag">');
+ for (var i = 0; i < tagList.length; i++) {
+ if (tagList[i]["term"] == selectedTagTerm && tagList[i]["label"] == selectedTagLabel) {
+ addHtml.push('<option value="' + tagList[i]["term"] + '" selected="selected">' + tagList[i]["label"] + '</option>');
+ selectedTagSetFlag = true;
+ } else {
+ addHtml.push('<option value="' + tagList[i]["term"] + '">' + tagList[i]["label"] + '</option>');
+ }
+ }
+
+ if (!selectedTagSetFlag) {
+ addHtml.push('<option value="' + selectedTagTerm + '" selected="selected">' + selectedTagLabel + '</option>');
+ }
+
+ addHtml.push('</select>');
+ $("#entry_category").append(addHtml.join('') + "<br>");
+ }
+}
-/**\r
- * FeedBlog SearchScript\r
- *\r
- * @copyright 2013 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)\r
- * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)\r
- * @since 2009/02/27\r
- * @version 4.2.0.0\r
- */\r
-\r
-// ブログ本体のHTMLファイルのURL\r
-var mainPageUrl;\r
-\r
-// 検索用ページURL\r
-var searchPageUrl;\r
-\r
-// 最新の記事を示すパスへの文字列\r
-var latestXml;\r
-\r
-// ログのリストが書かれたXMLのファイルパス\r
-var logXmlUrl;\r
-\r
-// 一画面あたりの表示記事数\r
-var showLength;\r
-\r
-/**\r
- * XMLファイルから読み込んだファイルのバリデートモード\r
- * 0 = 改行コード部分に<br/>を挿入\r
- * 1 = 改行コード部分に<br/>を挿入しない\r
- */\r
-var validateMode;\r
-\r
-// 検索結果をメモリ上に保持する変数です\r
-var loadedEntries;\r
-\r
-// fetchEntries 用のセマフォ\r
-var fetchEntriesSemaphore = new Semaphore();\r
-\r
-// 現在の検索語のキャッシュ\r
-var currentSearchWords;\r
-\r
-// ログのファイルリストを格納するグローバル変数です\r
-var logData;\r
-\r
-// コンボボックスのオブジェクトを格納するグローバル変数です\r
-var comboBox;\r
-\r
-// URL末尾用文字列(スクリプトを開いた瞬間のミリ秒を記録)\r
-var urlSuffix;\r
-\r
-/**\r
- * 記事を実際に生成します。この部分を編集することでデザインを変更可能です。\r
- * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト\r
- * @param {String} drawitem 「本文」を描画すべきパネルのDIV要素のid\r
- * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを最終的に描画すべきパネルのDIV要素のid\r
- * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か\r
- */\r
-function generatePanel(entry, drawitem, renderto, closed) {\r
- // プラグインを実行\r
- if ( typeof ($("#" + renderto).feedblog_contents_plugin) == "function") {\r
- $("#" + renderto).feedblog_contents_plugin({\r
- entry : entry\r
- });\r
- }\r
-\r
- // HTML用の配列を用意する\r
- var htmlBuffer = [];\r
-\r
- // 内部的に描画先IDを生成\r
- var feedblogContentId = "" + renderto + "_content_div";\r
-\r
- // 各要素をオブジェクトに描画\r
- $("#" + drawitem).html(entry.content);\r
-\r
- // ヘッダパネルを生成 class= .feedblog_header\r
- htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +\r
-\r
- // 本体記事を作成 class= .feedblog_content\r
- "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");\r
-\r
- // 最終描画実施\r
- $("#" + renderto).html(htmlBuffer.join(""));\r
-}\r
-\r
-/**\r
- * システム表示画面を実際に生成します。この部分を編集することでデザインを変更可能です。\r
- * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト\r
- * @param {String} drawitem パネルの本文を格納したDIV要素のid\r
- * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを焼き付けるDIV要素のid\r
- * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か\r
- */\r
-function generateSystemPanel(entry, drawitem, renderto, closed) {\r
- // HTMLを生成する\r
- var htmlBuffer = [];\r
-\r
- // 描画先IDを生成\r
- var feedblogContentId = "" + renderto + "_content_div";\r
-\r
- // 各要素をオブジェクトに描画\r
- $("#" + drawitem).html(entry.content);\r
-\r
- // ヘッダパネルを生成 class= .feedblog_header\r
- htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +\r
-\r
- // 本体記事を作成 class= .feedblog_content\r
- "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");\r
-\r
- $("#" + renderto).html(htmlBuffer.join(""));\r
-}\r
-\r
-/**\r
- * 検索フォーム及び結果表示フォームを生成するメソッドです。\r
- */\r
-function generateForm() {\r
- var formBuffer = [];\r
- formBuffer.push("<form class='feedblog_searchform' onsubmit='javascript: searchDiary(); return false;'>▼ 検索語句を入力してください (語句を半角で区切るとAND、|で区切るとORで検索します)<br/>");\r
- formBuffer.push("<input type='text' id='feedblog_searchword' class='feedblog_searchword'><input type='submit' id='feedblog_execsearch' value='検索'><br/>");\r
- formBuffer.push("<input type='checkbox' id='feedblog_regexpOptionI' class='feedblog_regexpOptionI' checked='checked'/><label class='feedblog_searchform' for='feedblog_regexpOptionI'>大文字、小文字を区別しない</label><br/>");\r
- formBuffer.push("<br/>");\r
- formBuffer.push("▼ 検索対象ログ選択<br/>");\r
- formBuffer.push("<div id='feedblog_logselecter'></div>");\r
- formBuffer.push("<input type='checkbox' id='feedblog_allsearchcheck' class='feedblog_allsearchcheck' checked='checked'/>");\r
- formBuffer.push("<label class='feedblog_searchform' for='feedblog_allsearchcheck'>すべてのログに対して検索を行う</label>");\r
- formBuffer.push("<br/>");\r
- formBuffer.push("</form>");\r
- $("#feedblog_searchform").html(formBuffer.join(""));\r
-\r
- var resultAreaBuffer = "<div class='feedblog_result_status'></div>";\r
-\r
- $("#feedblog_resultwritearea").html(resultAreaBuffer);\r
-}\r
-\r
-/**\r
- * 全ての定数を取得・セットします\r
- */\r
-function initialize() {\r
- // 初期値をhiddenパラメータより読み込みます\r
- mainPageUrl = $("#feedblog_mainpageurl").val();\r
- searchPageUrl = $("#feedblog_searchpageurl").val();\r
- latestXml = $("#feedblog_latestxml").val();\r
- logXmlUrl = $("#feedblog_loglistxmlurl").val();\r
- showLength = parseInt($("#feedblog_showlength").val());\r
- if (isNaN(showLength)) {\r
- showLength = 1;\r
- }\r
- validateMode = $("#feedblog_validatemode").val();\r
- \r
- // 初期値を設定します\r
- urlSuffix = +new Date();\r
-\r
- // 必要な環境を確認します\r
- var errorBuf = [];\r
- // 変数確認\r
- if (mainPageUrl === undefined) {\r
- errorBuf.push("設定値「feedblog_mainpageurl」が欠落しています。");\r
- }\r
- if (searchPageUrl === undefined) {\r
- errorBuf.push("設定値「feedblog_searchpageurl」が欠落しています。");\r
- }\r
- if (latestXml === undefined) {\r
- errorBuf.push("設定値「feedblog_latestxml」が欠落しています。");\r
- }\r
- if (logXmlUrl === undefined) {\r
- errorBuf.push("設定値「feedblog_loglistxmlurl」が欠落しています。");\r
- }\r
- if (showLength === undefined) {\r
- errorBuf.push("設定値「feedblog_showlength」が欠落しています。");\r
- }\r
- if (validateMode === undefined) {\r
- errorBuf.push("設定値「feedblog_validatemode」が欠落しています。");\r
- }\r
- // SHA-1関数確認\r
- try {\r
- if ( typeof (CryptoJS.SHA1) != "function") {\r
- errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");\r
- }\r
- } catch (ex) {\r
- errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");\r
- }\r
-\r
- // 描画エリアチェック\r
- if ($("#feedblog_searchform").length == 0) {\r
- errorBuf.push("描画エリア「feedblog_searchform」が存在しません。");\r
- }\r
- if ($("#feedblog_resultwritearea").length == 0) {\r
- errorBuf.push("描画エリア「feedblog_resultwritearea」が存在しません。");\r
- }\r
- if ($("#feedblog_writearea").length == 0) {\r
- errorBuf.push("描画エリア「feedblog_writearea」が存在しません。");\r
- }\r
-\r
- // エラーがある場合は以降の処理を継続しない\r
- if (errorBuf.length > 0) {\r
- alert("初期設定値に誤りがあります。\n詳細:\n" + errorBuf.join("\n"));\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-/**\r
- * jQueryへのイベント登録です\r
- */\r
-$(document).ready(function() {\r
- // 初期処理を実施\r
- if (!initialize()) {\r
- return false;\r
- }\r
-\r
- // ログ一覧のXMLをロードします\r
- logXMLLoader();\r
-\r
- // 各種パネルを生成します\r
- generateForm();\r
-});\r
-\r
-/**\r
- * jQueryでのパネル開閉を制御します\r
- */\r
-function closePanel(id) {\r
- $("#" + id).slideToggle();\r
-}\r
-\r
-/**\r
- * 記事クラス\r
- * @param {Object} obj entry 要素の DOM オブジェクト\r
- */\r
-function Entry(obj) {\r
- this.title = $("title:first", obj).text();\r
- if (this.title == "")\r
- requiredElementError(obj, "title");\r
- this.title = validateText(this.title);\r
- this.content = $("content:first", obj).text();\r
- this.content = validateText(this.content);\r
- this.id = $("id:first", obj).text();\r
- if (this.id == "")\r
- requiredElementError(obj, "id");\r
- this.date = $("updated:first", obj).text();\r
- if (this.date == "")\r
- requiredElementError(obj, "updated");\r
- this.date = validateData(this.date);\r
- this.category = $("category", obj);\r
-}\r
-\r
-/**\r
- * システム用記事クラス\r
- * @param {Object} obj entry 要素の DOM オブジェクト\r
- */\r
-function SystemEntry(obj) {\r
- this.title = $("title:first", obj).text();\r
- this.title = validateText(this.title);\r
- this.content = $("content:first", obj).text();\r
- this.content = validateText(this.content);\r
- this.id = $("id:first", obj).text();\r
- this.date = $("updated:first", obj).text();\r
- this.date = validateData(this.date);\r
- this.category = $("category", obj);\r
-}\r
-\r
-/**\r
- * 記事内が単語群を全て含んでいるか\r
- * @param {Array} keywords 単語群\r
- * @param {String} regexpType 正規表現の検索モードを示す文字列\r
- * @return {boolean} bool 全て含んでいれば true、さもなくば false\r
- */\r
-Entry.prototype.hasKeywords = function(keywords, regexpType) {\r
- // 正規表現が一致するかという判定"のみ"を行います\r
- for (var i = 0; i < keywords.length; i++) {\r
- // 正規表現チェック用のオブジェクトを用意します(OR条件は一時的に条件を置換)\r
- var reg = new RegExp('(?:' + keywords[i] + ')(?![^<>]*>)', regexpType);\r
- // 一致しなかったらその時点で脱出\r
- if (!reg.test(this.content) && !reg.test(this.title))\r
- return false;\r
- }\r
- return true;\r
-};\r
-\r
-/**\r
- * 呼び出すとDIV:id名:feedblog_writearea上のHTMLを削除し、ロードエフェクトを表示します\r
- */\r
-function loadingEffect() {\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
-\r
- // ロード表示用のパネルを生成\r
- var systemEntry = new SystemEntry();\r
- systemEntry.title = "Now Loading .....";\r
- systemEntry.content = '<br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';\r
- generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-\r
- // 結果表示エリアをリセット\r
- $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");\r
-}\r
-\r
-/**\r
- * 記事データのエラー時の処理を行います\r
- */\r
-function showError() {\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
-\r
- // エラー内容をパネルに描画\r
- var systemEntry = new SystemEntry();\r
- systemEntry.title = "エラー";\r
- var errorContent = [];\r
- errorContent.push('<br/>記事ファイル(XML)の取得に失敗しました。以下のような原因が考えられます。<br/><br/>');\r
- errorContent.push('・設定値「feedblog_latestxml」に正しいパスが設定されていない。<br/>');\r
- errorContent.push('・設定値「feedblog_loglistxmlurl」に正しいパスが設定されていない。<br/>');\r
- errorContent.push('・ローカル環境で起動している(必ずサーバにアップロードして実行してください)。<br/>');\r
- errorContent.push('<br/>');\r
- systemEntry.content = errorContent.join("\n");\r
- generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-\r
- // 結果表示エリアをリセット\r
- $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");\r
-}\r
-\r
-/**\r
- * 記事データのエラー時の処理を行います\r
- */\r
-function notFoundError() {\r
- $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');\r
- $("#feedblog_drawitem").html('<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>');\r
-\r
- // エラー内容をパネルに描画\r
- var systemEntry = new SystemEntry();\r
- systemEntry.title = "検索結果";\r
- systemEntry.content = '<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>';\r
- generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
-\r
- // 結果表示エリアをリセット\r
- $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");\r
-}\r
-\r
-/**\r
- * 記事データのエラー時の処理を行います\r
- */\r
-function requiredElementError(parent, name) {\r
- alert(parent.ownerDocument.URL + ": 必須な要素 " + name + " が存在しないか空な " + parent.tagName + " 要素が存在します");\r
-}\r
-\r
-/**\r
- * 日付のHTML表示用バリデーション処理を行います\r
- * @param {String} data RFC3339形式のdate-time文字列\r
- */\r
-function validateData(data) {\r
- data = data.replace(/T/g, " ");\r
-\r
- // 秒数の小数点以下の部分はカットする\r
- data = data.substring(0, 19);\r
-\r
- return data;\r
-}\r
-\r
-/**\r
- * 記事本文のバリデーション処理を行います\r
- * @param {String} contents 記事の本文が格納されている文字列\r
- */\r
-function validateText(contents) {\r
- // <br/>タグを挿入する\r
- if (validateMode == 0) {\r
- contents = contents.replace(/[\n\r]|\r\n/g, "<br />");\r
- }\r
-\r
- return contents;\r
-}\r
-\r
-/**\r
- * XML用に要素をエスケープします\r
- * @param {String} str エスケープを行いたい文字列\r
- */\r
-function xmlAttrContentEscape(str) {\r
- // return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');\r
- return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").replace(/^[\t]+/mg, "");\r
-}\r
-\r
-/**\r
- * 記事本文に日付を付加します\r
- * @param {String} contents 記事の本文が格納されている文字列\r
- * @param {String} id 記事の初公開日を示す日付文字列\r
- */\r
-function contentsWithid(contents, id) {\r
- // リンク用文末作成\r
- var hashTag = '<br/><div class="feedblog_content_footer"><a href="' + xmlAttrContentEscape(mainPageUrl) + '#' + xmlAttrContentEscape(id) + '" target="_blank">- この日の記事にリンクする -<\/a><\/div>';\r
- return contents + hashTag;\r
-}\r
-\r
-/**\r
- * 強調タグを追加します\r
- * @param {String} word 強調したい語句\r
- */\r
-function emphasizeWord(word) {\r
- return '<span style="background-color: red;">' + word + '</span>';\r
-}\r
-\r
-/**\r
- * 長い順に並べるための比較関数です\r
- * @param {String} a 比較対象(1)\r
- * @param {String} b 比較対象(2)\r
- */\r
-function compareLengthDecrease(a, b) {\r
- a = a.length;\r
- b = b.length;\r
- return a > b ? -1 : a < b ? 1 : 0;\r
-}\r
-\r
-/**\r
- * セマフォ制御用のオブジェクトです\r
- */\r
-function Semaphore() {\r
- this.id = null;\r
- this.count = 0;\r
- this.buf = [];\r
- this.xhrs = [];\r
-}\r
-\r
-/**\r
- * セマフォ初期化用の関数です\r
- */\r
-Semaphore.prototype.init = function() {\r
- while (this.xhrs.length > 0) {\r
- this.xhrs.shift().abort();\r
- }\r
- this.id = Math.random();\r
- this.count = 0;\r
- this.buf = [];\r
-};\r
-\r
-/**\r
- * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します\r
- */\r
-function logXMLLoader() {\r
- // ログ用のXMLを読み込みます\r
- jQuery.ajax({\r
- url : logXmlUrl + '?time=' + urlSuffix,\r
- method : "GET",\r
- error : showError,\r
- success : function(xmlData) {\r
- var separateTag = xmlData.getElementsByTagName("file");\r
- logData = new Array(separateTag.length);\r
-\r
- // 読み込んだ要素をStoreに格納して表示\r
- var boxBuffer = [];\r
- boxBuffer.push("<select class='feedblog_logselecter' id='feedblog_logbox'>");\r
- for (var i = 0; i < separateTag.length; i++) {\r
- boxBuffer.push("<option value='" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + "'>" + separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue + "</option>");\r
- logData[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
- }\r
- boxBuffer.push("</select>");\r
-\r
- // コンボボックス要素を生成\r
- $("#feedblog_logselecter").html(boxBuffer.join(""));\r
- }\r
- });\r
-}\r
-\r
-/**\r
- * 検索単語を取得します\r
- */\r
-function getSearchWords() {\r
- var searchWord = document.getElementById("feedblog_searchword").value;\r
- if (searchWord == "")\r
- return null;\r
- var searchWords = [];\r
-\r
- // 検索単語をサニタイジングします\r
- // HTMLのメタ文字\r
- searchWord = xmlAttrContentEscape(searchWord);\r
- // 正規表現のメタ文字\r
- searchWord = searchWord.replace(/([$()*+.?\[\\\]^{}])/g, '\\$1');\r
- // 半角スペースで配列に分割\r
- searchWords = searchWord.replace(/^\s+|\+$/g, '').split(/\s+/);\r
- // 正規表現の選択を長い順に並び替えます(AND条件)\r
- searchWords.sort(compareLengthDecrease);\r
-\r
- return searchWords.length == 0 ? null : searchWords;\r
-}\r
-\r
-/**\r
- * 文章内を特定の単語で検索し、一致した部分を強調表示タグで置き換えます\r
- * @param {String} searchWord 探索する単語\r
- * @param {String} plainText 探索を行う文章\r
- * @param {String} regexpType 正規表現の検索モードを示す文字列\r
- */\r
-function complexEmphasize(searchWord, plainText, regexpType) {\r
- // 正規表現の選択を長い順に並び替える\r
- searchWord = searchWord.split('|').sort(compareLengthDecrease).join('|');\r
- // タグの内側でないことを確認する正規表現を追加\r
- var pattern = new RegExp('(?:' + searchWord + ')(?![^<>]*>)', regexpType);\r
-\r
- var result = [];\r
- var currentIndex = -1;\r
- // 現在マッチしている部分文字列の開始位置\r
- var currentLastIndex = -1;\r
- // 現在マッチしている部分文字列の、現在の末尾\r
- var m;\r
- // 正規表現マッチの結果配列\r
- while ( m = pattern.exec(plainText)) {\r
- if (m.index > currentLastIndex) {\r
- // 新しい部分文字列へのマッチが始まったので、そこまでの文字列をバッファに書き出す\r
- if (currentIndex < currentLastIndex)\r
- result.push(emphasizeWord(plainText.substring(currentIndex, currentLastIndex)));\r
- result.push(plainText.substring(currentLastIndex, m.index));\r
- // 開始位置の更新\r
- currentIndex = m.index;\r
- }\r
- // 末尾位置を更新\r
- currentLastIndex = pattern.lastIndex;\r
- // 次の正規表現マッチは今マッチした文字の次の文字から\r
- pattern.lastIndex = m.index + 1;\r
- }\r
- // 残った文字列を書き出す\r
- if (currentIndex < currentLastIndex)\r
- result.push(emphasizeWord(plainText.substring(currentIndex, currentLastIndex)));\r
- result.push(plainText.substring(currentLastIndex));\r
-\r
- // 結合して返す\r
- return result.join('');\r
-}\r
-\r
-/**\r
- * 検索結果を分割して表示します(2回目以降呼び出し)\r
- * @param {int} showLength 一回の画面に表示する記事数\r
- * @param {int} startIndex 表示を開始する記事のインデックス\r
- */\r
-function showEntriesRange(showLength, startIndex) {\r
- // メモリ上から記事データをロード\r
- var entries = loadedEntries;\r
-\r
- // 表示インデックスが範囲外の場合はエラーパネルを表示して終了\r
- if (startIndex < 0 || entries.length <= startIndex) {\r
- showError();\r
- return;\r
- }\r
-\r
- var stringBuffer = [];\r
-\r
- // リミッターを設定する\r
- var loopLimit = (showLength + startIndex > entries.length) ? entries.length : showLength + startIndex;\r
- var indexShowEntries = loopLimit + 1;\r
-\r
- for (var i = startIndex; i < loopLimit; i++) {\r
- stringBuffer.push('<div class="feedblog_drawpanel" id="feedblog_drawpanel');\r
- stringBuffer.push(i);\r
- stringBuffer.push('"><div class="feedblog_drawitem" id="feedblog_drawitem');\r
- stringBuffer.push(i);\r
- stringBuffer.push('"><\/div><\/div>');\r
- }\r
- $("#feedblog_writearea").html(stringBuffer.join(''));\r
-\r
- stringBuffer.length = 0;\r
- for ( i = startIndex; i < loopLimit; i++) {\r
- var entry = entries[i];\r
- generatePanel(entry, "feedblog_drawitem" + i, "feedblog_drawpanel" + i, false);\r
- }\r
-\r
- // メニュー表示用バッファ\r
- var menuBuffer = [];\r
- menuBuffer.push("<div class='feedblog_pager_wrapper'>");\r
- menuBuffer.push("<ul class='feedblog_pager'>");\r
-\r
- // ブランクエリアを挟む\r
- menuBuffer.push("<li class='feedblog_pager_blank'></li>");\r
-\r
- // 左パネルの表示制御\r
- if (startIndex - showLength >= 0) {\r
- menuBuffer.push("\<li class='feedblog_pager_goback'><span class='feedblog_pager_goback' onclick='showEntriesRange(" + showLength + ", " + (startIndex - showLength) + "); return false;'>\< 前の" + showLength + "件を表示</span\></li>");\r
- } else {\r
- menuBuffer.push("\<li class='feedblog_pager_goback'>\< 前の" + showLength + "件を表示</a\></li>");\r
- }\r
-\r
- // 中央のパネルの表示制御\r
- menuBuffer.push("<li class='feedblog_pager_center'>[ ");\r
- var menuNumbers = Math.ceil(entries.length / showLength);\r
- for ( i = 0; i < menuNumbers; i++) {\r
- if (startIndex / showLength == i) {\r
- menuBuffer.push(i + " ");\r
- } else {\r
- menuBuffer.push("<span class='feedblog_pager_center' onclick='showEntriesRange(" + showLength + ", " + (i * showLength) + "); return false;'>");\r
- menuBuffer.push(i);\r
- menuBuffer.push("</span> ");\r
- }\r
- }\r
- menuBuffer.push("]</li>");\r
-\r
- // 右パネルの表示制御\r
- if (entries.length > startIndex + showLength) {\r
- menuBuffer.push("\<li class='feedblog_pager_gonext'><span class='feedblog_pager_gonext' onclick='showEntriesRange(" + showLength + ", " + (startIndex + showLength) + "); return false;'>次の" + showLength + "件を表示 \></span\></li>");\r
- } else {\r
- menuBuffer.push("\<li class='feedblog_pager_gonext'>次の" + showLength + "件を表示 \></li>");\r
- }\r
-\r
- // ブランクエリアを挟む\r
- menuBuffer.push("<li class='feedblog_pager_blank'></li>");\r
-\r
- menuBuffer.push("</ul></div>");\r
-\r
- // 検索結果を表示します\r
- $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'>" + entries.length + "件の記事が該当しました / " + (startIndex + 1) + "~" + loopLimit + "件目までを表示中<br/></div>" + menuBuffer.join(""));\r
-}\r
-\r
-/**\r
- * 検索時のjQuery.ajaxのcallback関数\r
- */\r
-function fetchEntries(xmlData) {\r
- // 大文字小文字を区別するかを取得します\r
- var regexpOptionI = document.getElementById("feedblog_regexpOptionI");\r
- var regexpType = regexpOptionI ? "ig" : "g";\r
-\r
- // entry要素のみを切り出します\r
- var entries = xmlData.getElementsByTagName("entry");\r
-\r
- // entry要素の回数だけ実行します\r
- for (var j = 0; j < entries.length; j++) {\r
- var entry = new Entry(entries[j]);\r
-\r
- // 正規表現が一致した場合は、強調表現処理を行います\r
- if (entry.hasKeywords(currentSearchWords, regexpType)) {\r
- // 強調表現を実行します\r
- entry.title = complexEmphasize(currentSearchWords.join("|"), entry.title, regexpType);\r
- entry.content = complexEmphasize(currentSearchWords.join("|"), entry.content, regexpType);\r
-\r
- fetchEntriesSemaphore.buf.push(entry);\r
- }\r
- }\r
-\r
- // セマフォのカウンタを減少させます (Ajaxとの同期のため)\r
- fetchEntriesSemaphore.count--;\r
-\r
- // 全てのログを読み終わったら表示\r
- if (fetchEntriesSemaphore.count == 0) {\r
- var entries = fetchEntriesSemaphore.buf;\r
-\r
- // 一軒も検索にヒットしなかった場合は専用のパネルを表示して終了\r
- if (entries.length == 0) {\r
- notFoundError();\r
- return;\r
- }\r
-\r
- // entryを更新時間でソート\r
- entries = entries.sort(function(a, b) {\r
- a = a.updated;\r
- b = b.updated;\r
- return a > b ? -1 : a < b ? 1 : 0;\r
- });\r
-\r
- loadedEntries = entries;\r
-\r
- // 表示ロジック呼び出し\r
- showEntriesRange(showLength, 0);\r
- }\r
-}\r
-\r
-/**\r
- * 「探索」ボタンを押されたときに呼び出されるメソッドです\r
- */\r
-function searchDiary() {\r
- // 検索結果フィールドをクリアします\r
- document.getElementById("feedblog_writearea").innerHTML = "";\r
-\r
- // 探索したい単語を取得します\r
- currentSearchWords = getSearchWords();\r
- if (!currentSearchWords) {\r
- alert("検索対象の単語が入力されていません");\r
- // 検索結果の欄をリセットします\r
- $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");\r
- return;\r
- }\r
-\r
- // ロードエフェクトを表示します\r
- loadingEffect();\r
-\r
- // 全チェックを取得します\r
- var allCheckedFlag = document.getElementById("feedblog_allsearchcheck").checked;\r
-\r
- // セマフォを初期化\r
- fetchEntriesSemaphore.init();\r
- // 記事が全検索モードか否かをチェックします\r
- var urls = null;\r
- if (allCheckedFlag == true) {\r
- // 全記事検索なので全てのログのURL\r
- urls = logData;\r
- } else {\r
- // 単独記事探索なので、選んだログのURL\r
- urls = [document.getElementById("feedblog_logbox").options[document.getElementById("feedblog_logbox").selectedIndex].value];\r
- }\r
- fetchEntriesSemaphore.urls = urls;\r
- fetchEntriesSemaphore.count = urls.length;\r
- for ( i = 0; i < urls.length; i++) {\r
- var xhr = new jQuery.ajax({\r
- url : urls[i] + '?time=' + urlSuffix,\r
- method : "GET",\r
- async : true,\r
- success : fetchEntries,\r
- error : showError\r
- });\r
- fetchEntriesSemaphore.xhrs.push(xhr);\r
- }\r
-}\r
+/**
+ * FeedBlog SearchScript
+ *
+ * @copyright 2013 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 4.2.1.0
+ */
+
+// ブログ本体のHTMLファイルのURL
+var mainPageUrl;
+
+// 検索用ページURL
+var searchPageUrl;
+
+// 最新の記事を示すパスへの文字列
+var latestXml;
+
+// ログのリストが書かれたXMLのファイルパス
+var logXmlUrl;
+
+// 一画面あたりの表示記事数
+var showLength;
+
+/**
+ * XMLファイルから読み込んだファイルのバリデートモード
+ * 0 = 改行コード部分に<br/>を挿入
+ * 1 = 改行コード部分に<br/>を挿入しない
+ */
+var validateMode;
+
+// 検索結果をメモリ上に保持する変数です
+var loadedEntries;
+
+// fetchEntries 用のセマフォ
+var fetchEntriesSemaphore = new Semaphore();
+
+// 現在の検索語のキャッシュ
+var currentSearchWords;
+
+// ログのファイルリストを格納するグローバル変数です
+var logData;
+
+// コンボボックスのオブジェクトを格納するグローバル変数です
+var comboBox;
+
+// URL末尾用文字列(スクリプトを開いた瞬間のミリ秒を記録)
+var urlSuffix;
+
+/**
+ * 記事を実際に生成します。この部分を編集することでデザインを変更可能です。
+ * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト
+ * @param {String} drawitem 「本文」を描画すべきパネルのDIV要素のid
+ * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを最終的に描画すべきパネルのDIV要素のid
+ * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か
+ */
+function generatePanel(entry, drawitem, renderto, closed) {
+ // プラグインを実行
+ if ( typeof ($("#" + renderto).feedblog_contents_plugin) == "function") {
+ $("#" + renderto).feedblog_contents_plugin({
+ entry : entry
+ });
+ }
+
+ // HTML用の配列を用意する
+ var htmlBuffer = [];
+
+ // 内部的に描画先IDを生成
+ var feedblogContentId = "" + renderto + "_content_div";
+
+ // 各要素をオブジェクトに描画
+ $("#" + drawitem).html(entry.content);
+
+ // ヘッダパネルを生成 class= .feedblog_header
+ htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +
+
+ // 本体記事を作成 class= .feedblog_content
+ "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");
+
+ // 最終描画実施
+ $("#" + renderto).html(htmlBuffer.join(""));
+}
+
+/**
+ * システム表示画面を実際に生成します。この部分を編集することでデザインを変更可能です。
+ * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト
+ * @param {String} drawitem パネルの本文を格納したDIV要素のid
+ * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを焼き付けるDIV要素のid
+ * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か
+ */
+function generateSystemPanel(entry, drawitem, renderto, closed) {
+ // HTMLを生成する
+ var htmlBuffer = [];
+
+ // 描画先IDを生成
+ var feedblogContentId = "" + renderto + "_content_div";
+
+ // 各要素をオブジェクトに描画
+ $("#" + drawitem).html(entry.content);
+
+ // ヘッダパネルを生成 class= .feedblog_header
+ htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +
+
+ // 本体記事を作成 class= .feedblog_content
+ "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");
+
+ $("#" + renderto).html(htmlBuffer.join(""));
+}
+
+/**
+ * 検索フォーム及び結果表示フォームを生成するメソッドです。
+ */
+function generateForm() {
+ var formBuffer = [];
+ formBuffer.push("<form class='feedblog_searchform' onsubmit='javascript: searchDiary(); return false;'>▼ 検索語句を入力してください (語句を半角で区切るとAND、|で区切るとORで検索します)<br/>");
+ formBuffer.push("<input type='text' id='feedblog_searchword' class='feedblog_searchword'><input type='submit' id='feedblog_execsearch' value='検索'><br/>");
+ formBuffer.push("<input type='checkbox' id='feedblog_regexpOptionI' class='feedblog_regexpOptionI' checked='checked'/><label class='feedblog_searchform' for='feedblog_regexpOptionI'>大文字、小文字を区別しない</label><br/>");
+ formBuffer.push("<br/>");
+ formBuffer.push("▼ 検索対象ログ選択<br/>");
+ formBuffer.push("<div id='feedblog_logselecter'></div>");
+ formBuffer.push("<input type='checkbox' id='feedblog_allsearchcheck' class='feedblog_allsearchcheck' checked='checked'/>");
+ formBuffer.push("<label class='feedblog_searchform' for='feedblog_allsearchcheck'>すべてのログに対して検索を行う</label>");
+ formBuffer.push("<br/>");
+ formBuffer.push("</form>");
+ $("#feedblog_searchform").html(formBuffer.join(""));
+
+ var resultAreaBuffer = "<div class='feedblog_result_status'></div>";
+
+ $("#feedblog_resultwritearea").html(resultAreaBuffer);
+}
+
+/**
+ * 全ての定数を取得・セットします
+ */
+function initialize() {
+ // 初期値をhiddenパラメータより読み込みます
+ mainPageUrl = $("#feedblog_mainpageurl").val();
+ searchPageUrl = $("#feedblog_searchpageurl").val();
+ latestXml = $("#feedblog_latestxml").val();
+ logXmlUrl = $("#feedblog_loglistxmlurl").val();
+ showLength = parseInt($("#feedblog_showlength").val());
+ if (isNaN(showLength)) {
+ showLength = 1;
+ }
+ validateMode = $("#feedblog_validatemode").val();
+
+ // 初期値を設定します
+ urlSuffix = +new Date();
+
+ // 必要な環境を確認します
+ var errorBuf = [];
+ // 変数確認
+ if (mainPageUrl === undefined) {
+ errorBuf.push("設定値「feedblog_mainpageurl」が欠落しています。");
+ }
+ if (searchPageUrl === undefined) {
+ errorBuf.push("設定値「feedblog_searchpageurl」が欠落しています。");
+ }
+ if (latestXml === undefined) {
+ errorBuf.push("設定値「feedblog_latestxml」が欠落しています。");
+ }
+ if (logXmlUrl === undefined) {
+ errorBuf.push("設定値「feedblog_loglistxmlurl」が欠落しています。");
+ }
+ if (showLength === undefined) {
+ errorBuf.push("設定値「feedblog_showlength」が欠落しています。");
+ }
+ if (validateMode === undefined) {
+ errorBuf.push("設定値「feedblog_validatemode」が欠落しています。");
+ }
+ // SHA-1関数確認
+ try {
+ if ( typeof (CryptoJS.SHA1) != "function") {
+ errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");
+ }
+ } catch (ex) {
+ errorBuf.push("crypt-jsモジュール(hmac-sha1.js)が読み込まれていません。");
+ }
+
+ // 描画エリアチェック
+ if ($("#feedblog_searchform").length == 0) {
+ errorBuf.push("描画エリア「feedblog_searchform」が存在しません。");
+ }
+ if ($("#feedblog_resultwritearea").length == 0) {
+ errorBuf.push("描画エリア「feedblog_resultwritearea」が存在しません。");
+ }
+ if ($("#feedblog_writearea").length == 0) {
+ errorBuf.push("描画エリア「feedblog_writearea」が存在しません。");
+ }
+
+ // エラーがある場合は以降の処理を継続しない
+ if (errorBuf.length > 0) {
+ alert("初期設定値に誤りがあります。\n詳細:\n" + errorBuf.join("\n"));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * jQueryへのイベント登録です
+ */
+$(document).ready(function() {
+ // 初期処理を実施
+ if (!initialize()) {
+ return false;
+ }
+
+ // ログ一覧のXMLをロードします
+ logXMLLoader();
+
+ // 各種パネルを生成します
+ generateForm();
+});
+
+/**
+ * jQueryでのパネル開閉を制御します
+ */
+function closePanel(id) {
+ $("#" + id).slideToggle();
+}
+
+/**
+ * 記事クラス
+ * @param {Object} obj entry 要素の DOM オブジェクト
+ */
+function Entry(obj) {
+ this.title = $("title:first", obj).text();
+ if (this.title == "")
+ requiredElementError(obj, "title");
+ this.title = validateText(this.title);
+ this.content = $("content:first", obj).text();
+ this.content = validateText(this.content);
+ 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);
+ this.category = $("category", obj);
+}
+
+/**
+ * システム用記事クラス
+ * @param {Object} obj entry 要素の DOM オブジェクト
+ */
+function SystemEntry(obj) {
+ this.title = $("title:first", obj).text();
+ this.title = validateText(this.title);
+ this.content = $("content:first", obj).text();
+ this.content = validateText(this.content);
+ this.id = $("id:first", obj).text();
+ this.date = $("updated:first", obj).text();
+ this.date = validateData(this.date);
+ this.category = $("category", obj);
+}
+
+/**
+ * 記事内が単語群を全て含んでいるか
+ * @param {Array} keywords 単語群
+ * @param {String} regexpType 正規表現の検索モードを示す文字列
+ * @return {boolean} bool 全て含んでいれば true、さもなくば false
+ */
+Entry.prototype.hasKeywords = function(keywords, regexpType) {
+ // 正規表現が一致するかという判定"のみ"を行います
+ for (var i = 0; i < keywords.length; i++) {
+ // 正規表現チェック用のオブジェクトを用意します(OR条件は一時的に条件を置換)
+ var reg = new RegExp('(?:' + keywords[i] + ')(?![^<>]*>)', regexpType);
+ // 一致しなかったらその時点で脱出
+ if (!reg.test(this.content) && !reg.test(this.title))
+ return false;
+ }
+ return true;
+};
+
+/**
+ * 呼び出すとDIV:id名:feedblog_writearea上のHTMLを削除し、ロードエフェクトを表示します
+ */
+function loadingEffect() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // ロード表示用のパネルを生成
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "Now Loading .....";
+ systemEntry.content = '<br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);
+
+ // 結果表示エリアをリセット
+ $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");
+}
+
+/**
+ * 記事データのエラー時の処理を行います
+ */
+function showError() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+
+ // エラー内容をパネルに描画
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "エラー";
+ var errorContent = [];
+ errorContent.push('<br/>記事ファイル(XML)の取得に失敗しました。以下のような原因が考えられます。<br/><br/>');
+ errorContent.push('・設定値「feedblog_latestxml」に正しいパスが設定されていない。<br/>');
+ errorContent.push('・設定値「feedblog_loglistxmlurl」に正しいパスが設定されていない。<br/>');
+ errorContent.push('・ローカル環境で起動している(必ずサーバにアップロードして実行してください)。<br/>');
+ errorContent.push('<br/>');
+ systemEntry.content = errorContent.join("\n");
+ generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);
+
+ // 結果表示エリアをリセット
+ $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");
+}
+
+/**
+ * 記事データのエラー時の処理を行います
+ */
+function notFoundError() {
+ $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem"> <\/div><\/div>');
+ $("#feedblog_drawitem").html('<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>');
+
+ // エラー内容をパネルに描画
+ var systemEntry = new SystemEntry();
+ systemEntry.title = "検索結果";
+ systemEntry.content = '<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>';
+ generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);
+
+ // 結果表示エリアをリセット
+ $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");
+}
+
+/**
+ * 記事データのエラー時の処理を行います
+ */
+function requiredElementError(parent, name) {
+ alert(parent.ownerDocument.URL + ": 必須な要素 " + name + " が存在しないか空な " + parent.tagName + " 要素が存在します");
+}
+
+/**
+ * 日付のHTML表示用バリデーション処理を行います
+ * @param {String} data RFC3339形式のdate-time文字列
+ */
+function validateData(data) {
+ data = data.replace(/T/g, " ");
+
+ // 秒数の小数点以下の部分はカットする
+ 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;
+}
+
+/**
+ * XML用に要素をエスケープします
+ * @param {String} str エスケープを行いたい文字列
+ */
+function xmlAttrContentEscape(str) {
+ // return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
+ return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").replace(/^[\t]+/mg, "");
+}
+
+/**
+ * 記事本文に日付を付加します
+ * @param {String} contents 記事の本文が格納されている文字列
+ * @param {String} id 記事の初公開日を示す日付文字列
+ */
+function contentsWithid(contents, id) {
+ // リンク用文末作成
+ var hashTag = '<br/><div class="feedblog_content_footer"><a href="' + xmlAttrContentEscape(mainPageUrl) + '#' + xmlAttrContentEscape(id) + '" target="_blank">- この日の記事にリンクする -<\/a><\/div>';
+ return contents + hashTag;
+}
+
+/**
+ * 強調タグを追加します
+ * @param {String} word 強調したい語句
+ */
+function emphasizeWord(word) {
+ return '<span style="background-color: red;">' + word + '</span>';
+}
+
+/**
+ * 長い順に並べるための比較関数です
+ * @param {String} a 比較対象(1)
+ * @param {String} b 比較対象(2)
+ */
+function compareLengthDecrease(a, b) {
+ a = a.length;
+ b = b.length;
+ return a > b ? -1 : a < b ? 1 : 0;
+}
+
+/**
+ * セマフォ制御用のオブジェクトです
+ */
+function Semaphore() {
+ this.id = null;
+ this.count = 0;
+ this.buf = [];
+ this.xhrs = [];
+}
+
+/**
+ * セマフォ初期化用の関数です
+ */
+Semaphore.prototype.init = function() {
+ while (this.xhrs.length > 0) {
+ this.xhrs.shift().abort();
+ }
+ this.id = Math.random();
+ this.count = 0;
+ this.buf = [];
+};
+
+/**
+ * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します
+ */
+function logXMLLoader() {
+ // ログ用のXMLを読み込みます
+ jQuery.ajax({
+ url : logXmlUrl + '?time=' + urlSuffix,
+ method : "GET",
+ error : showError,
+ success : function(xmlData) {
+ var separateTag = xmlData.getElementsByTagName("file");
+ logData = new Array(separateTag.length);
+
+ // 読み込んだ要素をStoreに格納して表示
+ var boxBuffer = [];
+ boxBuffer.push("<select class='feedblog_logselecter' id='feedblog_logbox'>");
+ 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>");
+ logData[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
+ }
+ boxBuffer.push("</select>");
+
+ // コンボボックス要素を生成
+ $("#feedblog_logselecter").html(boxBuffer.join(""));
+ }
+ });
+}
+
+/**
+ * 検索単語を取得します
+ */
+function getSearchWords() {
+ var searchWord = document.getElementById("feedblog_searchword").value;
+ if (searchWord == "")
+ return null;
+ var searchWords = [];
+
+ // 検索単語をサニタイジングします
+ // HTMLのメタ文字
+ searchWord = xmlAttrContentEscape(searchWord);
+ // 正規表現のメタ文字
+ searchWord = searchWord.replace(/([$()*+.?\[\\\]^{}])/g, '\\$1');
+ // 半角スペースで配列に分割
+ searchWords = searchWord.replace(/^\s+|\+$/g, '').split(/\s+/);
+ // 正規表現の選択を長い順に並び替えます(AND条件)
+ searchWords.sort(compareLengthDecrease);
+
+ return searchWords.length == 0 ? null : searchWords;
+}
+
+/**
+ * 文章内を特定の単語で検索し、一致した部分を強調表示タグで置き換えます
+ * @param {String} searchWord 探索する単語
+ * @param {String} plainText 探索を行う文章
+ * @param {String} regexpType 正規表現の検索モードを示す文字列
+ */
+function complexEmphasize(searchWord, plainText, regexpType) {
+ // 正規表現の選択を長い順に並び替える
+ searchWord = searchWord.split('|').sort(compareLengthDecrease).join('|');
+ // タグの内側でないことを確認する正規表現を追加
+ var pattern = new RegExp('(?:' + searchWord + ')(?![^<>]*>)', regexpType);
+
+ var result = [];
+ var currentIndex = -1;
+ // 現在マッチしている部分文字列の開始位置
+ var currentLastIndex = -1;
+ // 現在マッチしている部分文字列の、現在の末尾
+ var m;
+ // 正規表現マッチの結果配列
+ while ( m = pattern.exec(plainText)) {
+ if (m.index > currentLastIndex) {
+ // 新しい部分文字列へのマッチが始まったので、そこまでの文字列をバッファに書き出す
+ if (currentIndex < currentLastIndex)
+ result.push(emphasizeWord(plainText.substring(currentIndex, currentLastIndex)));
+ result.push(plainText.substring(currentLastIndex, m.index));
+ // 開始位置の更新
+ currentIndex = m.index;
+ }
+ // 末尾位置を更新
+ currentLastIndex = pattern.lastIndex;
+ // 次の正規表現マッチは今マッチした文字の次の文字から
+ pattern.lastIndex = m.index + 1;
+ }
+ // 残った文字列を書き出す
+ if (currentIndex < currentLastIndex)
+ result.push(emphasizeWord(plainText.substring(currentIndex, currentLastIndex)));
+ result.push(plainText.substring(currentLastIndex));
+
+ // 結合して返す
+ return result.join('');
+}
+
+/**
+ * 検索結果を分割して表示します(2回目以降呼び出し)
+ * @param {int} showLength 一回の画面に表示する記事数
+ * @param {int} startIndex 表示を開始する記事のインデックス
+ */
+function showEntriesRange(showLength, startIndex) {
+ // メモリ上から記事データをロード
+ var entries = loadedEntries;
+
+ // 表示インデックスが範囲外の場合はエラーパネルを表示して終了
+ if (startIndex < 0 || entries.length <= startIndex) {
+ showError();
+ return;
+ }
+
+ var stringBuffer = [];
+
+ // リミッターを設定する
+ var loopLimit = (showLength + startIndex > entries.length) ? entries.length : showLength + startIndex;
+ var indexShowEntries = loopLimit + 1;
+
+ for (var i = startIndex; i < loopLimit; i++) {
+ stringBuffer.push('<div class="feedblog_drawpanel" id="feedblog_drawpanel');
+ stringBuffer.push(i);
+ stringBuffer.push('"><div class="feedblog_drawitem" id="feedblog_drawitem');
+ stringBuffer.push(i);
+ stringBuffer.push('"><\/div><\/div>');
+ }
+ $("#feedblog_writearea").html(stringBuffer.join(''));
+
+ stringBuffer.length = 0;
+ for ( i = startIndex; i < loopLimit; i++) {
+ var entry = entries[i];
+ generatePanel(entry, "feedblog_drawitem" + i, "feedblog_drawpanel" + i, false);
+ }
+
+ // メニュー表示用バッファ
+ var menuBuffer = [];
+ menuBuffer.push("<div class='feedblog_pager_wrapper'>");
+ menuBuffer.push("<ul class='feedblog_pager'>");
+
+ // ブランクエリアを挟む
+ menuBuffer.push("<li class='feedblog_pager_blank'></li>");
+
+ // 左パネルの表示制御
+ if (startIndex - showLength >= 0) {
+ menuBuffer.push("\<li class='feedblog_pager_goback'><span class='feedblog_pager_goback' onclick='showEntriesRange(" + showLength + ", " + (startIndex - showLength) + "); return false;'>\< 前の" + showLength + "件を表示</span\></li>");
+ } else {
+ menuBuffer.push("\<li class='feedblog_pager_goback'>\< 前の" + showLength + "件を表示</a\></li>");
+ }
+
+ // 中央のパネルの表示制御
+ menuBuffer.push("<li class='feedblog_pager_center'>[ ");
+ var menuNumbers = Math.ceil(entries.length / showLength);
+ for ( i = 0; i < menuNumbers; i++) {
+ if (startIndex / showLength == i) {
+ menuBuffer.push(i + " ");
+ } else {
+ menuBuffer.push("<span class='feedblog_pager_center' onclick='showEntriesRange(" + showLength + ", " + (i * showLength) + "); return false;'>");
+ menuBuffer.push(i);
+ menuBuffer.push("</span> ");
+ }
+ }
+ menuBuffer.push("]</li>");
+
+ // 右パネルの表示制御
+ if (entries.length > startIndex + showLength) {
+ menuBuffer.push("\<li class='feedblog_pager_gonext'><span class='feedblog_pager_gonext' onclick='showEntriesRange(" + showLength + ", " + (startIndex + showLength) + "); return false;'>次の" + showLength + "件を表示 \></span\></li>");
+ } else {
+ menuBuffer.push("\<li class='feedblog_pager_gonext'>次の" + showLength + "件を表示 \></li>");
+ }
+
+ // ブランクエリアを挟む
+ menuBuffer.push("<li class='feedblog_pager_blank'></li>");
+
+ menuBuffer.push("</ul></div>");
+
+ // 検索結果を表示します
+ $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'>" + entries.length + "件の記事が該当しました / " + (startIndex + 1) + "~" + loopLimit + "件目までを表示中<br/></div>" + menuBuffer.join(""));
+}
+
+/**
+ * 検索時のjQuery.ajaxのcallback関数
+ */
+function fetchEntries(xmlData) {
+ // 大文字小文字を区別するかを取得します
+ var regexpOptionI = document.getElementById("feedblog_regexpOptionI");
+ var regexpType = regexpOptionI ? "ig" : "g";
+
+ // entry要素のみを切り出します
+ var entries = xmlData.getElementsByTagName("entry");
+
+ // entry要素の回数だけ実行します
+ for (var j = 0; j < entries.length; j++) {
+ var entry = new Entry(entries[j]);
+
+ // 正規表現が一致した場合は、強調表現処理を行います
+ if (entry.hasKeywords(currentSearchWords, regexpType)) {
+ // 強調表現を実行します
+ entry.title = complexEmphasize(currentSearchWords.join("|"), entry.title, regexpType);
+ entry.content = complexEmphasize(currentSearchWords.join("|"), entry.content, regexpType);
+
+ fetchEntriesSemaphore.buf.push(entry);
+ }
+ }
+
+ // セマフォのカウンタを減少させます (Ajaxとの同期のため)
+ fetchEntriesSemaphore.count--;
+
+ // 全てのログを読み終わったら表示
+ if (fetchEntriesSemaphore.count == 0) {
+ var entries = fetchEntriesSemaphore.buf;
+
+ // 一軒も検索にヒットしなかった場合は専用のパネルを表示して終了
+ if (entries.length == 0) {
+ notFoundError();
+ return;
+ }
+
+ // entryを更新時間でソート
+ entries = entries.sort(function(a, b) {
+ a = a.updated;
+ b = b.updated;
+ return a > b ? -1 : a < b ? 1 : 0;
+ });
+
+ loadedEntries = entries;
+
+ // 表示ロジック呼び出し
+ showEntriesRange(showLength, 0);
+ }
+}
+
+/**
+ * 「探索」ボタンを押されたときに呼び出されるメソッドです
+ */
+function searchDiary() {
+ // 検索結果フィールドをクリアします
+ document.getElementById("feedblog_writearea").innerHTML = "";
+
+ // 探索したい単語を取得します
+ currentSearchWords = getSearchWords();
+ if (!currentSearchWords) {
+ alert("検索対象の単語が入力されていません");
+ // 検索結果の欄をリセットします
+ $("#feedblog_resultwritearea").html("<div class='feedblog_result_status'></div>");
+ return;
+ }
+
+ // ロードエフェクトを表示します
+ loadingEffect();
+
+ // 全チェックを取得します
+ var allCheckedFlag = document.getElementById("feedblog_allsearchcheck").checked;
+
+ // セマフォを初期化
+ fetchEntriesSemaphore.init();
+ // 記事が全検索モードか否かをチェックします
+ var urls = null;
+ if (allCheckedFlag == true) {
+ // 全記事検索なので全てのログのURL
+ urls = logData;
+ } else {
+ // 単独記事探索なので、選んだログのURL
+ urls = [document.getElementById("feedblog_logbox").options[document.getElementById("feedblog_logbox").selectedIndex].value];
+ }
+ fetchEntriesSemaphore.urls = urls;
+ fetchEntriesSemaphore.count = urls.length;
+ for ( i = 0; i < urls.length; i++) {
+ var xhr = new jQuery.ajax({
+ url : urls[i] + '?time=' + urlSuffix,
+ method : "GET",
+ async : true,
+ success : fetchEntries,
+ error : showError
+ });
+ fetchEntriesSemaphore.xhrs.push(xhr);
+ }
+}