2008-01-24

「最新回應」模組 (Recent Comments)

剛剛翻了我自己的文章,發現我沒有寫過任何關於「最新回應」模組的文章,原因很簡單,我認為只要會搞「最新文章」模組的人,只要把自己文章的 RSS Feed 引用網址,改成文章回應的 Feed,最新文章當場就變成最新回應了。當然事情沒有想像中那麼單純,一方面最新文章想看到的和最新回應的內容會有些不一樣,像是最新文章只要列出時間、標題就好,但是最新回應要列出作者(回應者)、而 Blogger 單純的回應 Feed 裡面是沒有回應原文的標題 (Title 是部份的 Content)、單篇回應的連結多了「錨點」等等,加上,如果用我「最新文章」模組直接不改變數和函式名稱,想直接變成「最新回應」模組,又會有因為 Javascript 定義重複導致功能不正常的問題,好啦!那我直接改一篇最新回應的模組,有需要的就可以直接套用、不用再自己改了。

技術原理就和最新文章的一模一樣、透過 JSON 來抓 Feed 內容過濾排版成要呈現的樣子,大致版面(和能提供的資料)就這樣子,我也將能找出總數的方法一併套用,減少全域變數的使用,讓程式碼更加精簡(又少了十行吧~也併回頭去調整過「最新文章」的程式碼)。很抱歉,如果想顯示某回應原始的文章標題,光從 Blogger 提供的 Comment Feed 是找不到的,除非很麻煩地去一篇篇比對找出來,沒有更好的辦法前就先這樣囉!當然,之前提過 lvchen 的這篇文章 Recent Comment for Blogger 提供更棒更好的「最新回應」模組,也提供簡單安裝的懶人包,不介意用外掛 .js 或不想去搞程式碼的可以去使用,這應該是我目前看過最好最推薦的了。回到本題,如果還是想套用小弟的「最新回應」模組,動作依然是那幾個,首先,在範本、網頁元素裡加入一個新的網頁元素 (HTML/Javascript,或是直接編輯原有模組),塞入以下程式碼:

<div id="newComments">
  <noscript>failed!<br/>Javascript not supported here!</noscript>
</div>

<script>
var nCommentStartIndex = 1;
var nCommentShow = 8;

function showRecentComments(nIndex) {
  if (!nIndex)
    nIndex = nCommentStartIndex;
  var sFeedURL = '/feeds/comments/summary?orderby=published&start-index='+nIndex+'&max-results='+(nCommentShow+1)+'&alt=json-in-script&callback=generateComments';
  var script = document.createElement('script');
  document.getElementById('newComments').innerHTML = 'Loading <blink>...</blink>';
  script.setAttribute('src', sFeedURL);
  script.setAttribute('type', 'text/javascript');
  document.documentElement.firstChild.appendChild(script);
}

function generateComments(json) {
  function compareentry(a,b) {
    order= Date.parse(a.published.$t.replace(/^(\d{4})-(\d{2})-(\d{2})T([0-9:]*)([.0-9]*)(.)(.*)$/, '$1/$2/$3 $4 GMT')) - Date.parse(b.published.$t.replace(/^(\d{4})-(\d{2})-(\d{2})T([0-9:]*)([.0-9]*)(.)(.*)$/, '$1/$2/$3 $4 GMT'));
    return 0-order;
  }
 
  var sHTML = '<ul>';
  var sortentry = json.feed.entry.sort(compareentry);
  var nIndex = parseInt(json.feed.openSearch$startIndex.$t);
  var nTotalComment = parseInt(json.feed.openSearch$totalResults.$t);
  for (var i = 0, Comment; Comment = sortentry[i]; i++) {
    if (i >= nCommentShow)
      break;
    var authorname = Comment.author[0].name.$t;
    var title = Comment.title.$t.substr(0,30);
    var j = 0;
    while (j < Comment.link.length && Comment.link[j].rel != "alternate")
      j++;
    var link = Comment.link[j].href;
    var timestamp = Comment.published.$t.substr(0,10);
    sHTML += '<li><span class="item-title">'+authorname+': <a href="'+link+'">'+ title +'</a> - '+timestamp+'</span></li>';
  }
  sHTML += '</ul>';
  sHTML += generateCommentLink(parseInt(json.feed.entry.length), nIndex, nTotalComment);
  document.getElementById('newComments').innerHTML = sHTML;
}

