4 * @copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
\r
5 * @author Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
\r
9 // Feex XMLの<content>要素で、<br>を使用しているか?
\r
10 var inputValidateMode = 1;
\r
11 // 出力時に<content>要素に<br>を付加するか否かを格納する変数
\r
12 var outputValidateMode = 1;
\r
13 // ログのリストが書かれたXMLのファイルパスを記入してください
\r
22 // feedblogの設置アドレスを格納する変数
\r
26 * 編集中の内容を反映し、画面に出力します
\r
28 function applyChange() {
\r
29 feedInfo = applyFeedinfo();
\r
30 pageAddr = feedInfo.alternate;
\r
32 if(document.getElementById("entry_title").value == "" || document.getElementById("entry_stdin").value == "") {
\r
33 if(confirm("タイトルか記事が空白です!FeedBlogでの表示時にエラーが出ますがよろしいですか?") == false) {
\r
39 var entry = new Object();
\r
40 var dateRfc3339 = getDate();
\r
41 entry.id = pageAddr + "?" + dateRfc3339;
\r
42 entry.title = document.getElementById("entry_title").value;
\r
44 entry.published = dateRfc3339;
\r
45 entry.updated = dateRfc3339;
\r
46 entry.link = pageAddr + "#" + entry.id;
\r
47 entry.content = document.getElementById("entry_stdin").value.replace(/\r\n/g, "\n");
\r
48 entry.category = getTags();
\r
49 entryList.unshift(entry);
\r
52 refleshEntrylistBox();
\r
54 // 更新後、選択されている項目を、先刻追加した日記に移動する
\r
55 document.getElementById("logBox").selectedIndex = 1;
\r
58 entryList[editIndex].title = document.getElementById("entry_title").value;
\r
59 entryList[editIndex].updated = getDate();
\r
60 entryList[editIndex].content = document.getElementById("entry_stdin").value.replace(/\r\n/g, "\n");
\r
61 entryList[editIndex].category = getTags();
\r
63 document.getElementById("logBox").options[parseInt(editIndex) + 1].text = entryList[editIndex].title;
\r
66 document.getElementById("stdout").value = toXml(feedInfo, entryList);
\r
68 // プレビューエリアにHTMLを表示します
\r
69 document.getElementById("previewArea").innerHTML = entryList[editIndex].content.replace(/\n/g, "<br>");
\r
73 * mixi用に記事を変換し、ウインドウを立ち上げて表示します
\r
75 function openMixiWindow() {
\r
76 // 日記が選択されていない場合は何もしない
\r
82 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
85 var mixi_win = window.open('', 'feedblog_mixi_wrapper', 'width = 850, height = 490, scrollbars=yes');
\r
86 mixi_win.document.open();
\r
87 mixi_win.document.write(window_str);
\r
88 mixi_win.document.close();
\r
92 * mixi用に通常のHTMLを変換します
\r
93 * @param {String} contents 日記の本文が格納されている文字列
\r
95 function mixiWrapper(contents) {
\r
101 while( target_tag = contents.match(/<iframe[^>]*src=["']http:\/\/ext.nicovideo.jp\/thumb\/[A-Za-z]*\d*["'][^>]*>[^<]*<\/iframe>/i)) {
\r
102 // src="..."の部分だけ抜き出す
\r
103 target_element = target_tag[0].match(/src=["'][^"']*["']/i);
\r
104 target_element = '<externalvideo src="NC:' + target_element[0].replace(/src=["']http:\/\/ext.nicovideo.jp\/thumb\/|["']/ig, "") + ':D">';
\r
106 contents = contents.replace(target_tag, target_element);
\r
110 while(contents.match(/<a[^>]*>/)) {
\r
112 target_tag = contents.match(/<a[^>]*>/i);
\r
113 // href="..."の部分のみを抜き出す
\r
114 target_element = target_tag[0].match(/href=["'][^"']*["']/i);
\r
115 // 相対URIが検出された場合、フルに置換する
\r
116 var baseUri = document.location.href.replace(/[^\/]+$/, '');
\r
117 target_element = target_element[0].replace(/\.\//, baseUri).replace(/\.\.\//g, "");
\r
118 // Aタグ全体を消去し、再度Aクローズタグの置換を行う
\r
119 contents = contents.replace(target_tag, "");
\r
120 contents = contents.replace(/<\/a>/i, " ( " + target_element.replace(/href=|["']/g, "") + " ) ");
\r
123 // ブロック要素のタグが存在した場合、改行をその後に挿入します。
\r
124 if(document.getElementById("isCoverBlockTag").checked) {
\r
125 contents = contents.replace(/<(div|h\d)[^>]*>/ig, "-----------------------------------------------------------------------------\n");
\r
126 contents = contents.replace(/(\n|)<\/(div|h\d)>/ig, "\n-----------------------------------------------------------------------------\n");
\r
128 contents = contents.replace(/<\/(div|h\d|p)>/ig, "\n");
\r
132 contents = contents.replace(/<[\/]{0,1}ul>/ig, "");
\r
133 contents = contents.replace(/<li>/ig, "・");
\r
134 contents = contents.replace(/<\/li>/ig, "\n");
\r
137 contents = contents.replace(/<[^>]*>|<\/[^>]*>/ig, "");
\r
139 // 通常のタグ置換後、ニコニコ動画のタグを元に戻す
\r
140 contents = contents.replace(/<externalvideo src="NC:/g, "<externalvideo src='NC:");
\r
141 contents = contents.replace(/:D">/, ":D'>");
\r
144 contents = contents.replace(/ /g, " ");
\r
151 * @param {int} index entryListから削除される記事のインデックス
\r
153 function removeEntry(index) {
\r
154 if(editIndex == 0) {
\r
155 entryList.splice(editIndex, 1);
\r
156 refleshEntrylistBox();
\r
159 document.getElementById("stdout").value = ""
\r
160 document.getElementById("entry_title").value = "";
\r
161 document.getElementById("entry_stdin").value = "";
\r
163 if(editIndex > 0) {
\r
164 var prevIndex = document.getElementById("logBox").selectedIndex - 1;
\r
165 entryList.splice(editIndex, 1);
\r
166 refleshEntrylistBox();
\r
167 document.getElementById("logBox").selectedIndex = prevIndex;
\r
168 editIndex = editIndex - 1;
\r
170 entryLoader(editIndex);
\r
174 document.getElementById("previewArea").innerHTML = "";
\r
180 function allRemoveEntry() {
\r
184 refleshEntrylistBox();
\r
186 document.getElementById("stdout").value = ""
\r
187 document.getElementById("entry_title").value = "";
\r
188 document.getElementById("entry_stdin").value = "";
\r
194 function initialize() {
\r
195 inputValidateMode = $("#feedblog_inputvalidatemode").val();
\r
196 outputValidateMode = $("#feedblog_outputvalidatemode").val();
\r
197 logXmlUrl = $("#feedblog_loglistxmlurl").val();
\r
199 if(outputValidateMode == "1") {
\r
200 document.getElementById("feedblog_addcontentbr").checked = true;
\r
202 document.getElementById("feedblog_addcontentbr").checked = false;
\r
207 * 全DOMが使用可能になり次第、自動的に呼ばれる関数
\r
209 $(document).ready(function() {
\r
218 * ログファイル選択用のコンボボックスをid名:feedblog_logselecterに生成します
\r
220 function logXMLLoader() {
\r
223 url : logXmlUrl + "?time=" + (+new Date()),
\r
225 success : function(xmlData) {
\r
226 var separateTag = xmlData.getElementsByTagName("file");
\r
227 var urls = new Array(separateTag.length);
\r
230 // 読み込んだ要素をStoreに格納して表示
\r
231 var boxBuffer = [];
\r
232 boxBuffer.push("<form name='logform'><select name='logbox' onchange='xmlLoader(this.options[this.selectedIndex].value)'>");
\r
233 for(var i = 0; i < separateTag.length; i++) {
\r
235 initUrl = separateTag[i].getElementsByTagName("path")[0].firstChild.nodeValue;
\r
237 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
239 boxBuffer.push("</select></form>");
\r
242 document.getElementById("feedblog_logselecter").innerHTML = boxBuffer.join("");
\r
245 xmlLoader(initUrl);
\r
252 * URLを指定し、指定されたFeedXmlを読み込み、解析を行います
\r
253 * @param {String} url
\r
255 function xmlLoader(url) {
\r
256 // 記事本体のデータをローディングする
\r
257 var loader = new jQuery.ajax({
\r
258 url : url + "?time=" + (+new Date()),
\r
260 success : analyzeTargetXml,
\r
266 * 引数に存在するXMLデータを解析し、画面に反映します
\r
267 * @param {Object} xmlData
\r
269 function analyzeTargetXml(xmlData) {
\r
270 var rootTag = xmlData.getElementsByTagName("feed");
\r
271 var entryTag = xmlData.getElementsByTagName("entry");
\r
277 feedInfo = new FeedInfo(rootTag[0]);
\r
278 for(var i = 0; i < entryTag.length; i++) {
\r
279 entryList.push(new Entry(entryTag[i]));
\r
282 feedinfoLoader(feedInfo);
\r
283 pageAddr = feedInfo.alternate;
\r
285 refleshEntrylistBox();
\r
286 document.getElementById("entry_title").value = "";
\r
287 document.getElementById("entry_stdin").value = "";
\r
288 document.getElementById("stdout").value = "";
\r
292 document.getElementById("previewArea").innerHTML = "";
\r
296 * feedInfo変数の内容をHTMLに反映します
\r
297 * @param {FeedInfo} finfo 反映するfeedInfo変数
\r
299 function feedinfoLoader(finfo) {
\r
300 document.getElementById("feed_title").value = finfo.title;
\r
301 document.getElementById("feed_subtitle").value = finfo.subtitle;
\r
302 document.getElementById("feed_self").value = finfo.self;
\r
303 document.getElementById("feed_alternate").value = finfo.alternate;
\r
304 document.getElementById("feed_id").value = finfo.id;
\r
305 document.getElementById("feed_rights").value = finfo.rights;
\r
306 document.getElementById("feed_authorname").value = finfo.authorname;
\r
307 document.getElementById("feed_authoremail").value = finfo.authoremail;
\r
311 * HTMLの内容をFeedInfoに変換します
\r
313 function applyFeedinfo() {
\r
314 var finfo = new Object();
\r
315 finfo.title = document.getElementById("feed_title").value;
\r
316 finfo.subtitle = document.getElementById("feed_subtitle").value;
\r
317 finfo.self = document.getElementById("feed_self").value;
\r
318 finfo.alternate = document.getElementById("feed_alternate").value;
\r
319 finfo.id = document.getElementById("feed_id").value;
\r
320 finfo.rights = document.getElementById("feed_rights").value;
\r
321 finfo.authorname = document.getElementById("feed_authorname").value;
\r
322 finfo.authoremail = document.getElementById("feed_authoremail").value;
\r
328 * 指定したEntryList上のインデックスの記事をロードします
\r
329 * @param {int} index
\r
331 function entryLoader(index) {
\r
333 document.getElementById("entry_title").value = "";
\r
334 document.getElementById("entry_stdin").value = "";
\r
335 addTagSelectBoxFromCategory([]);
\r
338 document.getElementById("entry_title").value = entryList[index].title;
\r
339 document.getElementById("entry_stdin").value = entryList[index].content;
\r
340 addTagSelectBoxFromCategory(entryList[index].category);
\r
345 document.getElementById("previewArea").innerHTML = "";
\r
349 * 記事一覧の情報を表示するセレクトボックスにentryListの情報を反映させます
\r
351 function refleshEntrylistBox() {
\r
352 var stringBuffer = [];
\r
353 stringBuffer.push("<form name='logform'><select id='logBox' onchange='entryLoader(this.options[this.selectedIndex].value)'>");
\r
354 stringBuffer.push("<option value='-1'>新規作成</option>");
\r
355 for(var i = 0; i < entryList.length; i++) {
\r
356 stringBuffer.push("<option value='" + i + "'/>" + entryList[i].title + "</option>");
\r
358 stringBuffer.push("</select></form>");
\r
360 document.getElementById("feedblog_entryselect").innerHTML = stringBuffer.join("");
\r
365 * @param {Object} obj
\r
367 function FeedInfo(obj) {
\r
368 this.title = $("title:first", obj).text();
\r
369 this.subtitle = $("subtitle:first", obj).text();
\r
370 this.self = $("link[rel=self]", obj).attr("href");
\r
371 this.alternate = $("link[rel=alternate]", obj).attr("href");
\r
372 this.updated = $("updated:first", obj).text();
\r
373 this.id = $("id:first", obj).text();
\r
374 this.rights = $("rights:first", obj).text();
\r
375 this.authorname = $("author>name", obj).text();
\r
376 this.authoremail = $("author>email", obj).text();
\r
381 * @param {Object} obj entry 要素の DOM オブジェクト
\r
383 function Entry(obj) {
\r
384 this.id = $("id:first", obj).text();
\r
385 this.title = $("title:first", obj).text();
\r
386 this.summary = $("summary:first", obj).text();
\r
387 this.published = $("published:first", obj).text();
\r
388 this.updated = $("updated:first", obj).text();
\r
389 this.link = $("link:first", obj).attr("href");
\r
390 this.content = $("content:first", obj).text();
\r
392 var categoryList = [];
\r
393 var categories = $("category", obj);
\r
394 var tempCategory = {};
\r
395 for(var i = 0; i < categories.length; i++) {
\r
397 "term" : categories.eq(i).attr("term"),
\r
398 "label" : categories.eq(i).attr("label")
\r
400 categoryList.push(tempCategory);
\r
402 this.category = categoryList
\r
404 if(inputValidateMode == 1) {
\r
405 this.content = this.content.replace(/[\r\n]|\r\n/g, "");
\r
406 this.content = this.content.replace(/<br[ \/]*>/ig, "\n");
\r
407 this.content = this.content.replace(/^[ \t]*/mg, "");
\r
412 * グローバル変数からXMLを生成し、返却します
\r
413 * @param {FeedInfo} finfo
\r
414 * @param {Entry[]} elist
\r
416 function toXml(finfo, elist) {
\r
417 var stringBuffer = [];
\r
419 stringBuffer.push('<?xml version="1.0" encoding="utf-8"?>');
\r
420 stringBuffer.push('<feed xml:lang="ja-jp" xmlns="http://www.w3.org/2005/Atom">');
\r
421 stringBuffer.push('');
\r
423 stringBuffer.push('<title type="text">' + finfo.title + '</title>');
\r
424 stringBuffer.push('<subtitle type="text">' + finfo.subtitle + '</subtitle>');
\r
425 stringBuffer.push('<link rel="self" type="application/atom+xml" href="' + finfo.self + '" />');
\r
426 stringBuffer.push('<link rel="alternate" type="text/html" href="' + finfo.alternate + '" />');
\r
427 stringBuffer.push('<updated>' + getDate() + '</updated>');
\r
428 stringBuffer.push('<id>' + finfo.id + '</id>');
\r
429 stringBuffer.push('<rights type="text">' + finfo.rights + '</rights>');
\r
430 stringBuffer.push('<author>');
\r
431 stringBuffer.push(' <name>' + finfo.authorname + '</name>');
\r
432 stringBuffer.push(' <email>' + finfo.authoremail + '</email>');
\r
433 stringBuffer.push('</author>');
\r
434 stringBuffer.push('');
\r
436 for(var i = 0; i < elist.length; i++) {
\r
438 if(document.getElementById("feedblog_addcontentbr").checked) {
\r
439 temp_content = xmlAttrContentEscape(convertContent(elist[i].content));
\r
441 temp_content = xmlAttrContentEscape(elist[i].content);
\r
444 stringBuffer.push('<entry>');
\r
445 stringBuffer.push('<id>' + elist[i].id + '</id>');
\r
446 stringBuffer.push('<title>' + elist[i].title + '</title>');
\r
447 stringBuffer.push('<summary>' + elist[i].summary + '</summary>');
\r
448 stringBuffer.push('<published>' + elist[i].published + '</published>');
\r
449 stringBuffer.push('<updated>' + elist[i].updated + '</updated>');
\r
450 stringBuffer.push('<link href="' + elist[i].link + '" />');
\r
451 stringBuffer.push('<content type="html">' + temp_content + '</content>');
\r
453 for(var j = 0; j < elist[i].category.length; j++) {
\r
454 var tmpElist = elist[i].category[j];
\r
455 stringBuffer.push('<category term="' + tmpElist["term"] + '" label="' + tmpElist["label"] + '"/>');
\r
458 stringBuffer.push('</entry>');
\r
459 stringBuffer.push('');
\r
462 stringBuffer.push('</feed>');
\r
464 return stringBuffer.join("\n");
\r
468 * <content>要素の変換を行います
\r
469 * @param {String} content
\r
471 function convertContent(content) {
\r
472 if(document.getElementById("feedblog_addcontentbr").checked) {
\r
473 content = content.replace(/[\n\r]|\r\n/g, "<br>\n");
\r
475 content = content.replace(/<br>/ig, "\n");
\r
483 * @param {String} str エスケープを行う文字列
\r
485 function xmlAttrContentEscape(str) {
\r
486 return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/^[ ]+/mg, " ").replace(/^[\t]+/mg, "");
\r
491 * @param {String} str 逆エスケープを行う文字列
\r
493 function xmlAttrContentUnescape(str) {
\r
494 return str.replace(/^[\t]+/mg, "").replace(/^[ ]+/mg, " ").replace(/"/g, '"').replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&");
\r
498 * jQueryでのパネル開閉を制御します
\r
499 * @param {String} id 開閉するオブジェクトのid
\r
501 function closePanel(id) {
\r
502 $("#" + id).slideToggle();
\r
508 function showError() {
\r
509 alert("XMLファイルのローディング中にエラーが発生しました!");
\r
513 * RFC3339形式の日時を出力します
\r
515 function getDate() {
\r
516 var dNow = new Date();
\r
517 var sYear = dNow.getFullYear();
\r
518 var sMonth = dNow.getMonth() + 1;
\r
519 var sDate = dNow.getDate();
\r
520 var sHour = dNow.getHours();
\r
521 var sMinute = dNow.getMinutes();
\r
522 var sSecond = dNow.getSeconds();
\r
526 sMonth = "0" + sMonth;
\r
528 sDate = "0" + sDate;
\r
530 sHour = "0" + sHour;
\r
532 sMinute = "0" + sMinute;
\r
534 sSecond = "0" + sSecond;
\r
536 return sYear + "-" + sMonth + "-" + sDate + "T" + sHour + ":" + sMinute + ":" + sSecond + "+09:00";
\r
542 function getTags() {
\r
544 var tagListHtml = $("*[name=tag]");
\r
546 for(var i = 0; i < tagListHtml.length; i++) {
\r
547 // 各tag要素を取得して配列に格納
\r
548 if(tagListHtml.eq(i).val() != "") {
\r
550 "term" : tagListHtml.eq(i).val(),
\r
551 "label" : tagListHtml.eq(i).find(":selected").text()
\r
553 tagList.push(tagTemp);
\r
561 * デフォルトのタグ一覧を取得する関数です
\r
563 function getDefaultTags() {
\r
565 var plainTagText = $("#feedblog_tagdefine").val();
\r
570 "label" : "タグを選択してください"
\r
572 for(var i = 0; i < plainTagText.split('/').length; i++) {
\r
573 // 各tag要素を取得して配列に格納
\r
575 "term" : plainTagText.split('/')[i].split(',')[0],
\r
576 "label" : plainTagText.split('/')[i].split(',')[1]
\r
578 tagList.push(tagTemp);
\r
585 * HTMLにタグ選択用のセレクトボックスを追加します
\r
587 function addTagSelectBox() {
\r
588 var tagList = getDefaultTags();
\r
591 addHtml.push('<select name="tag">');
\r
592 for(var i = 0; i < tagList.length; i++) {
\r
593 addHtml.push('<option value="' + tagList[i]["term"] + '">' + tagList[i]["label"] + '</option>');
\r
595 addHtml.push('</select>');
\r
597 $("#entry_category").append(addHtml.join('') + "<br>");
\r
601 * 現在のカテゴリーオブジェクトに従いセレクトボックスを追加します
\r
603 function addTagSelectBoxFromCategory(categoryList) {
\r
604 $("#entry_category").html("");
\r
605 for(var j = 0; j < categoryList.length; j++) {
\r
607 var selectedTagTerm = categoryList[j]["term"];
\r
608 var selectedTagLabel = categoryList[j]["label"];
\r
609 var selectedTagSetFlag = false;
\r
611 var tagList = getDefaultTags();
\r
613 addHtml.push('<select name="tag">');
\r
614 for(var i = 0; i < tagList.length; i++) {
\r
615 if(tagList[i]["term"] == selectedTagTerm && tagList[i]["label"] == selectedTagLabel) {
\r
616 addHtml.push('<option value="' + tagList[i]["term"] + '" selected="selected">' + tagList[i]["label"] + '</option>');
\r
617 selectedTagSetFlag = true;
\r
619 addHtml.push('<option value="' + tagList[i]["term"] + '">' + tagList[i]["label"] + '</option>');
\r
623 if(!selectedTagSetFlag) {
\r
624 addHtml.push('<option value="' + selectedTagTerm + '" selected="selected">' + selectedTagLabel + '</option>');
\r
627 addHtml.push('</select>');
\r
628 $("#entry_category").append(addHtml.join('') + "<br>");
\r