Friday, March 31, 2006

How to create "Recent Comments"?

本站右邊Side Bar有一段列出本站訪客最近的留言,對追看本站的友好非常有用,有友好見過都詢問怎樣可將之放入自己的blog,以下是一個簡單的介紹。

Blogger.com本身並無提供Recent Comment的選項,幸好Blogger.com的Template可供用戶隨意改動,比其他blog網頁的彈性大得多,坊間也有人提供了在Blogger.com加Recent Comment的Hack程式,本站的Recent Comment即根據其中一個Hack程式改動而成。

先此聲明,以下的程序只適用於Blogger.com的blog,因為它用到Blogger.com伺服器的程式,所以其他的Blog Provider,例如網上行Hompy,Sina,便無能為力啦。

程序非常簡單,首先登入Blogger.com,選擇自己的blog,再選Template然後選擇裡面的Edit current,在這裡你會見到你所用template的程式碼。你只要將下面介紹的兩段程式碼插進原有的template程式碼,再Save,便完成了。要留意的是,以後如果再選用Blogger.com提供的其他template,今次插入的東西便會消失。

以下的程式碼取自這個網址再改動而成:

http://noisetheatre.blogspot.com/2005/11/javascript-implementation-of-recent.html


以上網址有簡單解釋不同段落的程式碼的作用,可供參考。

我的改動主要是改良顯示的模式,變為每個日子的下面顯示所有該日的留言。原本的模式如果要顯示留言日子,便需要每個留言都跟著一個日子,不好看之餘非常浪費網頁寶貴的空間。

第一段程式碼要放入<head>...</head>部份,你可以放在<title>...</title>之後:


<script type="text/javascript" language="JavaScript1.2">
/*
Blogger Recent Comments

Based on the Farrago Recent Comments Hack v1.03
boggerhacks.blogspot.com
(c) 2004 Ebenezer Orthodoxy

Statement: I would GPL the code if the original author would.
Mods by: Robin Parmar
Visit: noisetheatre.blogspot.com

Further modified from noisetheatre.blogspot.com by Ka Lok
*/

// our class
function RecentComments() {
// options to change
this.displayAmount = 10;
this.displayTemplate = '<li>[name]:<br/>[title]</li>';
this.displayPre = '<ul>';
this.displayPost = '</ul>';
this.displayLink = true;

// properties
this.comments = new Array();
this.title = '';
this.itemurl = '';

// methods
this.SetTemplate= rcSetTemplate;
this.SetAmount = rcSetAmount;
this.SetLink = rcSetLink;
this.SetPrePost = rcSetPrePost;

this.SetTitle = rcSetTitle;
this.SetUrl = rcSetUrl;

this.SortDate = rcSortDate;
this.AddComment= rcAddComment;
this.Display = rcDisplay;

// this line uses my date converter method
// this.DateConvert = rcDateConvert;
this.DateConvert = rcDateConvertWithDay;

// comment out the previous line and uncomment the
// next line to use original date format
// this.DateConvert = rcDateConvertDefault;

// or write your own and insert it

this.DateConvertForCommentListDisplay = rcDateConvertWithDayForCommentListDisplay;

}

// simple property setters: these are used by process
function rcSetTitle(x) {
this.title = document.getElementById(x).innerHTML;
}
function rcSetUrl(x) {
this.itemurl = x;
}

// these are used by user to customise
function rcSetTemplate(x) {
this.displayTemplate = x;
}
function rcSetAmount(x) {
this.displayAmount = x;
}
function rcSetLink(x) {
if (x==0) {
this.displayLink = false;
} else {
this.displayLink = true;
}
}
function rcSetPrePost(x, y) {
this.displayPre = x;
this.displayPost = y;
}

// date format converter
// insert your own here depending on the format you use for comment dates
// this one converts from:
// 01 November, 2005 16:35
// to:
// 11/01/2005 16:35:00
function rcDateConvert(dt) {
var s = dt.split(' ');
var d = s[0];
var m = s[1];
var y = s[2];
var t = s[3];

var MonthHash = new Array();
MonthHash['January'] = '01';
MonthHash['February'] = '02';
MonthHash['March'] = '03';
MonthHash['April'] = '04';
MonthHash['May'] = '05';
MonthHash['June'] = '06';
MonthHash['July'] = '07';
MonthHash['August'] = '08';
MonthHash['September']= '09';
MonthHash['October'] = '10';
MonthHash['November'] = '11';
MonthHash['December'] = '12';

// trim off comma
m = m.substring(0, m.length-1);

return MonthHash[m] + '/' + d + '/' + y + ' ' + t + ':00';
}