function generateCommentLink(nFetch, nIndex, nTotalComment) {
  var bOld = (nFetch > nCommentShow);
  if (bOld) nFetch = nCommentShow;
  var sResult = '<p align="right">Recent '+nIndex+'-'+(nIndex+nFetch-1)+', total: '+nTotalComment+'.&nbsp;&nbsp;';
  if (nIndex > nCommentStartIndex)
    sResult += '<a href="javascript:showRecentComments('+(nIndex-nCommentShow)+');" title="Newer Comments">&lt;&lt;</a>&nbsp;&nbsp;';
  if (bOld)
    sResult += '<a href="javascript:showRecentComments('+(nIndex+nCommentShow)+');" title="Older Comments">&gt;&gt;</a>';
  sResult += '</p>'; 
  return sResult;
}
</script>

<script language="javascript">showRecentComments();</script>

存檔搞定。同樣的,程式碼中紅色的那兩行一樣視個人狀況調整,我的範例是從最新的「第一個回應」、每一頁顯示「八篇回應」,如果要套用什麼其他文字、樣式或圖示,那就自己修改囉!

回應: 77

 

2008-01-17

計算 Blog 的文章總數和回應總數

上一篇文章才在講,如果能夠知道文章或回應的總數,透過迴圈固定數量去分割,就可以做出逐頁分頁的效果,甚至也可以讓使用者跳到「某一頁」。這些分頁效果的關鍵,就是從 Feed 得知該 Feed 總共有幾篇文章,用那個數量變數來切割,而一般 Feed 提供固定數量的文章(像 Blogger 預設 20 篇),單純用 JSON 或其他方法抓 Feed 內容來分析,也不能知道「總數」,直到我看到 lvchenRecent Comment for Blogger 外掛懶人包,哇!好華麗好炫的「加強型最新回應」模組啊!當然,除了上下頁、跳頁之外,我有興趣的是,那個右下角的「總回應數」是怎麼算出來的啊!也是從 Feed 中取出的嗎?變數名稱是啥?

這個懸而未決的疑問又然起了我的 RD 魂,於是乎下載了 lvchen 的懶人包 .js 參詳一番,喔!原來是 JSON callback 傳來的 Feed 內容,有一個函式變數是傳回「總數」的:

var total = json.feed.openSearch$totalResults.$t;

應用 JSON 的方法,用 Javascript callback 一個函式、傳入 Feed、套用以上函式就可以得到文章總數哩!馬上實作,在 Blogger 裡加入一個新的網頁元素 (HTML/Javascript),自己給標題並塞入以下程式碼:

<script>
function calculateAmount(json) {
  document.write(json.feed.openSearch$totalResults.$t);
}
</script>
<!-- Calculate the total amount of feed -->
<p>Post: <script src="/feeds/posts/default?alt=json-in-script&callback=calculateAmount" type="text/javascript"/></script>, Comment: <script src="/feeds/comments/default?alt=json-in-script&callback=calculateAmount" type="text/javascript"/></script></p>

