// SLB - Super Light Blog
//
// Copyright (c) 2005,2006 Satoshi Fukutomi <info@fuktommy.com>.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
function getValue(object, tagname) {
var element = object.getElementsByTagName(tagname)[0];
if (element) {
return element.firstChild.nodeValue;
} else {
return null;
}
}
var Entry = Class.create();
Entry.prototype = {
initialize: function(item) {
this.title = getValue(item, "title");
this.link = getValue(item, "link");
this.date = getValue(item, "dc:date") || getValue(item, "date");
this.description = getValue(item, "description");
this.content = getValue(item, "content:encoded") ||
getValue(item, "encoded");
this.id = this.link.substring(this.link.search("#")+2)
}
};
var DummyEntry = Class.create();
DummyEntry.prototype = {
initialize: function() {
this.title = "";
this.link = "";
this.date = "";
this.description = "";
this.content = "";
this.id = "";
}
};
var EntryCache = Class.create();
EntryCache.prototype = {
initialize: function() {
this.cache = {};
this.index = [];
},
append: function(entry) {
if (this.cache[entry.id]) {
if (! entry.content) {
return;
} else {
this.index = this.index.without(entry.id);
}
}
this.index.push(entry.id);
this.cache[entry.id] = entry;
while (this.index.length > cacheSize) {
var id = this.index.shift();
this.cache.remove(id);
}
}
};
var RSSread = Class.create();
RSSread.prototype = {
initialize: function() {
},
read: function (uri) {
new Ajax.Request(
uri,
{method: "get",
onComplete: this.callParser.bind(this)});
},
callParser: function (request) {
this.parseXML(request.responseXML);
}
};
var RecentRSS = Class.create();
RecentRSS.prototype = Object.extend((new RSSread), {
initialize: function (topPageMode) {
this.topPageMode = topPageMode;
},
parseXML: function (xml) {
var recentList = new RecentList(xml);
if (this.topPageMode) {
var recentEntry = new EntryList(xml, recentSize);
}
}
});
var ArchivesRSS = Class.create();
ArchivesRSS.prototype = Object.extend((new RSSread), {
parseXML: function (xml) {
var archiveList = new ArchivesList(xml);
}
});
var MonthlyRSS = Class.create();
MonthlyRSS.prototype = Object.extend((new RSSread), {
parseXML: function (xml) {
new EntryList(xml);
}
});
var EntryRSS = Class.create();
EntryRSS.prototype = Object.extend((new RSSread), {
parseXML: function (xml) {
var entry = new Entry(xml.getElementsByTagName("item")[0]);
entryCache.append(entry);
var entryView = new EntryView(entry);
}
});
var ContentRSS = Class.create();
ContentRSS.prototype = Object.extend((new RSSread), {
initialize: function (panel) {
this.panel = panel;
},
parseXML: function (xml) {
var entry = new Entry(xml.getElementsByTagName("item")[0]);
entryCache.append(entry);
this.panel.setContent(entry.content);
}
});
function isAdminMode() {
return document.cookie.search("slb_admin") >= 0;
}
function setAdminCookie() {
var day = new Date();
day.setTime(day.getTime() + 30*24*60*60*1000);
document.cookie = 'slb_admin=1; ' +
'path=/; ' +
'expires=' + day.toGMTString();
}
var baseURI = 'http://ajaxblog.hp.infoseek.co.jp/';
var blogTitle = 'Ajax Blog isweb出張所';
var cgiURI = 'cgi-bin/blog.cgi';
var rssURI = 'xml/rss.rdf';
var archivesURI = 'xml/archives.rdf';
var entryURI = 'xml';
var recentSize = 10;
var archiveSize = 10;
var cacheSize = 500;
var timezoneOffset = -540;
var entryCache;
function localDate(date) {
return date.replace('T', ' ').replace(/[-+]..:../, '');
}
var EntryPanel = Class.create();
EntryPanel.prototype = {
initialize: function (entry) {
this.entry = entry;
},
view: function () {
viewEntry(this.entry.id);
return false;
},
edit: function () {
viewEdit(this.entry.id);
return false;
},
setContent: function (content) {
this.content.innerHTML = content;
var anchor = this.content.getElementsByTagName('a');
var re_entry = new RegExp(baseURI + '#e([0-9]+)$');
var re_archive = new RegExp(baseURI + '#a([0-9]+-[0-9]+)$');
for (var i=0; i<anchor.length; i++) {
if (anchor[i].href.search(re_entry) == 0) {
anchor[i].onclick = (function (_id) {
return function (event) {viewEntry(_id); return false; };
})(RegExp.$1);
} else if (anchor[i].href.search(re_archive) == 0) {
anchor[i].onclick = (function (_id) {
return function (event) {viewArchives(_id);
return false; };
})(RegExp.$1);
}
}
},
loadContent: function () {
var id = this.entry.id;
if (entryCache.cache[id] && entryCache.cache[id].content) {
this.setContent(entryCache.cache[id].content);
} else {
var uri = idtordf(id);
var contentRSS = new ContentRSS(this);
contentRSS.read(uri);
}
},
desc_over: function () {
var description = this.content.firstChild;
description.style.backgroundColor = '#ccf';
},
desc_out: function () {
var description = this.content.firstChild;
description.style.backgroundColor = '#efefef';
},
toObject: function () {
var panel = document.createElement('div');
var heading = document.createElement('h2');
var anchor = document.createElement('a');
anchor.href = this.entry.link;
anchor.title = 'Permalink';
anchor.onclick = this.view.bind(this);
anchor.appendChild(document.createTextNode(this.entry.title));
heading.appendChild(anchor);
panel.appendChild(heading);
this.content = document.createElement('div');
if (this.entry.content) {
this.setContent(this.entry.content);
panel.appendChild(this.content);
} else if (this.entry.description) {
var description = document.createElement('p');
description.title = 'ここをクリックすると本文が表示されます。';
description.className = 'description';
description.onclick = this.loadContent.bind(this);
description.onmouseover = this.desc_over.bind(this);
description.onmouseout = this.desc_out.bind(this);
description.appendChild(
document.createTextNode(this.entry.description));
this.content.appendChild(description);
panel.appendChild(this.content);
}
var feedback = document.createElement('ul');
feedback.className = 'feedback';
var item = document.createElement('li');
anchor = document.createElement('a');
anchor.href = 'http://b.hatena.ne.jp/entry/' +
escape(this.entry.link);
anchor.innerHTML =
'<img src="/b_entry_de.gif" width="16" height="12" alt="" />' +
' Comments';
item.appendChild(anchor);
feedback.appendChild(item);
item = document.createElement('li');
anchor = document.createElement('a');
anchor.href = 'http://www.technorati.jp/search/search.html?' +
'queryMode=cosmos&queryCosmos=' +
encodeURIComponent(this.entry.link) +
'&language=all';
anchor.appendChild(document.createTextNode('Links to entry Entry'));
item.appendChild(anchor);
feedback.appendChild(item);
item = document.createElement('li');
item.appendChild(
document.createTextNode(localDate(this.entry.date)));
feedback.appendChild(item);
if (isAdminMode() && entryCache.cache[this.entry.id]) {
item = document.createElement('li');
anchor = document.createElement('a');
anchor.href = '';
anchor.onclick = this.edit.bind(this);
anchor.appendChild(document.createTextNode('Edit'));
item.appendChild(anchor);
feedback.appendChild(item);
}
panel.appendChild(feedback);
return panel;
}
};
function entryAnchor(entry) {
return '<a href="' + entry.link + '"' +
' onmouseover="peepEntry(\'' + entry.id + '\');"' +
' onclick="hidePeep(); viewEntry(\'' + entry.id + '\');' +
' return false;">' +
entry.title + '</a>';
};
var RecentList = Class.create();
RecentList.prototype = {
initialize: function(xml) {
var items = xml.getElementsByTagName("item");
this.panel = $("recent");
var buf = "<ul>";
for (i=0; i<items.length; i++) {
var entry = new Entry(items[i]);
entryCache.append(entry);
if (i < recentSize) {
buf += '<li><a href="' + entry.link + '" onclick=' +
'"viewEntry(\'' + entry.id + '\'); return false;">' +
entry.title + '</a></li>';
}
}
buf += "</ul>";
this.panel.innerHTML = buf;
}
};
var EntryList = Class.create();
EntryList.prototype = {
initialize: function(xml, size) {
var items = xml.getElementsByTagName('item');
this.panel = $('main');
this.panel.innerHTML = '';
for (var i=0; i<items.length; i++) {
if (size && (i>=size)) {
break;
}
var entry = new Entry(items[i]);
entryCache.append(entry);
var epobj = new EntryPanel(entry).toObject();
epobj.className = 'entry';
this.panel.appendChild(epobj);
}
document.title = blogTitle;
window.scroll(0, 0);
}
};
var ArchivesList = Class.create();
ArchivesList.prototype = {
initialize: function(xml) {
var items = xml.getElementsByTagName("item");
this.panel = $("archive");
var buf = "<ul>" +
'<li><a href="" onclick="viewRecent(); return false;">' +
'Current Entries</a></li>';
for (var i=0; i<items.length; i++) {
var entry = new Entry(items[i]);
buf += '<li><a href="' + entry.link +'" onclick=' +
'"viewArchives(\'' + entry.id + '\');return false;">' +
entry.title + '</a></li>';
}
if (archiveSize < items.length) {
buf += '<li id="morearchive"><a href="javascript:;">more</a></li>';
}
buf += "</ul>";
this.panel.innerHTML = buf;
this.listItems = this.panel.getElementsByTagName('li');
for (var i=archiveSize+1; i<this.listItems.length-1; i++) {
this.listItems[i].style.display = 'none';
}
moreArchive = $('morearchive');
moreArchive.onclick = this.viewMoreArchive.bind(this);
},
viewMoreArchive: function() {
for (var i=archiveSize+1; i<this.listItems.length-1; i++) {
this.listItems[i].style.display = 'block';
}
$('morearchive').style.display = 'none';
}
};
function viewArchives(id) {
var monthlyRSS = new MonthlyRSS();
monthlyRSS.read(entryURI + "/" + id + "/index.rdf");
var hash = '#a' + id;
if (location.hash != hash) {
location.hash = hash;
}
}
var EntryView = Class.create();
EntryView.prototype = {
initialize: function (entry) {
this.panel = $('main');
document.title = entry.title;
var epobj = new EntryPanel(entry).toObject();
epobj.className = 'entry';
this.panel.innerHTML = '';
this.panel.appendChild(epobj);
}
};
function viewRecent() {
var recentRSS = new RecentRSS(true);
recentRSS.read(rssURI);    
}
function idtordf(id) {
var date = new Date();
date.setTime(id * 1000 - timezoneOffset);
var year = date.getYear();
if (year < 1900) {
year += 1900;
}
var month = date.getMonth() + 1;
if (month < 10) {
month = "0" + month;
}
return entryURI + "/" + year + "-" + month + "/" + id + ".rdf";
}
function viewEntry(id) {
if (entryCache.cache[id] && entryCache.cache[id].content) {
var entry = entryCache.cache[id];
var entryView = new EntryView(entry);
} else {
var uri = idtordf(id);
var entryRSS = new EntryRSS();
entryRSS.read(uri);
}
var hash = '#e' + id;
if (location.hash != hash) {
location.hash = hash;
}
window.scroll(0, 0);
}
function peepEntry(id) {
if (entryCache.cache[id]) {
try {
var peep = $('peep');
peep.innerHTML = '';
var epobj = new EntryPanel(entryCache.cache[id]).toObject();
epobj.className = 'entry';
peep.appendChild(epobj);
} catch (e) {
}
}
}
function hidePeep() {
try {
$("peep").innerHTML = "";
} catch (e) {
}
}
var EditForm = Class.create();
EditForm.prototype = {
initialize: function(entry) {
this.entry = entry;
this.panel = $("main");
var buf = "";
buf =
'<form id="editform"><p>' +
'<input type="hidden" name="id" value="' + entry.id + '" />' +
'Edit: <a href="' + entry.link + '"' +
' onclick="viewEntry(\'' + entry.id + '\'); return false">' +
entry.title + '</a><br />' +
'Password: ' +
'<input type="password" name="passwd" value="" />' +
'<button type="button" id="savebutton">Save</button>' +
'<button type="button" id="previewbutton">Preview</button>' +
'<br />' +
'<textarea name="body" cols="60" rows="20">' +
'</textarea>' +
'</p></form>' +
'<div id="preview"></div>';
document.title = this.entry.title;
this.panel.innerHTML = buf;
var editform = $('editform');
if (entry.title || entry.content) {
editform.body.value = entry.title + '\n\n' + entry.content;
}
editform.body.style.width = '90%';
editform.body.title =
'記事本文を入力してください。1行目がタイトルになります。';
editform.onsubmit = saveEntry;
$("savebutton").onclick = saveEntry;
$("previewbutton").onclick = updatePreview;
updatePreview();
}
};
function updatePreview() {
var editform = $("editform");
var index = editform.body.value.search('\n');
var title = '';
var body = '';
if (index >= 0) {
title = editform.body.value.substr(0, index);
body = editform.body.value.substr(index);
}
$('preview').innerHTML = '<h2>' + title + '</h2>' + body;
}
function viewEdit(id) {
if (id && entryCache.cache[id]) {
var editForm = new EditForm(entryCache.cache[id]);
} else {
var editForm = new EditForm(new DummyEntry());
}
window.scroll(0, 0);
}
function saveEntry() {
var buf = "";
var form = $("editform");
buf += "id=" + form.id.value;
buf += "&passwd=" + encodeURIComponent(form.passwd.value);
buf += "&body=" + encodeURIComponent(form.body.value);
new Ajax.Request(
cgiURI,
{method: "post",
parameters: buf,
onComplete: showSaveResponce,
onFailure: alertSaveResponce})
setAdminCookie();
return false;
}
function showSaveResponce(request) {
var xml = request.responseXML;
if (! xml) {
alert("XML Error.");
} else if (xml.documentElement.nodeName == "error") {
var err = "";
try {
err = xml.documentElement.firstChild.nodeValue;
} catch (e) {
err = "Error";
}
alert(err);
} else {
var recentRSS = new RecentRSS(true);
recentRSS.read(rssURI);
}
}
function alertSaveResponce(request) {
alert("Error (status=" + request.status + ").");
}
function searchCache() {
var q = $("searchform").q.value;
var query = split(q.toLowerCase());
hidePeep();
if (query.length == 0) {
viewRecent();
return;
}
var panel = $("main");
var ids = entryCache.index.sort().reverse();
var buf = '<ul class="result">';
for (var i=0; i<ids.length; i++) {
var e = entryCache.cache[ids[i]];
var target = e.title + " " + (e.content || e.description);
if (andSearch(query, target)) {
buf += '<li>' + entryAnchor(e) +
'  (' + localDate(e.date) + ')</li>';
}
}
buf += '</ul>' +
'<div id="peep"></div>';
panel.innerHTML = buf;
}
function main() {
entryCache = new EntryCache();
if (location.hash.length > 1) {
var recentRSS = new RecentRSS(false);
if (location.hash.search("#a") == 0) {
viewArchives(location.hash.substring(2));
} else if (location.hash.search("#e") == 0) {
viewEntry(location.hash.substring(2));
}
} else {
var recentRSS = new RecentRSS(true);
}
recentRSS.read(rssURI);
var archivesRSS = new ArchivesRSS();
archivesRSS.read(archivesURI);
}
function split(query) {
var buf = [""];
for (var i=0; i<query.length; i++) {
var c = query.substr(i, 1);
if (c != " ") {
buf[buf.length-1] += c;
} else if (buf[buf.length-1] != "") {
buf.push("");
}
}
if (buf[buf.length-1] == "") {
buf.pop();
}
return buf;
}
function andSearch(query, target) {
target = target.toLowerCase();
for (var i=0; i<query.length; i++) {
if (target.search(query[i]) < 0) {
return false;
}
}
return true;
}
function setSideBarControl(html) {
$("sidebarctrl").innerHTML = html;
}
function showSideBar() {
setSideBarControl('<a href="javascript:hideSideBar()">Hide sidebar</a>');
$("sidebar").style.display = "";
$("main").style.marginLeft = "20%";
}
function hideSideBar() {
setSideBarControl('<a href="javascript:showSideBar()">Show sidebar</a>');
$("sidebar").style.display = "none";
$("main").style.marginLeft = "0";
}