// date format converter for new date format
//
// this one converts from:
// Monday, November 01, 2005 16:35:00pm
// to:
// 11/01/2005 16:35:00
function rcDateConvertWithDay(dt) {
var s = dt.split(' ');
var d = s[2];
var m = s[1];
var y = s[3];
var t = s[4]+' '+s[5];

var MonthHash = new Array();
MonthHash['January'] = '01';
MonthHash['February'] = '02';
MonthHash['March'] = '03';
MonthHash['April'] = '04';
MonthHash['May'] = '05';
MonthHash['June'] = '06';
MonthHash['July'] = '07';
MonthHash['August'] = '08';
MonthHash['September']= '09';
MonthHash['October'] = '10';
MonthHash['November'] = '11';
MonthHash['December'] = '12';

// trim off comma
d = d.substring(0, d.length-1);

// return MonthHash[m] + '/' + d + '/' + y + ' ' + t + ':00';
return MonthHash[m] + '/' + d + '/' + y + ' ' + t;
}

// date format converter for new date format for display in recent comment list
//
// this one converts from:
// Monday, November 01, 2005 16:35:00pm
// to:
// 01 Nov 2005
function rcDateConvertWithDayForCommentListDisplay(dt) {
var s = dt.split(' ');
var d = s[2];
var m = s[1];
var y = s[3];
var t = s[4];

var MonthHash = new Array();
MonthHash['January'] = 'Jan';
MonthHash['February'] = 'Feb';
MonthHash['March'] = 'Mar';
MonthHash['April'] = 'Apr';
MonthHash['May'] = 'May';
MonthHash['June'] = 'Jun';
MonthHash['July'] = 'Jul';
MonthHash['August'] = 'Aug';
MonthHash['September']= 'Sep';
MonthHash['October'] = 'Oct';
MonthHash['November'] = 'Nov';
MonthHash['December'] = 'Dec';

// trim off comma
d = d.substring(0, d.length-1);

// return MonthHash[m] + '/' + d + '/' + y + ' ' + t + ':00';
return d + ' ' + MonthHash[m] + ' ' + y;
}


// default converter: does nothing
// use if your comment date format is:
// mm/dd/yyyy hh:mm:ss
function rcDateConvertDefault(dt) {
return dt;
}

// given a date string this returns a sorted representation
function rcSortDate(strDate) {
strDate = this.DateConvert(strDate)

var d = new Date(strDate);

var day = '' + d.getDate();
if (day.length==1) {
day = '0' + day;
}
var month = '' + (d.getMonth()+1);
if (month.length==1) {
month = '0' + month;
}
var hour = '' + d.getHours();
if (hour.length==1) {
hour = '0' + hour;
}
var min = '' + d.getMinutes();
if (min.length==1) {
min = '0' + min;
}
var sec = '' + d.getSeconds();
if (sec.length==1) {
sec = '0' + sec;
}
var sortDate = '' + d.getFullYear() + month + day + hour + min + sec;
return sortDate;
}

