OSDN Git Service

fixed : when searchng diary, still not load yet, show error message.
[feedblog/feedblog_ext.git] / js / lunardial / feedblog_ext.js
1 /**
2  * FeedBlog CoreScript Ext Version
3  *
4  * @copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
5  * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
6  * @since 2008/10/16
7  * @version 3.1.0.0
8  */
9 // ブログ本体のHTMLファイルの名前を記入してください
10 var blogUrl = "./index.html"
11
12 // 最新の日記を示すパスへの文字列です。最新の日記を置く場所を変えたいときは変更してください。
13 var latestXml = "./xml/diary.xml";
14
15 // ログのリストが書かれたXMLのファイルパスを記入してください
16 var logXmlUrl = "./xml/loglist.xml";
17
18 // Ext jsパネルのサイズを記述してください
19 var extPanelWidth = 580;
20
21 // ログを表示するコンボボックスのサイズを記述してください
22 var extComboWidth = 150;
23
24 // 日記間のスパン(間隔)をPIXEL単位で記述してください
25 var entrySpan = 3;
26
27 // 一画面あたりの表示日記数です
28 var showLength = 3;
29
30 // 検索結果をメモリ上に保持する変数です
31 var loadedEntries;
32
33 /**
34  * XMLファイルから読み込んだファイルのバリデートモードを選択します。
35  * 0 = 改行コード部分に<br/>を挿入
36  * 1 = 改行コード部分に<br/>を挿入しない
37  */
38 var validateMode = "1";
39
40 /**
41  * Ext jsパネルを実際に生成します。この部分を編集することでデザインを変更可能です。
42  * @param {String} title パネルのタイトル部分に表示する文字列
43  * @param {String} drawitem パネルの本文を格納したDIV要素のid
44  * @param {String} renderto 「タイトル・更新日時・本文」の1日分の日記データを焼き付けるDIV要素のid
45  * @param {String} closed (Ext jsパネルオプション)日記をクローズ状態で生成するか否か
46  */
47 function generatePanel(title, drawitem, renderto, closed){
48     // Ext jsパネルを生成する
49     new Ext.Panel({
50         contentEl: drawitem,
51         width: extPanelWidth,
52         title: title,
53         hideCollapseTool: false,
54         titleCollapse: true,
55         collapsible: true,
56         collapsed: closed,
57         renderTo: renderto
58     });
59 }
60
61 /**
62  * Extへのイベント登録です。すべてのDOMが利用可能になった時点で実行されます。
63  */
64 $(document).ready(function(){
65     // 特定の過去ログの探索モードか否かを判別するためにハッシュを取得する
66     var urlhash = "" + location.hash.substring(1);
67     
68     // ハッシュが空か、ハッシュ形式の正規表現に一致しないようなら通常モードで実行
69     if (urlhash.length == 0) {
70         xmlLoader(latestXml);
71         logXMLLoader();
72     }
73     // ハッシュ形式の正規表現に一致したら探索モード
74     else {
75         searchMode(urlhash);
76         logXMLLoader();
77     }
78 });
79
80 /**
81  * 記事クラス
82  * @param {Object} obj entry 要素の DOM オブジェクト
83  */
84 function Entry(obj){
85     this.title = $("title:first", obj).text();
86     if (this.title == "") 
87         requiredElementError(obj, "title");
88     this.title = "<span>" + validateText(this.title) + "</span>";
89     this.content = $("content:first", obj).text();
90     this.content = "<span>" + validateText(this.content) + "</span>";
91     this.id = $("id:first", obj).text();
92     if (this.id == "") 
93         requiredElementError(obj, "id");
94     this.date = $("updated:first", obj).text();
95     if (this.date == "") 
96         requiredElementError(obj, "updated");
97     this.date = validateData(this.date);
98 }
99
100 /**
101  * 呼び出すとDIV:id名:writeArea上のHTMLを削除し、ロードエフェクトを表示します
102  */
103 function loadingEffect(){
104     document.getElementById("writeArea").innerHTML = '<div id="drawPanel"><div id="drawItem" class="code" style="text-align: center;"><\/div><\/div>';
105     document.getElementById("drawItem").innerHTML = '<br/><img src="./js/ext/resources/images/default/shared/blue-loading.gif"><br/>長時間画面が切り替わらない場合はページをリロードしてください。<br/><br/>';
106     
107     // ロード表示用のパネルを生成
108     generatePanel("Now Loading .....", "drawItem", "drawPanel", false);
109 }
110
111 /**
112  * 日記データのエラー時の処理を行います
113  */
114 function requiredElementError(parent, name){
115     Ext.Msg.alert("Error!", parent.ownerDocument.URL + ": 必須な要素 " +
116     name +
117     " が存在しないか空な " +
118     parent.tagName +
119     " 要素が存在します");
120 }
121
122 function xmlAttrContentEscape(str){
123     // return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
124     return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/^[ ]+/mg, "&nbsp;").replace(/^[\t]+/mg, "");
125 }
126
127 /**
128  * 日付のHTML表示用バリデーション処理を行います
129  * @param {String} data RFC3339形式のdate-time文字列
130  */
131 function validateData(data){
132     var regT = new RegExp("T", "gm");
133     data = data.replace(regT, " ");
134     
135     // 秒数の小数点以下の部分はカットする
136     data = data.substring(0, 19);
137     
138     return data;
139 }
140
141 /**
142  * 日記本文のバリデーション処理を行います
143  * @param {String} contents 日記の本文が格納されている文字列
144  */
145 function validateText(contents){
146     // <br/>タグを挿入する
147     if (validateMode == 0) {
148         contents = contents.replace(/[\n\r]|\r\n/g, "<br />");
149     }
150     
151     return contents;
152 }
153
154 /**
155  * 日記本文に日付を付加します
156  * @param {String} contents 日記の本文が格納されている文字列
157  * @param {String} id 日記の初公開日を示す日付文字列
158  */
159 function contentsWithid(contents, id){
160     // リンク用文末作成
161     var hashTag = '<br/><div style="text-align: right;"><a href="' +
162     xmlAttrContentEscape(blogUrl) +
163     '#' +
164     xmlAttrContentEscape(id) +
165     '" target="_blank">- この日の記事にリンクする -<\/a><\/span>';
166     return contents + hashTag;
167 }
168
169 /**
170  * ログファイル選択用のコンボボックスをid名:logSelecterに生成します
171  */
172 function logXMLLoader(){
173     // レコード構造を定義します
174     var PathRecord = new Ext.data.Record.create([{
175         name: "display",
176         type: "string"
177     }, {
178         name: "path",
179         type: "string"
180     }]);
181     
182     // ログ用のXMLを読み込みます
183     var logXMLData = new Ext.data.Store();
184     jQuery.ajax({
185         url: logXmlUrl,
186         method: "GET",
187         error: showError,
188         success: function(xmlData){
189             var separateTag = xmlData.getElementsByTagName("file");
190             
191             // 読み込んだ要素をStoreに格納して表示
192             for (var i = 0; i < separateTag.length; i++) {
193                 // レコードに登録する
194                 var record = new PathRecord({
195                     path: separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue,
196                     display: separateTag[i].getElementsByTagName("display")[0].firstChild.nodeValue
197                 });
198                 logXMLData.add(record);
199             }
200             
201             // コンボボックス要素を生成
202             document.getElementById("logSelecter").innerHTML = "<input type='text' id='logbox' style='width: " + extComboWidth + "px'/>";
203             
204             // コンボボックスを生成
205             var comboBox = new Ext.form.ComboBox({
206                 store: logXMLData,
207                 applyTo: "logbox",
208                 displayField: "display",
209                 valueField: "path",
210                 mode: "local",
211                 triggerAction: "all",
212                 emptyText: "ログを選択してください..."
213             });
214             
215             // コンボボックスのイベント登録
216             comboBox.on("change", function(){
217                 xmlLoader("" + comboBox.getValue());
218             });
219         }
220     });
221 }
222
223 /**
224  * 日記のデータが記述されたXMLデータを読み込むロジックを生成します
225  * @param {String} fileName 読み込み日記のデータが記述されているXMLファイルのパス
226  */
227 function xmlLoader(fileName){
228     // ロードエフェクトに切り替え
229     loadingEffect()
230     
231     var url = fileName;
232     
233     // 日記をロードします
234     var loader = new jQuery.ajax({
235         url: url,
236         method: "POST",
237         success: writeHtml,
238         error: showError
239     });
240 }
241
242 /**
243  * 日記データのエラー時の処理を行います
244  */
245 function showError(){
246     document.getElementById("writeArea").innerHTML = '<div id="drawPanel"><div id="drawItem" class="code" style="text-align: center;"><\/div><\/div>';
247     document.getElementById("drawItem").innerHTML = '<br/>日記ファイルのロードに失敗しました!<br/><br/>';
248     
249     // エラー内容をパネルに描画
250     generatePanel("Error!", "drawItem", "drawPanel", false);
251     
252     Ext.Msg.alert("Error!", "日記ファイルが読み込めません!");
253 }
254
255 /**
256  * 渡された文字列と一致するfeed1.0:updated要素を持った日記を検索し、表示します
257  * @param {String} urlhash feed1.0:updated要素と一致する文字列
258  */
259 function searchMode(urlhash){
260     // ロードエフェクト表示
261     loadingEffect();
262     
263     // ログXMLファイルを読み込む
264     var loader = new jQuery.ajax({
265         url: logXmlUrl,
266         method: "POST",
267         error: showError,
268         success: function(xmlData){
269             // ファイルパスの要素のみを抽出する
270             var separateTag = xmlData.getElementsByTagName("file");
271             var filelist = new Array(separateTag.length);
272             
273             // すべてのファイルパスを配列に格納する
274             for (var i = 0; i < separateTag.length; i++) {
275                 // "path"ノードの値を格納
276                 filelist[i] = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
277             }
278             
279             // 終端まで検索した日記XMLファイルの数を格納する変数
280             var searchFileNumbers = 0;
281             
282             // ファイルパス配列に格納されているすべての日記に対し、探索を開始する
283             for (var i = 0; i < separateTag.length; i++) {
284                 // ファイルパス配列の要素からリクエストを生成し、対象データをロードする
285                 var searchlog = new jQuery.ajax({
286                     url: filelist[i],
287                     method: "POST",
288                     success: function(xmlData){
289                         // entry要素のみを切り出す
290                         var searchSeparateTag = xmlData.getElementsByTagName("entry");
291                         
292                         for (var j = 0; j < searchSeparateTag.length; j++) {
293                             // entryタグ内部のidノードの値のみ抽出し、入力されたhashと比較を行う
294                             var id = searchSeparateTag[j].getElementsByTagName("id")[0].firstChild.nodeValue;
295                             
296                             // idの値と比較を行う
297                             if (urlhash == id) {
298                                 var entry = new Entry(searchSeparateTag[j]);
299                                 
300                                 document.getElementById("writeArea").innerHTML = '<div><table class="pager" width="' + extPanelWidth + '" cellspacing="1"><tbody>' +
301                                 '<tr><td class="pager" colspan="3">1件~1件(全1件)目の記事を表示中<br/></td></tr>' +
302                                 '<tr><td align="left"><<< 前の3件を表示</td><td align="center">[ 0 ]</td><td align="right">次の3件を表示 >>></td></tr></tbody></table></div>' +
303                                 '<div style="line-height: ' +
304                                 entrySpan +
305                                 'px;"><br/></div>' +
306                                 '<div id="drawPanel"><div id="drawItem" class="code"><\/div><\/div>' +
307                                 '<div style="line-height: ' +
308                                 entrySpan +
309                                 'px;"><br/></div>' +
310                                 '<div><table class="pager" width="' +
311                                 extPanelWidth +
312                                 '" cellspacing="1"><tbody>' +
313                                 '<tr><td align="left"><<< 前の3件を表示</td><td align="center">[ 0 ]</td><td align="right">次の3件を表示 >>></td></tr>' +
314                                 '<tr><td class="pager" colspan="3">1件~1件(全1件)目の記事を表示中<br/></td></tr></tbody></table></div>';
315                                 document.getElementById("drawItem").innerHTML = contentsWithid(entry.content, entry.id);
316                                 
317                                 // 探索されたパネルはオープン状態で展開する
318                                 generatePanel(entry.title + " / " + entry.date, "drawItem", "drawPanel", false);
319                                 
320                                 return true;
321                             }
322                             else 
323                                 if (j == searchSeparateTag.length - 1) {
324                                     // 日記を最後まで検索してもファイルが見つからなかった場合、検索済み日記ファイル数に1加算する
325                                     searchFileNumbers = searchFileNumbers + 1;
326                                     
327                                     // もうファイルがない場合はエラーを表示する
328                                     if (searchFileNumbers == separateTag.length) {
329                                         // ファイルパス配列から日記が見つからなかった場合の処理
330                                         document.getElementById("writeArea").innerHTML = '<div id="drawPanel"><div id="drawItem" class="code"><\/div><\/div>';
331                                         document.getElementById("drawItem").innerHTML = "指定された日記は存在しません。";
332                                         
333                                         // エラー内容を表示する
334                                         generatePanel("Search Failed.", "drawItem", "drawPanel", false);
335                                     }
336                                 }
337                         }
338                         
339                     }
340                 });
341             }
342         }
343     });
344 }
345
346 /**
347  * 検索結果を分割して表示します
348  * @param {int} showLength 一回の画面に表示する記事数
349  * @param {int} startIndex 表示を開始する日記のインデックス
350  */
351 function showEntriesRange(showLength, startIndex){
352     // メモリ上から日記データをロード
353     var entries = loadedEntries;
354     
355     // 表示インデックスが範囲外の場合はエラーパネルを表示して終了
356     if (startIndex < 0 || (entries.length <= startIndex && entries.length != 0)) {
357         showError();
358         return;
359     }
360     
361     var stringBuffer = [];
362     
363     // リミッターを設定する
364     var loopLimit = (showLength + startIndex > entries.length) ? entries.length : showLength + startIndex;
365     var indexShowEntries = loopLimit + 1;
366     
367     // 情報メニュー表示用バッファです
368     var menuInfoBuffer = [];
369     menuInfoBuffer.push("<tr><td colspan='3' class='pager'>");
370     var viewStartIndex = 0;
371     entries.length == 0 ? viewStartIndex = 0 : viewStartIndex = startIndex + 1
372     menuInfoBuffer.push(viewStartIndex + "件~" + loopLimit + "件(全" + entries.length + "件)目の記事を表示中<br/>");
373     menuInfoBuffer.push("</td></tr>");
374     
375     // ページ移動メニュー表示用バッファです
376     var menuMoveBuffer = [];
377     menuMoveBuffer.push("<tr>");
378     // 左パネルの表示制御
379     if (startIndex - showLength >= 0) {
380         menuMoveBuffer.push("\<td align='left'><a href='' onclick='showEntriesRange(" +
381         showLength +
382         ", " +
383         (startIndex - showLength) +
384         "); return false;'>\<\<\< 前の" +
385         showLength +
386         "件を表示</a\></td>");
387     }
388     else {
389         menuMoveBuffer.push("\<td align='left'>\<\<\< 前の" +
390         showLength +
391         "件を表示</a\></td>");
392     }
393     
394     // 中央のパネルの表示制御
395     menuMoveBuffer.push("<td align='center'>[ ");
396     var menuNumbers = Math.ceil(entries.length / showLength);
397     for (i = 0; i < menuNumbers; i++) {
398         if (startIndex / showLength == i) {
399             menuMoveBuffer.push(i + " ");
400         }
401         else {
402             menuMoveBuffer.push("<a href='' onclick='showEntriesRange(" +
403             showLength +
404             ", " +
405             (i * showLength) +
406             "); return false;'>");
407             menuMoveBuffer.push(i);
408             menuMoveBuffer.push("</a> ");
409         }
410     }
411     menuMoveBuffer.push("]</td>");
412     
413     // 右パネルの表示制御
414     if (entries.length > startIndex + showLength) {
415         menuMoveBuffer.push("\<td align='right'><a href='' onclick='showEntriesRange(" +
416         showLength +
417         ", " +
418         (startIndex + showLength) +
419         "); return false;'>\次の" +
420         showLength +
421         "件を表示 \>\>\></a\></td>");
422     }
423     else {
424         menuMoveBuffer.push("\<td align='right'>次の" +
425         showLength +
426         "件を表示 \>\>\></a\></td>");
427     }
428     menuMoveBuffer.push("</tr>");
429     
430     // メニューを結合してパネル(前方)に組み込みます
431     stringBuffer.push("<div><table cellspacing='1' width='" +
432     extPanelWidth +
433     "px' class='pager'><tbody>");
434     stringBuffer.push(menuInfoBuffer.join(""));
435     stringBuffer.push(menuMoveBuffer.join(""));
436     stringBuffer.push("</tbody></table></div>");
437     
438     stringBuffer.push('<div style="line-height: ' + entrySpan + 'px;"><br/></div>');
439     
440     // 日記描画部分のパネルを生成します
441     for (var i = startIndex; i < loopLimit; i++) {
442         stringBuffer.push('<div id="drawPanel');
443         stringBuffer.push(i);
444         stringBuffer.push('"><div id="drawItem');
445         stringBuffer.push(i);
446         stringBuffer.push('" class="code"><\/div><\/div><div style="line-height: ' + entrySpan + 'px;"><br/></div>')
447     }
448     
449     // メニューを結合してパネル(後方)に組み込みます
450     stringBuffer.push("<div><table cellspacing='1' width='" +
451     extPanelWidth +
452     "px' class='pager'><tbody>");
453     stringBuffer.push(menuMoveBuffer.join(""));
454     stringBuffer.push(menuInfoBuffer.join(""));
455     stringBuffer.push("</tbody></table></div>");
456     
457     document.getElementById("writeArea").innerHTML = stringBuffer.join("");
458     
459     for (var i = startIndex; i < loopLimit; i++) {
460         // 各要素をオブジェクトに格納します
461         var entry = entries[i];
462         document.getElementById("drawItem" + i).innerHTML = contentsWithid(entry.content, entry.id);
463         
464         // すべてのパネルをオープン状態で生成します
465         generatePanel(entry.title + " / " + entry.date, "drawItem" + i, "drawPanel" + i, false);
466     }
467 }
468
469 /**
470  * 日記のログファイルデータが記述されているXMLファイルを読み込んで表示します。DIV:id名:writeArea上に読み込んだ日記の内容を表示します
471  * @param {Object} xmlData 日記が記述されたXMLファイル(feed 1.0準拠)
472  */
473 function writeHtml(xmlData){
474     var separateTag = xmlData.getElementsByTagName("entry");
475     var stringBuffer = [];
476     // メモリ上での保持変数を初期化します
477     loadedEntries = [];
478     
479     // メモリ上の変数に全ての日記要素を格納します
480     for (var i = 0; i < separateTag.length; i++) {
481         loadedEntries.push(new Entry(separateTag[i]));
482     }
483     
484     // 表示ロジック呼び出し
485     showEntriesRange(showLength, 0);
486 }
487