存檔之後,你就會發現,多了一個會秀出總文章數 (Post) 以及總回應數 (Comment) 的模組,不到十行就搞定!當然,秀總數只是第一步,知道所有文章的數量,除了上面提到可以處理分頁效果,像是隨機顯示文章的模組也可以達成,此時只想讚嘆:Blogger 實在是太神了啊!函式除了以上獨立的用法,當然可以嵌入任何有用到 JSON 和 Feed 的 Hack 和模組,像是「最新文章」和「最新回應」可以多秀文章或回應的總數,分別嵌入這些模組的好處是可以用現成的 Feed、不用浪費時間再 Query 一次,這樣才能滿足我輩兼顧資料豐富和瀏覽效能的要求啊!(本文範例同步更新原文:最新文章模組「終極版」

發現這個玩意之後,我好奇的是哪裡有定義這些 Feed 資料的結構和相關函式,Google 了一下,原來這玩意叫做:Google Data API (GData),上述文章用到 Feed 資料結構,在這個 GData 的 document format reference 有提到,原來很多 Blogger 透過 JSON 取用來的 Feed 完整資料結構都定義在這啊!我還只是單純地查詢需要的資料並顯示出來,上面還提供很多方法讓使用者能夠直接操作和存取各種 Google 的網路服務,舉凡 Google Apps, Blogger, Calendar, Notebook, Gocs, Picasa, YouTube,都可以透過瞭解這些資料結構和方法去存取,靠簡單的 Javascript 來做處理,不管是開發自己的 Widget、或是嵌入自己的網站,都提供了無限想像的應用啊,太神了,這可能是一般自架 Blog、或是國內一些 BSP 永遠難望其項背的~

回應: 14

 

2008-01-09

最新文章模組「終極版」

好一陣子都沒有更新這裡的文章,不是因為我偷懶,實在是不知道有什麼技術文章可以寫,尤其是 Blogger Hack,版面改得差不多後其實也沒什麼好調整的了。尤其是我不喜歡像某些人改樣板總要外引他人的 .js 去做,用起來好像很方便,但根本不知其所以然,尤其是該檔案裡面有些作者自己用的 Code 或是 Function,引用者常看都不看直接下載就用了(更狠的就是根本不下載、直接引用作者的連結檔案),不小心改爛樣板或是與其他 Hack 衝突就哭天喊地了。當然我多少也會肯定一些「懶人包」的作用,它們的確幫助很多人簡易地調整 Blogger 外觀功能,但我自己仍偏好修改程式碼,一方面有效率、又不用塞一些不必要的程式碼,另一方面我很清楚這些 Hack 的作用和功能,樣板比較不會改爛掉。廢話那麼多,主要還是提醒參考我 Hack 的人,除非很清楚你在改什麼,不然還是參考人家寫的教學文章來做,不要完全不懂改壞了才一直來求救,還劈頭就說「我什麼都不懂」。

關於「最新文章」的模組,相關文章我已經有兩篇,第一篇最單純,就是使用 JSON 來做、把 Feed 抓來的內容挑幾篇出來、依照格式作成列表,除了「最新文章」,依照引用的 Feed 網址,你可以把這個模組改成「最新回應」、「Blog 聯播」或是「主題推薦文章」。第二篇「其他最近的文章」,根據首頁需求做了調整,因為首頁也會顯示最新文章,另外掛一個「最新文章」模組好像怪怪的,所以我就調整顯示的篇數和起始點,首頁如果有五篇文章,那麼模組裡就另外列第六篇到第十五篇總共十篇的標題。而這一篇是根據網友的建議再做延伸,既然另外顯示了十篇文章標題列表,能不能參考 Neo 一些 Template 和 Hack 的作法、做出上一頁下一頁的連結,透過 JSON 不用重新 Load 整個頁面的好處,在該模組裡就能瀏覽所有的文章標題?我花點時間看了一下 Neo 的作法,其實不難,用我前兩篇的基礎加幾個函數處理上下頁的問題就好了,我覺得興奮的是,這樣修改的應用、可以套用在任何文章或留言列表、只要加個連結,讀者不用 Reload 就可以直接切換上下頁觀看搜尋或列表結果,進而改善瀏覽的速度和效果,這才是這個模組背後帶來的好處(不是單純提供最新文章列表那麼簡單)。

開始修改!在範本、網頁元素裡,請加入一個新的網頁元素 (HTML/Javascript)、或是直接編輯原有模組,直接塞入以下程式碼:

<div id="newPosts">
  <noscript>failed!<br/>Javascript not supported here!</noscript>
</div>

<script>
var nPostStartIndex = 6;
var nPostShow = 10;

function showRecentPosts(nIndex) {
  if (!nIndex)
    nIndex = nPostStartIndex;
  var sFeedURL = '/feeds/posts/summary?orderby=published&start-index='+nIndex+'&max-results='+(nPostShow+1)+'&alt=json-in-script&callback=generatePosts';
  var script = document.createElement('script');
  document.getElementById('newPosts').innerHTML = 'Loading <blink>...</blink>';
  script.setAttribute('src', sFeedURL);
  script.setAttribute('type', 'text/javascript');
  document.documentElement.firstChild.appendChild(script);
}

function generatePosts(json) {
  function compareentry(a,b) {
    order= Date.parse(a.published.$t.replace(/^(\d{4})-(\d{2})-(\d{2})T([0-9:]*)([.0-9]*)(.)(.*)$/, '$1/$2/$3 $4 GMT')) - Date.parse(b.published.$t.replace(/^(\d{4})-(\d{2})-(\d{2})T([0-9:]*)([.0-9]*)(.)(.*)$/, '$1/$2/$3 $4 GMT'));
    return 0-order;
  }
 
  var sHTML = '<ul>';
  var sortentry = json.feed.entry.sort(compareentry);
  var nIndex = parseInt(json.feed.openSearch$startIndex.$t);
  var nTotalPost = parseInt(json.feed.openSearch$totalResults.$t);
  for (var i = 0, Post; Post = sortentry[i]; i++) {
    if (i >= nPostShow)
      break;
    var title = Post.title.$t;
    var j = 0;
    while (j < Post.link.length && Post.link[j].rel != "alternate")
      j++;
    var link = Post.link[j].href;
    var timestamp=Post.published.$t.substr(0,10);
    sHTML += '<li><span class="item-title"><a href="'+link+'">'+ title +'</a> - '+timestamp+'</span></li>';
  }
  sHTML += '</ul>';
  sHTML += generatePostLink(parseInt(json.feed.entry.length), nIndex, nTotalPost);
  document.getElementById('newPosts').innerHTML = sHTML;
}

function generatePostLink(nFetch, nIndex, nTotalPost) {
  var bOld = (nFetch > nPostShow);
  if (bOld) nFetch = nPostShow;
  var sResult = '<p align="right">Recent '+nIndex+'-'+(nIndex+nFetch-1)+', total: '+nTotalPost+'.&nbsp;&nbsp;';
  if (nIndex > nPostStartIndex)
    sResult += '<a href="javascript:showRecentPosts('+(nIndex-nPostShow)+');" title="Newer Posts">&lt;&lt;</a>&nbsp;&nbsp;';
  if (bOld)
    sResult += '<a href="javascript:showRecentPosts('+(nIndex+nPostShow)+');" title="Older Posts">&gt;&gt;</a>';
  sResult += '</p>'; 
  return sResult;
}
</script>

<script language="javascript">showRecentPosts();</script>

直接存檔就好了。啥?那樣不是比用誰的教學文章更簡單更快嗎?而且,還不用改樣板原始碼?那是因為我把人家外引的 .js 檔裡面的宣告函式和呼叫直接寫在上面,總共有六十幾行(好多,比我的上一版多了一倍),用的方法和 Neo 大同小異,但程式碼至少比 Neo 少了四五倍(因為人家還有其他自用函式,不一定用在這裡的),單就以上功能上面那些就夠了。要使用本 Hack 前,同樣地請注意以上標紅的那幾行,照個人需求修改。變數 nPostStartIndex 是此模組要顯示的文章是從「第幾篇」開始,像我首頁顯示五篇文章,起始文章就從 6 開始,要從頭開始請設成 1 (尤其你用來顯示最新回應或其他 Blog 聯播時)。而 nPostShow 是該模組一次顯示「幾篇文章」的數量變數,預設是十篇。另外,在等待讀取的過程,以上範例我是用文字 "Loading" 顯示,如果想和我一樣改成動畫(其實是一個動態的 GIF 圖檔,有興趣可以去這個網站取用,多是免費授權使用的),請把範例程式碼裡第十四行 'Loading ...' 改成你圖檔連結的 HTML 程式碼就行了。同樣的,切換上下頁的連結,範例中是用 << >> 箭頭,當然也可以改成你喜歡的圖片。

有了上下頁,我想最棒的是,可以讓使用者跳到「某一頁」是最好,同時也列出「總共有幾頁」、「全部有幾篇文章」,不過就現在用 JSON 去逐篇拉出 Feed 裡文章標題和數量的方法,沒辦法一次就知道「總數」,分頁和顯示的問題也不容易處理,我知道還是有 Hack 好像能做到,但我還是偏好簡單、有效率又獨立安全的辦法。如果有找到,本文會再改個第四版「最後終極版」好了,哈哈~

回應: 88