// adds to global comments array
function rcAddComment(title, url, id, a, datestamp) {
var author = a;
var expt = '';
var st = '';

// grab content of our hidden layer containing all items
var html = document.getElementById('comm' + id).innerHTML;

// strip out whitespace
while (html.indexOf("\n") > -1) {
html = html.replace("\n", "");
}
while (html.indexOf(" />") > -1) {
html = html.replace(" />", "/>");
}
while (html.indexOf(" <a/>") > -1) {
html = html.replace(" <a/>", "<a/>");
}

var htmll = html.toLowerCase();
var pos1 = htmll.lastIndexOf('<br><a></a>posted by');
var pos2 = htmll.lastIndexOf('<br><a></a><a></a>');
var pos3 = htmll.lastIndexOf('<br/><a/><a/>');
var pos4 = htmll.lastIndexOf('<br/><a></a><a></a>');
var aoffset = pos1 + 6;

if (pos3 > -1) {
pos2 = pos3;
}
if (pos4 > -1) {
pos2 = pos4;
}
if (pos2 > -1) {
pos1 = pos2;
aoffset = htmll.lastIndexOf('<a><b> </b></a>');
if (aoffset == -1) {
aoffset = htmll.lastIndexOf('<a><b></b></a>') - 1;
}
}

if (pos1 > -1) {
author = html.substr(aoffset+15, html.length-1);
expt = html.substr(0, pos1-4);
} else {
expt = html;
}
expt = expt.replace(/(<([^>]+)>)/ig, "");

if (expt.length > 50) {
expt = expt.substr(0, 50);
if (expt.lastIndexOf(' ') > -1) {
expt = expt.substr(0, expt.lastIndexOf(' '));
}
expt += '...';
}
expt = expt.replace('"', "\"");
expt = expt.replace("'", "\'");

author = author.replace("<A ", "<a ");
if (!this.displayLink) {
author = author.replace(/(<([^>]+)>)/ig, "");
}

// build a template string of HTML
st = this.displayTemplate.replace('[name]', author);
st = st.replace('[title]', '<a title="' + expt + '" href="'
+ url + '#c' + id + '">' + title + '</a>');

// prefix with date for sorting purposes
st = this.SortDate(datestamp) + st;

// append date for display
// newly added
// st = st + '<br/>(' + this.DateConvertForCommentListDisplay(datestamp) + ')';

// accumulate on our array
this.comments.push(st);
}

function rcDisplay() {
// most recent comments first
this.comments.sort();
this.comments.reverse();

if (this.displayPre.length >0) {
document.write(this.displayPre);
}

// start of new declaration for date header
var latestDate = '';
var revMonthHash = new Array();
revMonthHash['01'] = 'Jan';
revMonthHash['02'] = 'Feb';
revMonthHash['03'] = 'Mar';
revMonthHash['04'] = 'Apr';
revMonthHash['05'] = 'May';
revMonthHash['06'] = 'Jun';
revMonthHash['07'] = 'Jul';
revMonthHash['08'] = 'Aug';
revMonthHash['09'] = 'Sep';
revMonthHash['10'] = 'Oct';
revMonthHash['11'] = 'Nov';
revMonthHash['12'] = 'Dec';
// end of new declaration for date header

for (i=0; i<10 && i < this.comments.length && i < this.displayAmount; i++) {
var s = this.comments[i];

// start of showing date header

if (s.substr(0, 8) != latestDate) {
// if (latestDate != '') {
// document.write('</ul>');
// }
latestDate = s.substr(0, 8);
document.write('<li><i><u>' + s.substr(6, 2) + ' '
+ revMonthHash[s.substr(4, 2)] + ' ' + s.substr(0, 4) + '</u></i></li>');
// document.write('<li><ul>');
}

// end of showing date header

// strips off date prefix
s = s.substr(14, s.length-1);
document.write(s);
}

if (this.displayPost.length >0) {
document.write(this.displayPost);
}
}
</script>



以上的程式碼還有一個地方要注意,它假設blog的Comments的Comments Timestamp Format設定為「Monday, November 01, 2005 16:35:00 PM」的式樣,如果你要用別的設定,便需要修改上面的Javascript程式。

第二段程式碼要插進真正顯示Recent Comment的Side Bar的html碼之中:


<!-- START RecentComments 1.05 -->
<MainPage>
<h2>recent comments</h2>
<script type="text/javascript" language="JavaScript1.2">
var rc = new RecentComments();
rc.SetTemplate('<li>[name]: [title]</li>');
rc.SetPrePost('<ul>', '</ul>');
rc.SetLink(0);
rc.SetAmount(10);
</script>
<Blogger>
<span id="comm<$BlogItemNumber$>" style="visibility:hidden; position:absolute;">
<BlogItemTitle><$BlogItemTitle$></BlogItemTitle>
</span>
<script type="text/javascript" language="JavaScript1.2">
rc.SetTitle('comm<$BlogItemNumber$>');
rc.SetUrl('<$BlogItemPermalinkURL$>');
</script>
<BlogItemCommentsEnabled><BlogItemComments>
<span id="comm<$BlogCommentNumber$>" style="visibility:hidden; position:absolute;">
<$BlogCommentBody$>
</span>
<script type="text/javascript" language="JavaScript1.2">
rc.AddComment(rc.title, rc.itemurl, '<$BlogCommentNumber$>',
'<$BlogCommentAuthor$>', '<$BlogCommentDateTime$>');
</script>
</BlogItemComments></BlogItemCommentsEnabled>
</Blogger>
<script type="text/javascript" language="JavaScript1.2">
rc.Display();
</script>
</MainPage>
<!-- END RecentComments 1.05 -->

