OSDN Git Service

- add tag function.
[feedblog/feedblog.git] / js / lunardial / feedblog.js
1 /**\r
2  * FeedBlog CoreScript\r
3  *\r
4  * @copyright 2013 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)\r
5  * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)\r
6  * @since 2009/02/27\r
7  * @version 4.1.0.0\r
8  */\r
9 \r
10 // ブログ本体のHTMLファイルのURL\r
11 var mainPageUrl;\r
12 \r
13 // 検索用ページURL\r
14 var searchPageUrl;\r
15 \r
16 // 最新の記事を示すパスへの文字列\r
17 var latestXml;\r
18 \r
19 // ログのリストが書かれたXMLのファイルパス\r
20 var logXmlUrl;\r
21 \r
22 // 一画面あたりの表示記事数\r
23 var showLength;\r
24 \r
25 /**\r
26  * XMLファイルから読み込んだファイルのバリデートモード\r
27  * 0 = 改行コード部分に<br/>を挿入\r
28  * 1 = 改行コード部分に<br/>を挿入しない\r
29  */\r
30 var validateMode;\r
31 \r
32 // 検索結果をメモリ上に保持する変数\r
33 var loadedEntries;\r
34 \r
35 // fetchEntries 用のセマフォ\r
36 var fetchEntriesSemaphore = new Semaphore();\r
37 \r
38 /**\r
39  * 記事を実際に生成します。この部分を編集することでデザインを変更可能です。\r
40  * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト\r
41  * @param {String} drawitem 「本文」を描画すべきパネルのDIV要素のid\r
42  * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを最終的に描画すべきパネルのDIV要素のid\r
43  * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か\r
44  */\r
45 function generatePanel(entry, drawitem, renderto, closed) {\r
46         // プラグインを実行\r
47         if( typeof ($("#" + renderto).feedblog_contents_plugin) == "function") {\r
48                 $("#" + renderto).feedblog_contents_plugin({\r
49                         entry : entry,\r
50                         mainPageUrl : mainPageUrl,\r
51                         searchPageUrl : searchPageUrl\r
52                 });\r
53         }\r
54 \r
55         // HTML用の配列を用意する\r
56         var htmlBuffer = [];\r
57 \r
58         // 内部的に描画先IDを生成\r
59         var feedblogContentId = "" + renderto + "_content_div";\r
60 \r
61         // 各要素をオブジェクトに描画\r
62         $("#" + drawitem).html(entry.content);\r
63 \r
64         // ヘッダパネルを生成 class= .feedblog_header\r
65         htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +\r
66 \r
67         // 本体記事を作成 class= .feedblog_content\r
68         "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");\r
69 \r
70         // 最終描画実施\r
71         $("#" + renderto).html(htmlBuffer.join(""));\r
72 }\r
73 \r
74 /**\r
75  * システム表示画面を実際に生成します。この部分を編集することでデザインを変更可能です。\r
76  * @param {Entry} entry 記事の情報が代入されたEntryオブジェクト\r
77  * @param {String} drawitem パネルの本文を格納したDIV要素のid\r
78  * @param {String} renderto 「タイトル・更新日時・本文」の1日分の記事データを焼き付けるDIV要素のid\r
79  * @param {String} closed (Ext jsパネルオプション)記事をクローズ状態で生成するか否か\r
80  */\r
81 function generateSystemPanel(entry, drawitem, renderto, closed) {\r
82         // HTMLを生成する\r
83         var htmlBuffer = [];\r
84 \r
85         // 描画先IDを生成\r
86         var feedblogContentId = "" + renderto + "_content_div";\r
87 \r
88         // 各要素をオブジェクトに描画\r
89         $("#" + drawitem).html(entry.content);\r
90 \r
91         // ヘッダパネルを生成 class= .feedblog_header\r
92         htmlBuffer.push("<div class='feedblog_header' onclick='closePanel(\"" + feedblogContentId + "\")'><span>" + entry.title + "</span></div>" +\r
93 \r
94         // 本体記事を作成 class= .feedblog_content\r
95         "<div class='feedblog_content' id='" + feedblogContentId + "'><span>" + document.getElementById(renderto).innerHTML + "</span></div>");\r
96 \r
97         $("#" + renderto).html(htmlBuffer.join(""));\r
98 }\r
99 \r
100 /**\r
101  * 全ての定数を取得・セットします\r
102  */\r
103 function initialize() {\r
104         mainPageUrl = $("#feedblog_mainpageurl").val();\r
105         searchPageUrl = $("#feedblog_searchpageurl").val();\r
106         latestXml = $("#feedblog_latestxml").val();\r
107         logXmlUrl = $("#feedblog_loglistxmlurl").val();\r
108         showLength = parseInt($("#feedblog_showlength").val());\r
109         if(isNaN(showLength)) {\r
110                 showLength = 1;\r
111         }\r
112         validateMode = $("#feedblog_validatemode").val();\r
113 }\r
114 \r
115 /**\r
116  * jQueryへのイベント登録です。すべてのDOMが利用可能になった時点で実行されます。\r
117  */\r
118 $(document).ready(function() {\r
119         // 初期処理を実施\r
120         initialize();\r
121 \r
122         // 制御に必要な各種パラメタを取得する\r
123         var tag = getTagFromUrl();\r
124         var urlhash = getHashFromUrl();\r
125 \r
126         // ハッシュが空か、ハッシュ形式の正規表現に一致しないようなら通常モードで実行\r
127         if(urlhash.length == 0 && tag.length == 0) {\r
128                 fullWriteMode(latestXml);\r
129                 logXMLLoader();\r
130         } else if(urlhash.length == 0) {\r
131                 // タグが指定されているのでタグ探索モード\r
132                 searchTagMode(tag);\r
133                 logXMLLoader();\r
134         } else {\r
135                 // ハッシュ形式の正規表現に一致したら探索モード\r
136                 searchHashMode(urlhash);\r
137                 logXMLLoader();\r
138         }\r
139 });\r
140 \r
141 /**\r
142  * jQueryでのパネル開閉を制御します\r
143  */\r
144 function closePanel(id) {\r
145         $("#" + id).slideToggle();\r
146 }\r
147 \r
148 /**\r
149  * 記事クラス\r
150  * @param {Object} obj entry 要素の DOM オブジェクト\r
151  */\r
152 function Entry(obj) {\r
153         this.title = $("title:first", obj).text();\r
154         if(this.title == "")\r
155                 requiredElementError(obj, "title");\r
156         this.title = validateText(this.title);\r
157         this.content = $("content:first", obj).text();\r
158         this.content = validateText(this.content);\r
159         this.id = $("id:first", obj).text();\r
160         if(this.id == "")\r
161                 requiredElementError(obj, "id");\r
162         this.date = $("updated:first", obj).text();\r
163         if(this.date == "")\r
164                 requiredElementError(obj, "updated");\r
165         this.date = validateData(this.date);\r
166         this.category = $("category", obj);\r
167 }\r
168 \r
169 /**\r
170  * システム用記事クラス\r
171  * @param {Object} obj entry 要素の DOM オブジェクト\r
172  */\r
173 function SystemEntry(obj) {\r
174         this.title = $("title:first", obj).text();\r
175         this.title = validateText(this.title);\r
176         this.content = $("content:first", obj).text();\r
177         this.content = validateText(this.content);\r
178         this.id = $("id:first", obj).text();\r
179         this.date = $("updated:first", obj).text();\r
180         this.date = validateData(this.date);\r
181         this.category = $("category", obj);\r
182 }\r
183 \r
184 /**\r
185  * 呼び出すとDIV:id名:feedblog_writearea上のHTMLを削除し、ロードエフェクトを表示します\r
186  */\r
187 function loadingEffect() {\r
188         $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem">&nbsp;<\/div><\/div>');\r
189 \r
190         // ロード表示用のパネルを生成\r
191         var systemEntry = new SystemEntry();\r
192         systemEntry.title = "Now Loading .....";\r
193         systemEntry.content = '<br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';\r
194         generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
195 }\r
196 \r
197 /**\r
198  * 記事データのエラー時の処理を行います\r
199  */\r
200 function showError() {\r
201         $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem">&nbsp;<\/div><\/div>');\r
202 \r
203         // エラー内容をパネルに描画\r
204         var systemEntry = new SystemEntry();\r
205         systemEntry.title = "エラー";\r
206         systemEntry.content = '<br/>記事ファイルのロードに失敗しました!<br/><br/>';\r
207         generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
208 \r
209         alert("記事ファイルが読み込めません!");\r
210 }\r
211 \r
212 /**\r
213  * 記事データのエラー時の処理を行います\r
214  */\r
215 function notFoundError() {\r
216         $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem">&nbsp;<\/div><\/div>');\r
217 \r
218         // エラー内容をパネルに描画\r
219         var systemEntry = new SystemEntry();\r
220         systemEntry.title = "検索失敗";\r
221         systemEntry.content = '<br/>検索条件に一致する記事は見つかりませんでした。<br/><br/>';\r
222         generateSystemPanel(systemEntry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
223 }\r
224 \r
225 /**\r
226  * 記事データのエラー時の処理を行います\r
227  */\r
228 function requiredElementError(parent, name) {\r
229         alert(parent.ownerDocument.URL + ": 必須な要素 " + name + " が存在しないか空な " + parent.tagName + " 要素が存在します");\r
230 }\r
231 \r
232 function xmlAttrContentEscape(str) {\r
233         return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/^[ ]+/mg, "&nbsp;").replace(/^[\t]+/mg, "");\r
234 }\r
235 \r
236 /**\r
237  * 日付のHTML表示用バリデーション処理を行います\r
238  * @param {String} data RFC3339形式のdate-time文字列\r
239  */\r
240 function validateData(data) {\r
241         var regT = new RegExp("T", "gm");\r
242         data = data.replace(regT, " ");\r
243 \r
244         // 秒数の小数点以下の部分はカットする\r
245         data = data.substring(0, 19);\r
246 \r
247         return data;\r
248 }\r
249 \r
250 /**\r
251  * 記事本文のバリデーション処理を行います\r
252  * @param {String} contents 記事の本文が格納されている文字列\r
253  */\r
254 function validateText(contents) {\r
255         // <br/>タグを挿入する\r
256         if(validateMode == 0) {\r
257                 contents = contents.replace(/[\n\r]|\r\n/g, "<br />");\r
258         }\r
259 \r
260         return contents;\r
261 }\r
262 \r
263 /**\r
264  * URLからタグを取得するための関数\r
265  */\r
266 function getTagFromUrl() {\r
267         // GETパラメタよりタグを取得する\r
268         var tag = "";\r
269         if(location.search.length > 1) {\r
270                 var queries = location.search.substring(1).split('&');\r
271                 for(var i = 0; i < queries.length; i++) {\r
272                         if(("" + queries[i].split('=')[0]) == "tag") {\r
273                                 tag = "" + queries[i].split('=')[1];\r
274                         }\r
275                 }\r
276         }\r
277 \r
278         return tag;\r
279 }\r
280 \r
281 /**\r
282  * URLからハッシュを取得するための関数\r
283  */\r
284 function getHashFromUrl() {\r
285         return "" + location.hash.substring(1);\r
286 }\r
287 \r
288 /**\r
289  * 長い順に並べるための比較関数です\r
290  * @param {String} a 比較対象(1)\r
291  * @param {String} b 比較対象(2)\r
292  */\r
293 function compareLengthDecrease(a, b) {\r
294         a = a.length;\r
295         b = b.length;\r
296         return a > b ? -1 : a < b ? 1 : 0;\r
297 }\r
298 \r
299 /**\r
300  * セマフォ制御用のオブジェクトです\r
301  */\r
302 function Semaphore() {\r
303         this.id = null;\r
304         this.count = 0;\r
305         this.buf = [];\r
306         this.xhrs = [];\r
307 }\r
308 \r
309 /**\r
310  * セマフォ初期化用の関数です\r
311  */\r
312 Semaphore.prototype.init = function() {\r
313         while(this.xhrs.length > 0) {\r
314                 this.xhrs.shift().abort();\r
315         }\r
316         this.id = Math.random();\r
317         this.count = 0;\r
318         this.buf = [];\r
319 }\r
320 /**\r
321  * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します\r
322  * class - .feedblog_logform, .feedblog_logselecter\r
323  */\r
324 function logXMLLoader() {\r
325         // ログ用のXMLを読み込みます\r
326         jQuery.ajax({\r
327                 url : logXmlUrl + '?time=' + (+new Date()),\r
328                 method : "GET",\r
329                 error : showError,\r
330                 success : function(xmlData) {\r
331                         var separateTag = xmlData.getElementsByTagName("file");\r
332 \r
333                         // 読み込んだ要素をStoreに格納して表示\r
334                         var boxBuffer = [];\r
335                         boxBuffer.push("<form class='feedblog_logselecter' name='feedblog_logform'><select class='feedblog_logselecter' id='feedblog_logbox' onchange='fullWriteMode(this.options[this.selectedIndex].value)'>");\r
336                         for(var i = 0; i < separateTag.length; i++) {\r
337                                 boxBuffer.push("<option value='" + separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue + "'>" + separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue + "</option>");\r
338                         }\r
339                         boxBuffer.push("</select></form>");\r
340 \r
341                         // コンボボックス要素を生成\r
342                         $("#feedblog_logselecter").html(boxBuffer.join(""));\r
343                 }\r
344         });\r
345 }\r
346 \r
347 /**\r
348  * 記事のデータが記述されたXMLデータを読み込むロジックを生成します\r
349  * @param {String} fileName 読み込み記事のデータが記述されているXMLファイルのパス\r
350  */\r
351 function fullWriteMode(fileName) {\r
352         // ロードエフェクトに切り替え\r
353         loadingEffect()\r
354 \r
355         var url = fileName;\r
356 \r
357         // 記事をロードします\r
358         var loader = new jQuery.ajax({\r
359                 url : url + '?time=' + (+new Date()),\r
360                 method : "GET",\r
361                 success : function(xmlData) {\r
362                         var separateTag = xmlData.getElementsByTagName("entry");\r
363                         var stringBuffer = [];\r
364                         // メモリ上での保持変数を初期化します\r
365                         loadedEntries = [];\r
366 \r
367                         // メモリ上の変数に全ての記事要素を格納します\r
368                         for(var i = 0; i < separateTag.length; i++) {\r
369                                 loadedEntries.push(new Entry(separateTag[i]));\r
370                         }\r
371 \r
372                         // 表示ロジック呼び出し\r
373                         showEntriesRange(showLength, 0);\r
374                 },\r
375                 error : showError\r
376         });\r
377 }\r
378 \r
379 /**\r
380  * 渡された文字列と一致するfeed1.0:updated要素を持った記事を検索し、表示します\r
381  * @param {String} urlhash feed1.0:updated要素と一致する文字列\r
382  */\r
383 function searchHashMode(urlhash) {\r
384         // ロードエフェクト表示\r
385         loadingEffect();\r
386 \r
387         // ログXMLファイルを読み込む\r
388         var loader = new jQuery.ajax({\r
389                 url : logXmlUrl + '?time=' + (+new Date()),\r
390                 method : "GET",\r
391                 error : showError,\r
392                 success : function(xmlData) {\r
393                         // ファイルパスの要素のみを抽出する\r
394                         var separateTag = xmlData.getElementsByTagName("file");\r
395                         var urls = new Array(separateTag.length);\r
396 \r
397                         // すべてのファイルパスを配列に格納する\r
398                         for(var i = 0; i < separateTag.length; i++) {\r
399                                 // "path"ノードの値を格納\r
400                                 urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
401                         }\r
402 \r
403                         // セマフォを初期化\r
404                         fetchEntriesSemaphore.init();\r
405                         fetchEntriesSemaphore.urls = urls;\r
406                         fetchEntriesSemaphore.count = urls.length;\r
407 \r
408                         // ファイルパス配列に格納されているすべての記事に対し、探索を開始する\r
409                         for(var i = 0; i < separateTag.length; i++) {\r
410                                 // ファイルパス配列の要素からリクエストを生成し、対象データをロードする\r
411                                 var xhr = new jQuery.ajax({\r
412                                         url : urls[i],\r
413                                         method : "GET",\r
414                                         success : fetchHashEntries\r
415                                 });\r
416                                 fetchEntriesSemaphore.xhrs.push(xhr);\r
417                         }\r
418                 }\r
419         });\r
420 }\r
421 \r
422 /**\r
423  * URLハッシュ検索用のjQueryコールバック関数\r
424  */\r
425 function fetchHashEntries(xmlData) {\r
426         // ハッシュを取得\r
427         var urlhash = getHashFromUrl();\r
428 \r
429         // entry要素のみを切り出す\r
430         var entries = xmlData.getElementsByTagName("entry");\r
431 \r
432         for(var i = 0; i < entries.length; i++) {\r
433                 // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う\r
434                 var entry = new Entry(entries[i]);\r
435 \r
436                 // idの値と比較を行う\r
437                 if(urlhash == entry.id) {\r
438                         // 一致した場合は該当記事を表示する\r
439                         $("#feedblog_writearea").html('<div id="feedblog_drawpanel" class="feedblog_drawpanel"><div id="feedblog_drawitem" class="feedblog_drawitem">&nbsp;<\/div><\/div>');\r
440                         generatePanel(entry, "feedblog_drawitem", "feedblog_drawpanel", false);\r
441 \r
442                         return true;\r
443                 }\r
444 \r
445                 // セマフォのカウンタを減少させます (Ajaxとの同期のため)\r
446                 fetchEntriesSemaphore.count--;\r
447 \r
448                 // 最後のファイルまで探索しても記事が見つからなかった場合はエラーを表示します。\r
449                 if(fetchEntriesSemaphore.count == 0) {\r
450                         var entries = fetchEntriesSemaphore.buf;\r
451 \r
452                         if(entries.length == 0) {\r
453                                 notFoundError();\r
454                                 return false;\r
455                         }\r
456                 }\r
457         }\r
458 }\r
459 \r
460 /**\r
461  * 渡された文字列と一致するfeed1.0:category:termタグ要素を持った記事を検索し、表示します\r
462  * @param {String} urlhash feed1.0:category:term要素と一致する文字列\r
463  */\r
464 function searchTagMode(tag) {\r
465         // ロードエフェクト表示\r
466         loadingEffect();\r
467 \r
468         // ログXMLファイルを読み込む\r
469         var loader = new jQuery.ajax({\r
470                 url : logXmlUrl + '?time=' + (+new Date()),\r
471                 method : "GET",\r
472                 error : showError,\r
473                 success : function(xmlData) {\r
474                         // ファイルパスの要素のみを抽出する\r
475                         var separateTag = xmlData.getElementsByTagName("file");\r
476                         var urls = new Array(separateTag.length);\r
477 \r
478                         // すべてのファイルパスを配列に格納する\r
479                         for(var i = 0; i < separateTag.length; i++) {\r
480                                 // "path"ノードの値を格納\r
481                                 urls[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;\r
482                         }\r
483 \r
484                         // セマフォを初期化\r
485                         fetchEntriesSemaphore.init();\r
486                         fetchEntriesSemaphore.urls = urls;\r
487                         fetchEntriesSemaphore.count = urls.length;\r
488 \r
489                         // ファイルパス配列に格納されているすべての記事に対し、探索を開始する\r
490                         for(var i = 0; i < separateTag.length; i++) {\r
491                                 // ファイルパス配列の要素からリクエストを生成し、対象データをロードする\r
492                                 var xhr = new jQuery.ajax({\r
493                                         url : urls[i],\r
494                                         method : "GET",\r
495                                         success : fetchTagEntries\r
496                                 });\r
497                                 fetchEntriesSemaphore.xhrs.push(xhr);\r
498                         }\r
499                 }\r
500         });\r
501 }\r
502 \r
503 /**\r
504  * タグ検索用のjQueryコールバック関数\r
505  */\r
506 function fetchTagEntries(xmlData) {\r
507         // タグを取得する\r
508         var tag = getTagFromUrl();\r
509 \r
510         // entry要素のみを切り出す\r
511         var entries = xmlData.getElementsByTagName("entry");\r
512 \r
513         for(var j = 0; j < entries.length; j++) {\r
514                 var entry = new Entry(entries[j]);\r
515 \r
516                 for(var k = 0; k < entry.category.length; k++) {\r
517                         // タグのIDが一致したら格納\r
518                         if(tag == entry.category.eq(k).attr("term")) {\r
519                                 // entryを格納する\r
520                                 fetchEntriesSemaphore.buf.push(entry);\r
521                         }\r
522                 }\r
523         }\r
524 \r
525         // セマフォのカウンタを減少させます (Ajaxとの同期のため)\r
526         fetchEntriesSemaphore.count--;\r
527 \r
528         if(fetchEntriesSemaphore.count == 0) {\r
529                 var entries = fetchEntriesSemaphore.buf;\r
530 \r
531                 if(entries.length == 0) {\r
532                         notFoundError();\r
533                         return false;\r
534                 }\r
535 \r
536                 // entryをidでソート\r
537                 entries = entries.sort(function(a, b) {\r
538                         a = a.id;\r
539                         b = b.id;\r
540                         return a > b ? -1 : a < b ? 1 : 0\r
541                 });\r
542 \r
543                 loadedEntries = entries;\r
544 \r
545                 // 表示ロジック呼び出し\r
546                 showEntriesRange(showLength, 0);\r
547         }\r
548 }\r
549 \r
550 /**\r
551  * 検索結果を分割して表示します\r
552  * class - div.feedblog_pager, ul.feedblog_pager, li.feedblog_pager などなど\r
553  * @param {int} showLength 一回の画面に表示する記事数\r
554  * @param {int} startIndex 表示を開始する記事のインデックス\r
555  */\r
556 function showEntriesRange(showLength, startIndex) {\r
557         // メモリ上から記事データをロード\r
558         var entries = loadedEntries;\r
559 \r
560         // 表示インデックスが範囲外の場合はエラーパネルを表示して終了\r
561         if(startIndex < 0 || (entries.length <= startIndex && entries.length != 0)) {\r
562                 showError();\r
563                 return;\r
564         }\r
565 \r
566         var stringBuffer = [];\r
567 \r
568         // リミッターを設定する\r
569         var loopLimit = (showLength + startIndex > entries.length) ? entries.length : showLength + startIndex;\r
570         var indexShowEntries = loopLimit + 1;\r
571 \r
572         // 情報メニュー表示用バッファです\r
573         var menuInfoBuffer = [];\r
574         menuInfoBuffer.push("<div class='feedblog_pager_shownumber'>");\r
575         var viewStartIndex = 0;\r
576         entries.length == 0 ? viewStartIndex = 0 : viewStartIndex = startIndex + 1\r
577         menuInfoBuffer.push(viewStartIndex + "件~" + loopLimit + "件(全" + entries.length + "件)目の記事を表示中<br/>");\r
578         menuInfoBuffer.push("</div>");\r
579 \r
580         // ページ移動メニュー表示用バッファです\r
581         var menuMoveBuffer = [];\r
582         menuMoveBuffer.push("<ul class='feedblog_pager'>");\r
583 \r
584         // ブランクエリアを挟む\r
585         menuMoveBuffer.push("<li class='feedblog_pager_blank'></li>");\r
586 \r
587         // 左パネルの表示制御\r
588         if(startIndex - showLength >= 0) {\r
589                 menuMoveBuffer.push("\<li class='feedblog_pager_goback'><span class='feedblog_pager_goback' onclick='showEntriesRange(" + showLength + ", " + (startIndex - showLength) + "); return false;'>\< 前の" + showLength + "件を表示</span\></li>");\r
590         } else {\r
591                 menuMoveBuffer.push("\<li class='feedblog_pager_goback'>\< 前の" + showLength + "件を表示</a\></li>");\r
592         }\r
593 \r
594         // 中央のパネルの表示制御\r
595         menuMoveBuffer.push("<li class='feedblog_pager_center'>[ ");\r
596         var menuNumbers = Math.ceil(entries.length / showLength);\r
597         for( i = 0; i < menuNumbers; i++) {\r
598                 if(startIndex / showLength == i) {\r
599                         menuMoveBuffer.push(i + " ");\r
600                 } else {\r
601                         menuMoveBuffer.push("<span class='feedblog_pager_center' onclick='showEntriesRange(" + showLength + ", " + (i * showLength) + "); return false;'>");\r
602                         menuMoveBuffer.push(i);\r
603                         menuMoveBuffer.push("</span> ");\r
604                 }\r
605         }\r
606         menuMoveBuffer.push("]</li>");\r
607 \r
608         // 右パネルの表示制御\r
609         if(entries.length > startIndex + showLength) {\r
610                 menuMoveBuffer.push("\<li class='feedblog_pager_gonext'><span class='feedblog_pager_gonext' onclick='showEntriesRange(" + showLength + ", " + (startIndex + showLength) + "); return false;'>\次の" + showLength + "件を表示 \></span\></li>");\r
611         } else {\r
612                 menuMoveBuffer.push("\<li class='feedblog_pager_gonext'>次の" + showLength + "件を表示 \></li>");\r
613         }\r
614 \r
615         // ブランクエリアを挟む\r
616         menuMoveBuffer.push("<li class='feedblog_pager_blank'></li>");\r
617 \r
618         menuMoveBuffer.push("</ul>");\r
619 \r
620         // メニューを結合してパネル(前方)に組み込みます\r
621         stringBuffer.push("<div class='feedblog_pager_wrapper'>");\r
622         stringBuffer.push(menuInfoBuffer.join(""));\r
623         stringBuffer.push(menuMoveBuffer.join(""));\r
624         stringBuffer.push("</div>");\r
625 \r
626         // 記事描画部分のパネルを生成します\r
627         for(var i = startIndex; i < loopLimit; i++) {\r
628                 stringBuffer.push('<div class="feedblog_drawpanel" id="feedblog_drawpanel');\r
629                 stringBuffer.push(i);\r
630                 stringBuffer.push('"><div class="feedblog_drawitem" id="feedblog_drawitem');\r
631                 stringBuffer.push(i);\r
632                 stringBuffer.push('"><\/div><\/div>')\r
633         }\r
634 \r
635         // メニューを結合してパネル(後方)に組み込みます\r
636         stringBuffer.push("<div class='feedblog_pager_wrapper'>");\r
637         stringBuffer.push(menuMoveBuffer.join(""));\r
638         stringBuffer.push(menuInfoBuffer.join(""));\r
639         stringBuffer.push("</div>");\r
640 \r
641         $("#feedblog_writearea").html(stringBuffer.join(""));\r
642 \r
643         for(var i = startIndex; i < loopLimit; i++) {\r
644                 // 各要素をオブジェクトに格納します\r
645                 var entry = entries[i];\r
646 \r
647                 // すべてのパネルをオープン状態で生成します\r
648                 generatePanel(entry, "feedblog_drawitem" + i, "feedblog_drawpanel" + i, false);\r
649         }\r
650 }\r
651 \r