// http://code.google.com/apis/gdata/reference.html
// http://lvchen.blogspot.com/feeds/comments/default?start-index=1&max-results=7
// http://lvchen.blogspot.com/feeds/posts/summary?published-min=2007-04-00&published-max=2007-04-31&max-results=90
// 1. 快取文章標題 (Done)
// 2. 如何加快顯示的速度?(Done)
// 3 沒有留言時,footer 的顯示資訊 (Not necessary)
// 4. Google 取消了 max-results 99 的限制,現在可以取超過99的文章與回應,在安裝程式中取消限制。
// 5. 增加是否可顯示日期設定 (可以在樣式設定中更改,只要拿掉 %timestamp% 就可以了,原來的樣式設定對於隱藏內文的判斷會有問題,已修正) (Done)
// 6. 文章被刪除後,留言還在的問題。(Done)
// 7. 文章的總數,應扣掉被刪除的留言,這樣就不需要顯示看不到留言的訊息???可能做到嗎??? (好像不行耶)
// 8. 嘗試作出可相容 neo 的換文章方式 (???)
// 9. 確定相容最新版 jquery (Done)
// 10. 取消迴圈設定,增加快取大小設定 cachesize (Done)
// 11. 修正沒有留言時的錯誤 (Done)
// 12. 修正查無文章的錯誤 (Done)
// 13. 查無文章但設定為顯示留言的話,要記得把連結拿掉
// 14. 增加直接到留言頁面的圖示,利用 blogID and postID 。
// 15. 修正 content 出現 <> 會造成顯示錯誤。
// 16. 全部展開,按下一頁後,開關圖示會不正常。
// 17. 相容其他使用 jQuery 的外掛。
// 跳頁測試,先按下一頁,再按上一頁,跳到最後一頁(使用跳頁按鈕),按上一頁,再按下一頁,跳到第二頁(使用跳頁按鈕,index=4),按上一頁,完成。
//----------- Define Global Variables, can be modified---------------
//var rcPreSetting = {};
var rcPreSetting = {
g_szBlogDomain:'lvchen.blogspot.com',
g_iShowCount: 10,
noContent: ['原文已被刪除','
沒有留言可以顯示
'],
cachesize: 40,
showJumpButton:true,
showRCnoPost:false,
rcFoldImage:[
'http://lvchen.hostse.com/rc20/rc_0609_f.gif','留了言',
'http://lvchen.hostse.com/rc20/rc_0609_uf.gif','留言說:',
'
載入中...',
'全部展開','全部隱藏'],
otherText:['跳至留言','我跳','上一頁','下一頁','您正在看留言 %range%,共有 %totalNum% 則留言'],
reply:['http://lvchen.hostse.com/rc20/external.png','直接去留言'],
rcAuthorLinkFormat:'%author%',
rcTitleLinkFormat: '%g_szTitle%',
createDisplayFormat:'%rcAuthorLinkFormat% 於 %rcTitleLinkFormat%%replyImg% %rcSay% 「%content%」 - %timestamp%',
today:'今天',
authorLink:true,
rcDateFormat: 1
};
// add %replyURL% and %replyImg%
//----------- Define Global Variables, should NOT be modified---------------
jQuery.noConflict();
var rcSetting = {
g_szComments:[],
maxPostsNum:0, // Max posts number
commentStartIndex : 1, // find comment from
commentTotalNum : 0, // Total comments number
showAllFlag : false, // Check if the showAll button clicke
outDate:0,
titleArr:[],
linkArr:[],
replyArr:[]
};
var rcFunction = {}; // The only Globa function...maybe
// ----------- Add header buttons---------------
rcFunction.addHeaderButton = function ()
{
var headerButton = '';
else
headerButton += '';
jQuery('#divrc').before(headerButton);
};
//----------- Fetch comment JSON feed, several variables that is comments related are determine here ---------------
rcFunction.commentJSONfeed = function (json)
{
if (json.feed.entry!=undefined)
{
rcSetting.g_szComments = json.feed.entry;
if (rcSetting.commentTotalNum == 0)
rcSetting.commentTotalNum = json.feed.openSearch$totalResults.$t*1; // Retrive total number of comments
if (rcPreSetting.g_iShowCount > rcSetting.commentTotalNum)
rcPreSetting.g_iShowCount = rcSetting.commentTotalNum;
rcFunction.fetchPostsTitle(1,'');
}
else
{
jQuery('#divrc').html(rcPreSetting.noContent[1]);
}
};
rcFunction.fetchComments = function(Index,increment)
{
jQuery ('#jumpButton').attr('disabled','disabled');
var y_script = document.createElement('script');
var callbacksrc = 'http://' + rcPreSetting.g_szBlogDomain + '/feeds/comments/default?alt=json-in-script&callback=rcFunction.commentJSONfeed&max-results=' + increment + '&start-index='+ Index;
y_script.setAttribute('src',callbacksrc);
y_script.setAttribute('id', 'jsonCommnets');
y_script.setAttribute('type', 'text/javascript');
document.documentElement.firstChild.appendChild(y_script);
};
rcFunction.fetchPostsTitle = function(Index,source)
{
if (rcSetting.linkArr.length > 0 & source == '')
{
rcFunction.titleJSONfeed('');
}
else
{
var y_script = document.createElement('script');
if (source !='')
callbacksrc = source + '?&alt=json-in-script&callback=rcFunction.findLossTitles';
else
callbacksrc = 'http://' + rcPreSetting.g_szBlogDomain + '/feeds/posts/summary?max-results='+ rcPreSetting.cachesize + '&start-index=' + Index + '&alt=json-in-script&callback=rcFunction.titleJSONfeed';
y_script.setAttribute('src',callbacksrc);
y_script.setAttribute('id', 'jsonPosts');
y_script.setAttribute('type', 'text/javascript');
document.documentElement.firstChild.appendChild(y_script);
}
};
rcFunction.findTitle = function(orgLink)
{
var loop = rcPreSetting.cachesize; // set up looping time, default is 30, which means we check only 30 title every time.
for (var j = 0 ; j < loop ; j++) // This is main loop to chekc if the title of the link exists.
{
if (rcSetting.linkArr[j] == orgLink)
{
break;
}
}
if (j != loop)
return j;
else
{
//return ' '; // If title can not be found within a cycle, prepare to check again.
return -1;
}
};
//----------- An inner funciton that converts Month---------------
rcFunction.monthConvert = function(monthIndex)
{
var monthName=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
return monthName[(parseInt(monthIndex,10)-1)];
};
//----------- An inner function that check dates Get Today's Date---------------
rcFunction.checkTodayDate = function(dateString)
{
var timeStr = new Date();
var rcDateStr = new Date(parseInt(dateString.substr(0,4),10),parseInt(dateString.substr(5,2),10)-1,parseInt(dateString.substr(8,2),10));
//alert, I am not sure this method is working, Better test before try. 04/04/2008
//rcDateStr.setFullYear(parseInt(dateString.substr(0,4),10),parseInt(dateString.substr(5,2),10)-1,parseInt(dateString.substr(8,2),10));
if (rcDateStr.getYear() == timeStr.getYear())
{
if (rcDateStr.getMonth() == timeStr.getMonth())
{
if (rcDateStr.getDate() == timeStr.getDate())
return true;
else
return false;
}
else
return false;
}
else
return false;
};
//----------- Fetch posts JSON feed, several variables that is comments related are determine here ---------------
//----------- Look for matched titles and show comments ---------------
rcFunction.titleJSONfeed = function (posts)
{
// initial variables
var temp = '';
var checkLink = '' ;
var checkTitle = '';
var reaplyImg,replyURL,findIdx,g_szTitle,linkidx;
// Cache title and its URL
//replyURL = posts.feed.entry[0].link[1].href;
//if (posts.feed.entry != undefined) {
if (rcPreSetting.createDisplayFormat.indexOf('%rcTitleLinkFormat%') != -1) //cache if we need to find a title
{
if (posts != '' && posts.feed.entry != undefined)
{
var entries = posts.feed.entry;
if (rcSetting.maxPostsNum == 0)
rcSetting.maxPostsNum = posts.feed.openSearch$totalResults.$t*1;
if (entries.length*1 < rcPreSetting.cachesize) // if only a few posts, correct the cachesize
rcPreSetting.cachesize = entries.length*1;
for (var i = 0 ; i < rcPreSetting.cachesize ; i++)
{
var titleLinkIdx = replyLinkIdx ='';
var j = 0;
while (j < entries[i].link.length)
{
if (entries[i].link[j].rel == "alternate")
titleLinkIdx = j;
else if (entries[i].link[j].rel == "replies" && entries[i].link[j].type == 'text/html')
replyLinkIdx = j;
if (titleLinkIdx!='' && replyLinkIdx!='')
break;
j++;
}
rcSetting.linkArr[i] = entries[i].link[titleLinkIdx].href;
rcSetting.titleArr[i] = entries[i].title.$t;
if (replyLinkIdx!='')
rcSetting.replyArr[i] = entries[i].link[replyLinkIdx].href;
else
rcSetting.replyArr[i] = '';
}
}
}
//----------- Main loop to create a list of comments -------------------------------------------------------
//----------- Post URLs are located and compared for getting the name of its title ---------------
for (i=0; i < rcSetting.g_szComments.length*1; i++)
{
var comment = rcSetting.g_szComments[i]; // Extract comments from JSON
j = 0;
while (j 0)
var orgLink = link.replace(/(^.+)\?.*$/,'$1'); // Link URL of the Post
if (rcPreSetting.createDisplayFormat.indexOf('%rcTitleLinkFormat%') != -1)
{
if (orgLink != checkLink) // if the previous comment is from the same post, then we don't have to look for the title of the post
{
findIdx = rcFunction.findTitle(orgLink);
if (findIdx == -1)
{
if(comment['thr$in-reply-to']!=undefined)
{
g_szTitle = ''+ comment["thr$in-reply-to"].source +'';
replyURL = '';
reaplyImg = '
';
}
else
{
g_szTitle = '';
replyURL = '';
reaplyImg = '';
}
}
else
{
g_szTitle = rcSetting.titleArr[findIdx];
replyURL = rcSetting.replyArr[findIdx];
reaplyImg = '
';
}
checkTitle = g_szTitle;
checkLink = orgLink;
}
else
g_sztitle = checkTitle;
}
if (comment.content== undefined) // This will check wheather full or short comment feed
{
var content = comment.summary.$t;
if (content.length > 40)
var short_content = content.substr(0,40)+'...';
else
var short_content = content;
content = content.replace(/\u003c/g,'<'); // fix < 04/28/2008
content = content.replace(/\u003e/g,'>'); // fix > 04/28/2008
}
else
{
var content = comment.content.$t; // The complete content of a comment.
//blogger change the API, title missing. I use substring of content to create short_content 8/25/2007
var short_content = content.replace(/<.*?>/g,'');
if (short_content.length > 40)
short_content = short_content.substr(0,40)+'...';
}
content = content.replace(/\$/g,'$'); // fix $1 and $2 being replaced. 9/6/2007
var author = comment.author[0].name.$t; // Author's Name
//var reaplyURL = rcSetting.replyArr[findIdx];
//----------- Select Date Format and do some necessary modification---------------
if (rcPreSetting.rcDateFormat == 1)
{
var timestamp=comment.published.$t.substr(0,10); // Determine date and time.
if (rcFunction.checkTodayDate(timestamp))
timestamp = rcPreSetting.today + ' ' + comment.published.$t.substr(11,5);
}
else if (rcPreSetting.rcDateFormat == 2)
var timestamp=rcFunction.monthConvert(comment.published.$t.substr(5,2))+' '+comment.published.$t.substr(8,2);
short_content = short_content.replace(/"/gim,"""); // fix short_content masses up author display
// Create Authour's information
var g_szAuthorsLink = rcPreSetting.rcAuthorLinkFormat;
g_szAuthorsLink = g_szAuthorsLink.replace(/%link%/g,althorLink);
g_szAuthorsLink = g_szAuthorsLink.replace(/%timestamp%/g,timestamp);
g_szAuthorsLink = g_szAuthorsLink.replace(/%author%/g, author);
g_szAuthorsLink = g_szAuthorsLink.replace(/%replyURL%/g, replyURL);
g_szAuthorsLink = g_szAuthorsLink.replace(/%short_content%/g, short_content);
// Create title information
var g_orignalLink = rcPreSetting.rcTitleLinkFormat;
g_orignalLink = g_orignalLink.replace(/%orgLink%/g, orgLink);
g_orignalLink = g_orignalLink.replace(/%timestamp%/g, timestamp);
g_orignalLink = g_orignalLink.replace(/%g_szTitle%/g, g_szTitle);
g_orignalLink = g_orignalLink.replace(/%replyURL%/g, replyURL);
g_orignalLink = g_orignalLink.replace(/%short_content%/g, short_content);
var expendStyle= ' ';
// Finally, create the comment list
temp += '- '+ expendStyle;
var displayFormat = rcPreSetting.createDisplayFormat;
displayFormat = displayFormat.replace(/%timestamp%/g,'' + timestamp + '');
displayFormat = displayFormat.replace(/%rcAuthorLinkFormat%/g,'' + g_szAuthorsLink + '');
displayFormat = displayFormat.replace(/%rcTitleLinkFormat%/g,'' + g_orignalLink + '');
displayFormat = displayFormat.replace(/%replyImg%/g,reaplyImg);
if (!rcSetting.showAllFlag)
displayFormat = displayFormat.replace(/%rcSay%/g,''+rcPreSetting.rcFoldImage[1]+'');
else
displayFormat = displayFormat.replace(/%rcSay%/g,''+rcPreSetting.rcFoldImage[3]+'');
displayFormat = displayFormat.replace(/\s*(\S*)%content%(\S*)\s*/g, '$1'+content+'$2'); // This is somewhat tricky
temp += displayFormat + '
';
}
temp+='
';
jQuery('#divrc').html(temp);
// Some necessary operations to match custom format
//jQuery(document).ready(function()
//{
if (jQuery('#divrc li .comcontent').html() != null)
{// folding feature is used when full content exists.
jQuery('#divrc li').css({listStyle:'none',background:'none'});
if (!rcSetting.showAllFlag)
jQuery('#divrc li .rcfold').css('background','url('+rcPreSetting.rcFoldImage[0]+') center no-repeat');
else
jQuery('#divrc li .rcfold').css('background','url('+rcPreSetting.rcFoldImage[2]+') center no-repeat');
}
else
{// if full content does not exist, remove folding feature
jQuery('#divrc li').find('.rcfold').remove();
jQuery('#showAllButton').remove();
}
//});
rcFunction.RunAfterDomReady();
if (rcPreSetting.showRCnoPost)
jQuery('#divrc li:has(.postDeleted)').find('.rcPostTitle').replaceWith(rcPreSetting.noContent[0]).end().each(function(){
jQuery(this).find('.rcAuthor').html(jQuery(this).find('.rcAuthor').text());
});
else
jQuery('#divrc li:has(.postDeleted)').remove(); // If a post has been deleted, remove that comment.
rcSetting.outDate = jQuery('#divrc li:has(.noTitleMessage)').length;
if (rcSetting.outDate == 0) {
if (jQuery('#divrc li').length == 0)
jQuery('#divrc').html(rcPreSetting.noContent[1]);
rcFunction.addFooterButton();
}
else
rcFunction.titleReCheck(); // go to titleCheck to check if any title misses.
};
rcFunction.addFooterButton = function () //This function is only used in titleCheck(), so I make it an inner function for easy management
{
var text = rcPreSetting.otherText;
var commentInfo ='';
else
footerButton = '
'+text[3]+'';
}
else if (g_show + index > tnum)
{
footerButton = '
'+text[2]+'';
}
else
{
footerButton = '
'+text[2]+' '+text[3]+'';
}
jQuery('#divrc').after(commentInfo + footerButton);
//rcFunction.RunAfterDomReady();
};
rcFunction.titleReCheck = function()
{
var source = jQuery('#divrc li .noTitleMessage').eq(0).text();
jQuery('#jsonPosts').remove();
rcFunction.fetchPostsTitle(1,source);
};
// How do we find the title of a post that we miss at first check?
// We look again and then use jQuery to replace noTitleMessage'
// This function is called by titleCheck ONLY.
rcFunction.findLossTitles = function (posts)
{
var title = posts.entry.title.$t;
var link = posts.entry.link;
for (var i = 0 ; i < link.length ; i++)
{
if (link[i].rel == 'alternate')
var titleLink = link[i].href;
else if (link[i].rel == 'replies' && link[i].type == 'text/html')
var replyLink = link[i].href;
else if (link[i].rel == 'self')
var sourceLink = link[i].href;
}
jQuery('#divrc li:has(.noTitleMessage)').each(function(i){
if (jQuery('.noTitleMessage',this).text() == sourceLink)
{
jQuery(this).find('.replyImg a').attr('href',replyLink).end().find('.noTitleMessage').replaceWith(title);
rcSetting.outDate--;
}
});
if (rcSetting.outDate != 0)
rcFunction.titleReCheck();
else
rcFunction.addFooterButton();
};
//----------- Some actions run after list of comments are created. ---------------//on mouseover?? instead of hover
rcFunction.RunAfterDomReady = function ()
{
if (!rcSetting.showAllFlag)
jQuery('#divrc li').find('.comcontent').hide();
jQuery('#divrc li').find('.rcfold').hover(
function(){jQuery(this).css({cursor:'pointer'});},
function(){jQuery(this).css({cursor:'default'});}).click(
function() {
var comment = jQuery(this).parent().find('.comcontent');
if (comment.is(':visible')) {
jQuery(this).css('background','url(' + rcPreSetting.rcFoldImage[0] + ') center no-repeat');
jQuery(this).parent().find('.rcsay').html(rcPreSetting.rcFoldImage[1]);
comment.hide();
}
else
{
jQuery(this).css('background','url(' + rcPreSetting.rcFoldImage[2] +') center no-repeat');
jQuery(this).parent().find('.rcsay').html(rcPreSetting.rcFoldImage[3]);
comment.show();
}
});
//if (rcSetting.showAllFlag) {rcSetting.showAllFlag= false; rcFunction.showOrHideAll();}
};
// ----------- Handler for showing or hiding all comment -----------
// ----------- Dynamic onclick does not work, so I merge two function into one. it seems working and running better ! 10/17 -----------
rcFunction.showOrHideAll = function()
{
if (rcSetting.showAllFlag)
{
jQuery('.comcontent').hide();
jQuery('.rcfold').css('background','url('+ rcPreSetting.rcFoldImage[0] +') center no-repeat');
jQuery('#headerButton a:eq(0)').html(rcPreSetting.rcFoldImage[5]);
jQuery('#divrc .rcsay').html(rcPreSetting.rcFoldImage[1]);
rcSetting.showAllFlag = false;
}
else
{
jQuery('.comcontent').show();
jQuery('.rcfold').css('background','url('+ rcPreSetting.rcFoldImage[2] +') center no-repeat');
jQuery('#headerButton a:eq(0)').html(rcPreSetting.rcFoldImage[6]);
jQuery('#divrc .rcsay').html(rcPreSetting.rcFoldImage[3]);
rcSetting.showAllFlag = true;
}
};
/*----------- ChangePage, backward, forward, or jump ---------------
//----------- Direction = -1, backward ---------------
//----------- Direction = 0, jump by index ---------------
//----------- Direction = 1, forward ---------------
//----------- Direction = 2, jump by date ---------------
//----------- indexIwant is index I want --------------*/
rcFunction.changePage = function(direction,indexIwant)
{
var jump = true;
if (direction == 1) // Next page
rcSetting.commentStartIndex += rcPreSetting.g_iShowCount;
else if (direction == -1) // previsous page
rcSetting.commentStartIndex -= rcPreSetting.g_iShowCount;
else
{//rcFunction.gotoIndexIwant = function (indexIwant)
if (indexIwant > rcSetting.commentTotalNum)
indexIwant = rcSetting.commentTotalNum;
else if (indexIwant < 1)
indexIwant = 1;
var pageNumber = Math.ceil(indexIwant/rcPreSetting.g_iShowCount);
if (pageNumber == Math.ceil(rcSetting.commentStartIndex/rcPreSetting.g_iShowCount))
jump = false;
else
rcSetting.commentStartIndex = (pageNumber-1)*rcPreSetting.g_iShowCount + 1;
}
if (jump)// 同一頁不跳, 不同頁我跳
{
jQuery('#jsonCommnets').remove();
jQuery('#jsonPosts').remove();
jQuery('#divrc').next().remove().end().after(''+rcPreSetting.rcFoldImage[4]+'
');
if (rcSetting.commentStartIndex + rcPreSetting.g_iShowCount > rcSetting.commentTotalNum)
rcFunction.fetchComments(rcSetting.commentStartIndex, rcSetting.commentTotalNum - rcSetting.commentStartIndex + 1);
else
rcFunction.fetchComments(rcSetting.commentStartIndex, rcPreSetting.g_iShowCount);
}
};
// ----------- Run after DOM ready -----------
jQuery(document).ready(function()
{
//----------- Create basic structure---------------
var rcStart = jQuery('div.widget-content').filter(':contains(###recentComment###)').slice(0,1); // Choise only the first tag we found. 9/23
if(rcStart.length > 0)
{
rcStart.find('script').remove(); //fix script confict while two script in the same widget. 08/24/2007
rcStart.html(rcStart.html().replace(/###recentComment###/i,''));
jQuery('#divrc').html(rcPreSetting.rcFoldImage[4]);
rcFunction.addHeaderButton();
// Why not add footer here? Because the info in footer is mainly depending on the comment body
rcFunction.fetchComments(rcSetting.commentStartIndex, rcPreSetting.g_iShowCount);
}
});