Thursday, March 30, 2006

去韓國做板仔(第五回大結局)

大結局的劇情非常簡單,不過是吃和睡而已。

吃早餐的時候見到酒店有名為Strawberry Festival的推廣,原來3月開始士多啤梨在韓國當造。

這一天的重點是明洞4小時自由行。



先來一個肥牛火鍋:


吃完之後同行兩位壯男居然喊不夠飽,(我倒沒有所謂,因為已經獨力吃完無人光顧的泡菜),唯有到另外一間食店再來一個大的薯仔豬骨煲:



另加一碟蛋的菜式(這個菜式應該叫什麼名字,我實在不知道,請大家指教。)



這兩天吃午餐的時候,食店的電視總是開著棒球,吃肥牛火鍋的時候,有不少食客跟侍應都非常聚精會神地看,情況跟我們看世界杯足球差不多。後來才知道,他們看的是一個叫World Baseball Classic的比賽,似乎跟棒球世界杯差不多。韓國成績不錯,入了四強,不過輸了給日本,還給日本拿了冠軍。

吃完兩個大午餐,在街頭搜購了一些士多啤梨,我們買的一檔,一小盒透明膠盒裝賣2500韓圜,折合港幣大概20元,比較便宜,味道不差,不過大小十分參差,底層的比較小顆。其他的檔賣的價錢大概3500至4000韓圜,看來比較大顆,味道就無從稽考。


之後便肚滿腸肥離開明洞,上機去。


Wednesday, March 29, 2006

去韓國做板仔(第四回)

「去韓國做板仔」系列休息了一星期,現在又返來啦!

第四天自費繼續滑雪,目標有兩個:


  • 嘗試其他長途的滑道,

  • 苦練「S」字滑行!



首先到另一個名為「Gold Paradise」的滑道,沿途要轉吊車。第二程吊車路程非常長,感覺跟香港海洋公園的吊車差不多長,但是不要忘記,這吊車不過是只有一張椅的普通滑雪吊車,四周無遮無掩:



可是吊車的路程跟海洋公園一樣,有上有落。落的時候眼光不其然望著自己的雙腿在半空搖晃,刮起的冰冷強風再要用盡力搖動吊車,刺激程度遠超海洋公園及第三天上「Rainbow Paradise」的吊車。

回來再找資料,原來根據龍平網站資料,這一段吊車全長1.72公里,海洋公園的吊車,根據其網站,也不過是1.5公里長。

經過刺激的吊車旅程,便到了「Gold Paradise」及其他難度更高滑道的起點:海拔1127米的Gold Peak。



「Gold Paradise」跟第三天的「Rainbow Paradise」形式差不多,都是彎彎曲曲的山路,周圍有風景看,路程卻短得多,全長1.536公里。沿途不時有大小不一的碎冰塊盤據路中心,像爛馬路一般,由於路面不闊,碎冰避無可避,唯有扎好馬,慢慢震過去,程況非常惡劣。





中途有分叉路,其實是一個非常陡斜,可能超過45度斜的滑道,望之心寒。



完成「Gold Paradise」之後,到初級滑道苦練「S」字滑行,跌得屁股開花。雙腳的動作連接與重心的配合固然要熟練,更要緊的重點卻跟踏單車和其他講究平衡的運動差不多,就算當轉到背向山腳滑行時都要眼望前方,不要害怕,眼望地下便立即變滾地胡蘆。

苦練的成績,請看以下片段:



結果是做了1.5個「S」字,努力都算沒有白費。

午餐到滑雪場Food Court來一個石頭飯,味道不錯:



意外的是配菜非常豐富,還有一條魚呢!



第三回的「東南西北」風謎題亦已解開,那個東西其實是一個按鈕,按下去之後,食店櫃面有一個顯示屏會顯示按鈕的檯號,侍應看見便到來落單。

吃完午餐準備離開滑雪場的時候,期待已久的事居然在此時發生:下大雪!



為什麼不早幾天下雪?上天最大的愚弄,莫過於此!







雪後的美景:





離開滑雪場之後在首爾外圍停留,晚餐有「最靚既豬腩肉」五花腩:



待續(還有大結局)…

Friday, March 24, 2006

辦公室外的天地

公司搬到新地點已有三個多月。新地點位於工廠區,附近的遊人景物和店鋪和以前在遊客區簡直是天堂與地獄之分!唯有苦中作樂,盡量發掘可觀的景致。

辦公室大樓外居然有一個公園,遊人稀少:


松樹居然有點古意:


亭臺樓閣俱全:


意外地這幅相有點似國畫,見笑見笑!


公園外的樓牆:


附近的著名地標:香港麵粉廠。


小強的哥哥開的:


誰說香港沒有尖端科技?


奇景!建築物裡面也可種樹!


這座建築物原來是九龍麵粉廠。


九龍麵粉廠跟香港麵粉廠只隔兩個街口左右,想不到香港跟九龍不用再填海距離已經那麼近!


茫茫前路:

Tuesday, March 21, 2006

去韓國做板仔(第三回)

第三天是這個旅程的一個重大日子。

這天的氣溫如預報所言急降至零下數度,還不時刮起大風,吹得人頭痛,但是沒有下雪,非常令人失望!



更令人氣餒的是,滑雪場似乎曾經出動壓雪車,將早兩天的「思樂冰」都壓成硬冰,跌倒只會更痛,似要增加對我們的考驗。

不過這些都無阻我們的熱情,因為這天下午有一個重大的目標:征服「彩虹車道」(Rainbow Paradise)!

「彩虹車道」似乎是官方的譯名,我是從韓國觀光公社的網站抄出來,但總是感覺怪怪的,我又不是在上面駕車,叫什麼車道?還是用英文名稱算了。

根據龍平滑雪場網站的資料,Rainbow Paradise全長5.6公里,是全場最長的滑道,起點於海拔1458米的Dragon Peak,到終點為止全程下降702米,整體難度屬中級。滑道其實是一條彎彎曲曲的山路,斜度不斷變化,有平坦的路也有急彎,沿途還可欣賞不同的景色。

要上Rainbow Paradise的起點Dragon Peak,要乘吊車,即是海洋公園那種,乘車時順便欣賞周圍的景色:





山頂的天氣情況非常惡劣,風大得會吹倒人,氣溫相信已經低過零下十度,因為朋友的電子表已經不能提供溫度讀數。在這個情況下當然不便久留,要立即出發。

整個Rainbow Paradise最難的路段其實是起點,即下圖吊車柱右方對下的斜坡,斜度很大:


玩snowboard在這裡有好處,面對這個斜坡只要橫板慢慢下滑,無驚無險便過了關。不像上次用ski由於初學,技術太差,在這個斜坡連站都站不起來,唯有爬回起步點,醜死!

今次沿途施展「之」字滑行技術,雖然在當地高手面前不值一晒,但是勝在穩穩陣陣,不過速度真的很慢,滑完全程要一個多小時。

回頭講當天午餐,在滑雪場的Food Court吃熱狗,實在不大吸引:


有趣的是,在Food Court每張檯的檯角都有這個我覺得像打麻將紀錄「東南西北」風的東西:


究竟它的作用是什麼,留待下回分解。

晚上,滑雪場居然噴人造雪,令人不解,為什麼不早幾天噴?



玩完人造雪,靜了下來,夜涼如水(實際是冷得發抖),最好去「擋著」裴勇俊跟崔智友「談談情」。


待續…

Saturday, March 18, 2006

去韓國做板仔(第二回)

龍平滑雪場其實是舊遊之地,上次滑雪已經來過:



龍平滑雪場是韓國著名的滑雪場,建成於1975年。龍平曾舉辦1999年冬季亞運,亦將會是所在的江原道舉辦的2010年冬季奧運的主要場地。現擁有31條不同難度的滑道,適合初學者以至滑雪高手。


滑雪團更提供一整天的初級課程,未滑過雪的團友可以即學即玩。

龍平滑雪的其中一個好處,在於滑雪場就在酒店旁邊,滑雪期間要來回酒店房間都非常方便。據聞韓國另外一個著名的茂朱滑雪場,從酒店去滑雪場要乘車。

龍平的另外一個賣點,是韓劇「藍色生死戀之冬日戀曲」的主要拍攝場地:


由於三月已是滑雪季節的尾段,究竟滑雪場是否有足夠的雪,報名時也不確定,只是賭一賭運氣。結果首兩天的情況實在不算好也不太壞,有些滑道的某些路段不夠雪露出冰面,滑過的時候比較顛簸,減速有困難,幸而也有其他滑道情況較佳,不過有不少地方,尤其是近滑道末段較平的地方,有不少冰夾著水的窪(如下面兩幅圖雪地中較深灰色的地方),領隊的形容最貼切,像「思樂冰」,跌倒幾次之後「理論上應該防水」的褲跟手套已經濕透,很不好受。





首天下午跟隨當地導遊學玩snowboard,學了大概一個多小時,總算是學會了面向山下橫板落山,由於小弟資質魯鈍,要到第二天才大致做到面向山下「之」字落山。

本來當地導遊第二天應承教「S」字滑行,誰知到約定時間卻不見蹤影,事後又口同鼻拗,大家都聲稱在約定地點見不到對方,導遊還稱見到其他團友並已經教了他們「S」字滑行。無辦法,這個世界果然是沒有免費午餐,唯有靠自己吧。幸好朋友已經下載了不少教滑雪及snowboard的video到帶來的Notebook,晚上便在房間舉行研習。忽然覺得跟那些四天泰國/馬來西亞/菲律賓潛水考牌團差不多,都是日間實習夜晚溫習,好認真。

由於太專注snowboard的技術研究,傳統雪橇(ski)還是忘記了罷,專心做板仔算了。

第二天的午餐只是在滑道旁的豬扒飯快餐,價錢8,000韓圜,折合大概港幣六十多元:



再次證實劇烈運動後吃什麼都好味!

第二晚的天氣預測預期第三日氣溫會急降,非常期待會下雪。

待續…

Friday, March 17, 2006

去韓國做板仔(第一回)

上星期五跟兩位老友參加了一個韓國五天滑雪團。自問並非滑雪發燒友,同樣的旅行團在兩年多前又已經參加過一次,不過難得兩位已婚老友都得到老婆批准成行,當然要義無反顧,捨命陪君子。

首日航機大清早到達仁川機場,立即乘旅遊車直奔目的地:龍平滑雪場。途中停在公路旁的休息站,也有一些有趣的東西,例如供寵物暫住的儲物櫃:



還有叫大家坐長途車要運動鬆弛一下的告示:



滑雪團的費用已包括三天滑雪入場費加器材租賃費,另外第四天還可以自費繼續滑雪。不過所包的滑雪器材只是傳統的滑雪橇,即是雙腿穿著兩條長條型的雪橇再加手握兩枝滑雪杖(ski pole)那一種。至於滑雪板(snowboard),要另外加錢租,一天費用30,000韓圜,四天90,000韓圜,折合大概港幣七百多元。我從來未在真正的雪場玩過snowboard,上次滑雪只玩過傳統的滑雪橇,所以我來之前從來沒有認真想過要玩snowboard。今次因為朋友慫恿,加上當地導遊免應承免費教授snowboard,才當場決定花錢租了四天Snowboard,連同原來的滑雪橇,打算來一次又ski又snowboard。至於何時玩ski,何時玩snowboard,到時才算吧。後來的發展,倒是十分意外。

租snowboard的店鋪,非常簡陋:



對於到達滑雪場之前吃bunch的韓國餐,倒是從來沒有什麼寄望,所以沒有失望:



吃完bunch上車,滑雪場經已在望,心情卻不興奮,只想再睡一會:



待續…