2008-11-21

Feed 訂閱網址轉換

根據我另一個 Blog 的這篇文章,原先有訂閱我 FeedBurner 網址的朋友,可以更改訂閱的網址,從 http://feeds.feedburner.com/abinlee-tech,改成了 http://feedproxy.google.com/abinlee-tech (當然,如果你訂閱的還是 http://abintech.twidv.com/feeds/posts/default 那就沒差啦),有空沒事的話就改一下吧!(基本上懶得改也沒關係啦,會自動轉址~)

之前這篇文章提到,Blogger 「預設」的 Feed 網址其實是塞在預設的「檔頭」(head-content) 裡面,自從 FeedBurner 和 Blogger 整合以後,只要到 Blogger 設定(可以參考官方文件),即使看原始碼裡面「好像」還是原來的官方 Feed (就是像 http://xxx.blogspot.com/feeds/posts/default 這樣),但當使用者透過 Firefox 或其他工具訂閱 Blog 時,也會「自動」轉址到 FeedBurner 的頁面,所以就不需要多費工夫在檔頭後面手動再加 FeedBurner 的訂閱 Link 了,也因此,新訂閱我 Blog 的朋友,透過你的瀏覽器或網路服務直接訂閱,自然就會用到最新的 FeedBurner 網址(feedproxy.google.com),一整個無痛就是啦~

回應: 4

 

2008-10-27

內嵌的文章留言 (Embedded Comment Form)

自從四個月前 Blogger in Draft 發表了多項新功能後,其中除了 Import / Export 這個備份功能外,我最感興趣的就是終於有內嵌式的文章留言了。當然第一時間我也玩了一下,功能面我很滿意,但是在自訂外觀和手動調整方面限制很多,一方面懶、另一方面想說等正式版公佈後再花時間研究,所以遲遲沒有套用在 Blog 上。終於在上禮拜,Blogger 官方 Blog 宣佈這個功能在正式版上可以使用,當然我也掛來玩,發現在 Draft 上的限制依舊,但功能更完整和強大了,既然都是正式版了,那還是來更新一下吧!

官方說明該功能要啟用很簡單,只要登入 Blogger 的後台介面,在「設定」、「意見」的地方,將「意見欄位置」,從原來的地方改成「已內嵌下列文章」(Embedded below post, 嵌入文章下方,翻譯的真爛)就好了!但如果這樣啟用,在文章的下方卻沒看到可以直接回應文章的表單的話,那代表你使用了非官方的樣板、或像我曾經改動過程式碼,讓系統找不到能嵌入原始碼的地方,為求安全起見,Blogger 不敢硬塞破壞原來的版面,所以啟用後才會看不見,如果仍堅持要用這個功能,請乖乖修改程式碼 Hack 一下,才能正確啟用囉!(如果你到這個步驟就看得到內嵌的意見欄,那下面就可以跳過啦!)

嵌入式留言功能的設定方法(順利的話這個步驟就能完成)

動手修改之前,我比較了一下前後程式碼的差別。官方為了實現這個功能,產生了一段多的程式碼(照理來說這段每個人的樣板裡都有,打開「版面配置」、「修改HTML」、「開啟小裝置範本」,搜尋一下就找得到),讓使用者能 include 這個「模組」,這段原始程式碼如下(如果找不到,自己找類似的地方複製貼上):

<b:includable id='comment-form' var='post'>
  <div class='comment-form'>
    <a name='comment-form'/>
    <h4 id='comment-post-message'><data:postCommentMsg/></h4>
    <p><data:blogCommentMessage/></p>
    <data:blogTeamBlogMessage/>
    <a expr:href='data:post.commentFormIframeSrc' id='comment-editor-src'/>
    <iframe allowtransparency='true' class='blogger-iframe-colorize' frameborder='0' height='275' id='comment-editor' scrolling='auto' src='' width='100%'/>
    <data:post.iframeColorizer/>
  </div>
</b:includable>

顧名思義,這個 comment-form 模組就是顯示「內嵌意見欄」的程式碼定義,仔細一看,沒錯,是用之前曾經人人喊打的 iframe 語法,如果端詳一下原始碼,會發現嵌入 iframe 裡的是一隻 Javascript 程式,不過為了實現很多神奇的功能(容後述),似乎也不得不這樣做。但也因為意見欄完全由內嵌 iframe 的 Javascript 產生,如果更要改樣式內容就變得很困難(這就是我前面說的限制),還好那個程式寫得還不錯,當嵌入 Blog 後,會根據你 Blog 的背景和文字樣式自動調整,所以顏色字型不至於格格不入。如果樣板裡有發現上面那段程式碼,接下來才進到 Hack 的重點:要找到「內嵌意見欄」程式碼的正確位置,然後塞進顯示的程式碼。

程式碼很短,但找到正確的位置很不容易(人眼都不一定看得出來,也難怪 Blogger 系統找不到),我 Google 了一些修改的文章,裡面範例的程式碼和我的樣板不大一樣,所以這裡只能交代一下「大原則」,不要問我為什麼你的樣板裡面找不到(尤其是只想 search & replace 的人),因為每個人的樣板都不一樣。原來樣板裡的「發表留言」,程式碼只不過是一行帶你到留言介面的「連結」,這個連結會塞在首頁,以及文章的後面,連結的程式碼為:

<a expr:href='data:post.addCommentUrl' expr:onclick='data:post.addCommentOnclick'><data:postCommentMsg/></a>

所以你在搜尋上面這段的時候,如果有多筆就要判斷一下(只有一筆就直接改啦),基本上是在 <p class='comment-footer'> 這一行下面、最靠近它的那一筆,才是嵌入文章下方的正確位置。上面這一行的內容是找出留言的連結網址 (a expr...)、還有顯示發表留言的訊息(<data:postCommentMsg/>,像我的 Blog 這個訊息就是「張貼意見」),要嵌入意見欄、哪天不想用又可以改回來(到「設定」、「意見」切換回「整頁模式」就行),把上面那一行取代成下面這樣(如果你原始碼裡有其他樣式定義,請自行補足):

<b:if cond='data:post.embedCommentForm'>
  <b:include data='post' name='comment-form'/>
<b:else/>
  <a expr:href='data:post.addCommentUrl' expr:onclick='data:post.addCommentOnclick'><data:postCommentMsg/></a>
</b:if>

有點程式基礎的應該看得懂,我用了一個 if 的判斷句,如果在設定那邊開啟「已內嵌下列文章」(cond='data:post.embedCommentForm'),那就使用 comment-form 這個模組,不然,請使用可以跳到留言介面的連結,順利的話這樣就搞定啦~如果你對留言的樣式不滿意,就像上面講的,iframe 裡面定義的玩意很難改動,我唯一有做的,就是把這個意見欄寬度,改得和我 Blog 文章一樣,不然就那麼一小塊看起來怪怪的。改法也不難,就是在 CSS 樣式區段裡 (<b:skin> 後面)、加入覆蓋原定義的宣告:

/* The comment-form width */
.comment-form {
  max-width: 650px !important;
  width: 650px !important;
}

紅色的值請照每個人樣板的寬度自行調整。我個人覺得原來另外跳出的留言介面(整頁模式)其實也很不錯,不但不用改就能顯示頭像、留言的身份和登入系統整合的也比較完整,重點還有防垃圾留言的機制。內嵌的意見欄雖然比較簡潔、也符合一般 Blog 的習慣,但功能還不夠齊全,還好未來 Blogger 官方只要修改 iframe 嵌入的那隻 Javascript,應該也能無痛升級,這點就請各位期待囉~

這個內嵌意見欄比起一般 Blog 的留言有幾個特異功能,首先是留言的身份,除了最基本的匿名、提供名字網址、用 Blogger 身份,還有像 AOL、OpenID 或幾種常見的 Blog 系統帳號,當然,只要你有輸入過,它也會 Log 起來,不過特別的是它把紀錄的 cookie 直接整合進下拉的 combo list 上面,這樣就不用仰賴瀏覽器的「自動完成」啦!再者,由於提供多種系統的登入支援,登入資訊是用 popup 的小視窗動態顯示在 iframe 裡,所以登入、文章預覽和留言結果,都是塞在 iframe 的範圍內,這樣好不好見仁見智,但至少不用跳轉下一頁、而且又不會破壞版面 Layout。最後,也是網友熱烈要求的「訂閱」功能,只要使用者留言時的「身份」裡面有正確的 e-mail,點下「訂閱」的連結,只要該文章的留言有更新或回覆時,系統都會主動寄 e-mail 來通知你,這樣對留言發問的人來說,就不用一直盯著留言等回應了~

留言身份的整合機制、留言的訂閱功能

其實除了嵌入式的留言介面,還有 RankingStar Rating 評分機制,和最新的「追蹤者」紀錄、還有 iGoogle 的文章發表 Widget ,都看得出來 Blogger 一直在成長和進步,而且都不需要 Hack 修改就能用(反而,如果你像我去 Hack,會導致這些新功能不能用,就像這個嵌入式留言還要修改才出得來)。所以如果是新手使用者、沒時間或能力來修改,就和一般 BSP 一樣,直接用官方的樣板和模組,套用起來既簡單又方便,像我一樣不安於室的、或嫌官方功能不好用的,才需要手動亂改,而且要擔的風險就像這個 Hack 一樣,一日 Hack 就終身都要 Hack 了,雖然可以隨心所欲把 Blog 調整成自己想要的樣子,但手動調整功能和定期維護都是免不了的啦~

回應: 77

 

2008-10-24

網誌存檔預設收合 (Archives Default Collapsed)

原先我是用「網誌存檔」這個 Blogger 的預設模組來做 Archive Calendar,後來搞出 Feed Calendar 後,這個 Archive 模組還是保留(就是我現在版面、旁邊的 MONTHLY ARCHIVE),而且恢復了它的原貌。我喜歡用的樣式是「階層」(Hierarchy)、每月存檔一次,好處是可以依年月動態展開自己發表的文章,方便統計和依時間快速索引。不過這個樣式有個缺點,就是預設會展開、讓在 Sidebar 這個模組變得很長,偏偏 Blogger 並沒有調整預設不要展開的選項,所以只好手動 Hack 一下。

這個 Hack 要修改樣板的原始碼。到「版面配置」、「修改 HTML」、勾選「展開小裝置範本」(記得先手動備份樣板),先找到「網誌存檔」的區段(type='BlogArchive'),找到下面這一段:

<li expr:class='&quot;archivedate &quot; + data:i.expclass'>

然後取代成下面這樣(如果找不到,找關鍵字 data:i.expclass 也行),意思是預設將網誌存檔的樹狀結構收合折疊起來:

<li expr:class='&quot;archivedate collapsed&quot;'>

這樣是收起來了沒錯,但是怎麼預設的箭頭符號還是展開的啊?這一點也要修正。找一下下面這段程式碼:

<b:includable id='toggle' var='interval'>
  <b:if cond='data:interval.toggleId'>
  <b:if cond='data:interval.expclass == &quot;expanded&quot;'>
    <a class='toggle' expr:href='data:widget.actionUrl + &quot;&amp;action=toggle&quot; + &quot;&amp;dir=close&amp;toggle=&quot; + data:interval.toggleId +       &quot;&amp;toggleopen=&quot; + data:toggleopen'>
        <span class='zippy toggle-open'>&#9660; </span>
    </a>
  <b:else/>
    <a class='toggle' expr:href='data:widget.actionUrl + &quot;&amp;action=toggle&quot; + &quot;&amp;dir=open&amp;toggle=&quot; + data:interval.toggleId +         &quot;&amp;toggleopen=&quot; + data:toggleopen'>
          <span class='zippy'>

就修改紅色的地方,把 close 改成 open (預設是展開的,所以連結點下去會收合,改成預設收合後,連結點下去就是 open 囉!),然後把 &#9660; 改成 &#9658;(更改圖示),這樣才算是完整地修改預設的畫面顯示和操作。最後,因為預設這些展開、收合的圖示是用 ASCII 碼中的特殊符號,在 IE6 裡面一樣會莫名其妙放大(再罵一次,IE 真爛),如果你覺得礙眼,一樣可以透過 CSS 來修正。在 CSS 區段(標籤 <b:skin> 後面)裡加入下面的定義就搞定了:

#ArchiveList .toggle-open {  /* Fixed for IE */
  _font-size: 90% !important;
}

其實這篇不是什麼新的玩意,我一直都有在用,只不過用過當時忘了記錄下來,突然臨時發生狀況時不知所措,才趕緊再去找一下解決方案,然後再筆記起來。

參考文章:收縮Blog Archive!

回應: 13

 

2008-10-22

自訂樣板的變數 (Blogger Template Variable)

自從上次發生 Google Pages 放出即將停止的消息,相信很多人和我一樣,原本將 Google Pages 當圖床的,紛紛轉換地方用來 Hosting 部落格專用的圖檔。不知道大家是不是覺得很麻煩,要去找出樣板裡所有 Hard-code 的圖檔網址和檔名,然後一一取代到新圖床的位置。尤其像我用了不少 Hack,還要一個個模組檢查,真是辛苦。本來想說就做一次而已,認了~但,最近重灌電腦,預設的 IE 回復成 6.0,有天瀏覽自己 Blog 時發現,怎麼所有透明底圖和動態的 GIF 都不透明了 (GIF transparency)?雖然在 IE7 和 Firefox 是正常的,但還在用 IE6 的人應該還不少吧!Google 完發現好像是我新圖床:Google Sites 的問題!這可不是開玩笑的,Bug 修正前我只好暫時再回去用 Google Pages 頂一下,不過,之前做過一次更改圖片路徑的事情又要全部重作一次,實在好累啊!

後來思考,程式寫多了,能不能搞一個類似全域變數,用一個變數來指定圖床的網址,以後有任何更改,只要改這一個變數內容就行啦!試了很多辦法,包含用 Javascript 做,都沒辦法做到我希望看到的程度,回頭看看 Blogger 的原始樣板,沒錯,在 CSS 區段內是有 Blogger 樣式變數的定義,官方也有文件解釋使用方法和語法,不過文件中標明了「這是用來自訂文字和顏色」使用的(也就是用在「版面配置」、「字型和顏色」的版面設定),type 也只有 font 和 color 兩種,沒搞頭啊!不過反骨的我當然不放棄,管他的,先試了再說。開始前先講一下這種樣板變數的預設用法。在樣板裡面,定義樣式變數的區段是在 <b:skin> 標籤的後面,拿我的樣板擷取部份來舉個例子:

/* Variable definitions
   ====================
   <Variable name="bgcolor" description="Page Background Color"
             type="color" default="#000" value="#000000">
   <Variable name="textcolor" description="Text Color"
             type="color" default="#ccc" value="#cccccc">
   <Variable name="linkcolor" description="Link Color"
             type="color" default="#9ad" value="#99aadd">
......

這一段是用來宣告和定義 CSS 使用的自訂變數型態和內容,像是 bgcolor 變數,就是設定 Blog 的背景顏色,textcolor 想當然是文字顏色。當在調整 CSS 顏色或字型時,像是某個區塊要指定 Blog 背景色為該模組的背景、文字也套用整個 Blog 的字型顏色,在 CSS 定義裡就可以寫成像:

code.cmd {
  color: $textcolor;
  background: $bgcolor;
}

是的,呼叫的方式就是直接用 $變數名稱。進入重點,我們來自己定義一個變數,當作外連圖片的網址:

<Variable name="imgURL" description="Image hosting URL"
             type="automatic" value="http://abinlee.link.googlepages.com">

其中變數名稱叫 imgURL,變數的內容(網址)為 value 裡的定義(請自行修改),那麼只要在 Blogger 專用的 CSS 區段裡(<b:skin> </b:skin>),都可以用 $imgURL 來取用圖片網址這個變數囉!還記得我在這篇「在版面格式中放入小圖示」的文章中提到,把常用圖片從 <img> 標籤改成了 <span> 定義、以 CSS 取代傳統圖片連結,這時候又派上用場,「所有」 Blog 裡用到的小圖片我都用 CSS 定義取代,也因此這些圖片的網址就都能用這個自訂變數啦!舉個例,我 Blog 常見的一個小動態 GIF 的定義變成:

.sign-icon {
  background: url($imgURL/sign.gif) no-repeat left;
  padding-left: 11px;
}

把 <img> 變成了 background 圖檔(聽說改成這樣還好處多多,圖片可以背景讀入提昇網頁效率),記得加個 padding-left(值就是該圖檔的寬),而實際的使用上從:

<img src="http://abinlee.link.googlepages.com/sign.gif" title="Sign">

變成了:

<span class=sign-icon title="Sign">&#160;</span>

程式碼變的比較短(少了固定圖片網址)、更有彈性(用 CSS 就能調整圖片樣式),還可以得到加速載入(背景讀取)以及套用樣板變數的好處,真是有百利而無一害啊!哪天 Google Sites 又修好、想換圖床的時候,我只要改 imgURL 的變數值,整個樣板模組不大需要變動,很快就可以搞定了!

不過別高興的太早,還有最後一個步驟。剛剛提到我們的各種圖檔都搞成背景讀入,這樣很棒,但在 IE6 又有一個狀況(IE 真是個爛瀏覽器),背景讀入的圖片被預設取消 Cache,也就是說每當你游標移到這些「背景讀入」的 GIF 檔,它又會重新載入、看起來像是閃爍一樣,這個問題要透過 Javascript 去調整 BackgroundImageCache 的預設值。請在 Javascript 的區段加入:

<!-- Used for Fixing IE6 background image cache problem  -->
<script type='text/JavaScript'>
try {
  document.execCommand(&#39;BackgroundImageCache&#39;, false, true);
} catch(e) {}
</script>

這個設定在其他瀏覽器都是正常無害的,專門針對 IE6 開啟。

最後總結一下。本文是利用 Blogger 內建專屬的樣式變數,拿來實現樣板全域變數的需求,接著把所有用到 <img> 標籤的圖檔,統統轉換成 CSS 的樣式控制,以便能使用剛定義的全域變數,達到用一個變數控制所有圖檔網址路徑的目的。最後再修正 IE6 對 CSS 樣式背景圖不 Cache 的問題,這樣就大功告成了!這裡也可以倡導一下好的程式寫作概念,所有宣告都應該模組化以保持彈性,避免 Hard-code 任何可能需要修改的資訊在程式主體裡,才能避免未來找不到或沒改到,未來還可以預留樣式調整的彈性。

回應: 6

 

2008-09-24

Bug Fixed! 文章和回應連結錯亂的問題

我們知道 Blogger 的系統一直有在調整,不管是解 Bug、還是新增功能,原則上都是一直往好的方向前進。不過系統一調整,對 Blogger 的 Hack 來說都是一種挑戰,如果 Hack 的 Code 沒寫好,系統調整完某些功能就會開始不正常。從官方開發者的 Blogger Developer Network 的部落格新聞看來,系統最近調整的重點是 Feed (Atom or RSS) 的資料擴充和標準化,基本上應該是「無痛」的擴充才是,結果調整完我一堆透過 Feed 抓取資料內容當作模組的功能開始不正常,主要的現象是:連結的網址不正確了,跑去查了一下 Code、又翻了 Google Data API 的 Spec,都看不出有什麼異狀,後來 Search 了半天,總算找到原因,而且也找到了解決的辦法。

稍微解釋一下問題點好了(不會寫程式的這段可以跳過)。我有很多 Hack,都是透過 JSON、去抓取 Feed 的資料內容,然後再一個一個 Parse 到我需要的變數裡,串成我要的東西。在「網址連結」(link) 這個資料結構裡,基本上不是只有一個連結,而是一個陣列,之前這個陣列裡都只有一個元素,就是文章的網址,因此我在寫 Code 的時候沒想太多,當我需要文章網址的時候,就直接呼叫 link[0].href。問題來了,這次系統調整完,這個陣列裡變成不只一筆資料啊,而且關鍵是,原來我要的網址資料,被移到陣列的「最後一筆」去了,link[0] 不但不是我要的,這「最後一筆」還不知道會是那一筆哩(Comment 是 link[2] 、Post 是 link[4])~就算我又 Hard-code 特定一筆,根據這篇 Blogger Data API 討論區的這篇文章,負責開發的 RD 還說,「資料順序並不是規格裡有硬性規定的」,所以就算報 Bug 給官方要求修改,一則不知道什麼時候會改回來(或是改成怎樣),二則還不一定會被接受,所以 Hard-code 不是解決問題的辦法,看來還是要想其他方式解決。後來我查了我自己的所有模組,ㄟ?還是有模組不受此調整影響的耶!查了一下內容,喔~雖然「順序」會變,但文章網址的「屬性」不會變啊!從這篇文章提到 Feed 元素的資料結構得知,每個 link 元件有很多個屬性,像是 href、rel 和 type,一般我們取用的都是 href,而文章網址有一個確定屬性,就是 rel = "alternate" (至於為什麼,請查 W3C 的文件)。知道這個條件問題就好解決啦!只要我們找出 link 這個陣列裡如果 rel 的值是 alternate 的話,那筆 link 的 href 就是我們要的啦!

根據以上的發現,要做的調整也很簡單,多三行判斷檢查就行了。原先的程式大多像是這樣(以「最新文章」模組的程式為例):

var link = Post.link[0].href;

我修改後多了檢查,確定是原文網址才可以:

var j = 0;
while (j < Post.link.length && Post.link[j].rel != "alternate")
  j++;
var link = Post.link[j].href;

再舉個例子好了(因為用「最新回應」這個模組的人看來最多),原先程式那一行是這樣的:

var link = Comment.link[0].href;

依照以上的原則修改後,變成:

var j = 0;
while (j < Comment.link.length && Comment.link[j].rel != "alternate")
  j++;
var link = Comment.link[j].href;

最小修改、只多三行(含一個 while 迴圈檢查),這樣就能確定網址是對的啦~即便哪天 Blogger 又調整了 link 這個陣列裡資料的順序,也不會再影響到 Hack 的運作了。

在找問題翻規格的過程中,我發現搞 Hack 的王道,是直接用 Blogger Data API 裡的 Javascript Client Library,只要 include 該 library 的網址,就可以用 Google 自訂的方法來取得 Feed 資料,像是如果要抓標題,不需要什麼 title.$t,直接呼叫 getTitle() 就行啦(函式規格文件在這邊),當然,這個 API 會不會調整也很難講啦,但我想至少 Google 會兼顧這些 API 的正確性吧,閒來無事的話再玩玩看好了~

以下整理這個問題會牽扯到我的相關 Blogger Hack 連結,如果你也有用過以下的玩意,記得順手修正一下,才不會不正常。原文的程式碼我都修改過了,如果看得懂以上原理,建議可以自己動手改改看,而局部修改的原則就是先找「我的原始碼裡」 var j = 0; 這一行開始,把後面幾行蓋掉「你程式碼裡」像是 xxx.link[0].href 的那一行,如果懶得找的就直接重貼一次程式碼也行。

結合發表文章的日曆模組 (Feed Calendar)
「最新回應」模組 (Recent Comments)
首頁即時可展開的留言回應 (Comment Toggling) (這個沒有 Source Code,請自行查看我的網頁原始碼)
最新文章模組「終極版」
最新文章的加強版:其他的最新文章 (Other Recent Posts)
應用 JSON 實現「最新文章」、「最新回應」和「Blog 聯播」

除了以上模組,如果發現有修正無效、引發其他狀況的 Hack,也歡迎回報。

註:像是和「回應」有相關的 Hack,有人刻意拿掉我像是 .replace("#c", "#comment-") 這個部份,之所以這樣寫當然是有原因的!因為 Blogger 的 comment feed 裡的 link 值一直有問題,明明是某個人的回應連結,文章網址後面接的應該是網址加上該人留言的錨點 (就是 # 後面那個 ID),Blogger 的格式是 #comment-ID,可是連結卻總誤植為 #cID,所以我才「好心」把它取代成正確的,如果哪天 Blogger 找到這個 Bug 並且修好的話,才有必要拿掉(當然不改也行,還是會連結到正確文章,只是不會自動跳到留言者的那個留言,要自己翻...)。看來,我應該也去和官方「反應」一下,難道都沒有人發現有問題嗎?

註 2:以上修正 commment- 的問題經 LVCHEN 提醒後發現是舊樣板才有的問題,我調整過了,看來新的樣板都是以 #cID 作為連結,因此「預設」應該是不用去修正,我也把它從程式碼裡拿掉了。

回應: 15

 

2008-08-06

Google Page Creator (Google Pages) 服務即將終止

昨天看到這個消息還有點不能置信,但經過證實,果然是真的,這對很多利用 Blogger 空間來寫 Blog,很多連外檔案(圖檔、Javascript)都放到 Google Pages 空間的人來說衝擊很大啊!像我也是,很多的小圖示連結都是直接用那邊的空間,該服務一旦停用(或服務和網址更改),我版面的小圖示就會變成叉燒包了!還有些網友喜歡在上面擺 Javascript 檔案、作為外掛模組和功能的,這次就要糗了(之前就說過了吧)~當然,Google 並不是完全廢掉該服務,只是將其旗下的兩個服務:Google Page CreatorGoogle Sites 兩相整併,本來這兩個就都是提供使用者創建網頁的服務,後者還是從有名的 Wiki: JotSpot 併購來的,因此存續的服務是 Google Sites(功能架構也比較完整和強大),自然 Google Page Creator 就要終止、把資源都集中到 Google Sites 啦!

根據官方公告,目前 Google Pages 的狀態是「停止新用戶註冊」,原用戶可以繼續使用「一段時間」,完成整併後資源會「自動」匯入 Google Sites 裡面, 我覺得或許能夠「無痛」將兩個服務合成一個,但,有沒有人願意冒險、自己 Blog 有一堆圖片叉燒包、或是 Javascript 都不會動以後,才匆忙地去搬家?比較保險的還是自己手動搬一搬,才能避開服務整合的陣痛期。還好之前我就玩過 Google Sites、就連 Google Apps 裡面的 Sites 服務也略有涉獵,看來圖檔搬家應該是小事,不過我還是有發現幾個問題。首先,Google Sites 還不能完全使用「自訂網域」,因為 Google Sites 開放給個人之前,是要有自訂網域而且申請 Google Apps 的人才能玩的東西,因此 Google Sites 想搞自訂網域,除非你有 Google Apps,只能用 Apps 下面的 Site 及其 Web address mapping 設定才能做(請看此文說明,還好我都有玩),沒有玩 Apps 的人就只能用醜醜的網域和路徑了。其次,想透過 Google Sites 來存放「檔案」,勢必是要在 Site 裡建立一個 File Cabinet「檔案櫃」、並開放公開分享才能存取。檔案櫃裡除了一樣有 100MB 的容量限制,它還會限制檔案類型!最該死的,它不允許你擺 .js 檔(當然,你改副檔名還是可以上啦)!希望以上只是「過渡時期」的限制,在完成整併後以上問題都能改善。

所以各位,不管是從 Google Pages 轉到 Google Sites,或是另起爐灶換地方,還是趕快準備把外引的各個圖檔和連結給搬家吧~

註:目前一般 Google 帳號也支援自訂網域了!另外要注意一點,如果把 Google Sites 當圖床,切記後面那句 ?attredirects=0 千萬別漏了,不然動態 gif 可能不會動、transparent gif 的透明底圖效果也會消失啊!

回應: 29

 

2008-07-24

結合發表文章的日曆模組 (Feed Calendar)

一年多前參考國外某些人作法修改,做了一個 Blogger 用的整合日曆模組,針對發表過的文章,其實 Blogger 有內建一個網頁元素叫做「網誌存檔 Archive」,先將其屬性改成每日存檔,系統就會針對每天的文章做出一個靜態的頁面連結,然後用 Javascript 做出日曆、判斷哪些天有文章、把日前和連結整合在一起,再利用 Javascript 做出一些效果,達到還能在不同月份切換。比起當時很多人用外掛、其他網路服務幫忙,這個方法相對先進多了,速度又快,剛開始我還蠻滿意的,不過其中還是有不少差強人意的地方。首先,它利用了 Blogger 內建的 Daily Archive,所以設定和修改比較麻煩,而且系統限制只能用一個 Archive,所以有了日曆之後功能排擠,沒辦法再有其他網誌存檔的功能。第二個問題是原理,原來的辦法要求抓出「所有」的當日文章、產生靜態連結網址,然後再一個個塞進日曆中,雖然讓做出來的日曆在月份切換時反應很快,但看原始碼會看到所有文章的存檔連結,可想而知一旦時間一長文章量爆多,幾百幾千篇的文章都產生靜態連結放在原始檔裡,那應該很恐怖(所以我另一個 Blog 不敢用這個日曆)。最後,用 Daily Archive 做出來的日曆還有個討厭的問題,即使當天只有單篇文章,它不能直接顯示該文章的標題或永久連結,還是只能幫你連結到 Daily Archive 的頁面,等於要多花一個步驟多點一次連結才能看到日曆上當日的全文,感覺還挺不直覺的。結合以上要求,最好是日曆只即時抓要顯示的當月文章連結、與「網誌存檔」拖勾、日曆上還可以顯示該文章的永久連結和標題(當然如果一天有兩篇以上文章,問題也要解決),還能兼顧效能和美觀,這樣才是個功能性好的日曆。

後來經網友 LVCHEN 的提醒、看到了他做的「日曆文章列表」,哇!原來 Feed 裡新多加能指定時間範圍的參數啊!結合 JSON 的技巧,不就可以輕易地取得特定某個月的當月文章嗎?有了新的資料來源,修改之前的日曆程式,不就可以解決以上的所有問題嗎?知道之後整個寫程式的熱血又來了,花了一兩天測試修改,終於做出這個新的日曆模組。整段程式碼不到 5K(比前個版本更小)、如果搭配前篇文章提到壓縮 Javascript 的技巧,還可以壓到 3K 以下!和之前以網誌存檔當資料來源做出來的 Archive Calendar 原理不同,這次是利用 Feed 來搞,所以就叫它 Feed Calendar 囉!如果你用過之前的程式,記得先手動移除修改的部份、備份完整的原始樣板,然後再開始下面的動作。(修改程式碼還是需要一點程度,也有一定風險,如果你是什麼都不懂的使用者,請直接參考 LVCHEN 的安裝外掛,比較簡單風險又低,效果也差不多~)

第一個步驟,先塞入這個日曆外觀顏色的 CSS 樣式定義。如果你用過之前日曆,那就不用改啦,因為我用了一樣的定義,打開版面配置、修改樣板原始碼的 HTML,放在 <head> 標籤內、定義 CSS 樣版的區段裡:

/* Feed Calendar Styles */
#Calendar {
  margin: 0px;
}
#Calendar .act {
  color: #fff;
  padding: 4px;
}
#CalendarTable table {
  border-collapse: collapse;
  padding: 0px;
  border: 0px;
}
#CalendarTable table th {
  padding: 1px;
  color: #777;
  margin: 0;
}
#CalendarTable table td {
  height: 25px;
  color: #999;
  text-align: center;
  padding: 1px;
  margin: 0;
}
#CalendarTable table td a {
  display: block;
}
#CalendarTable .Today {
  color: #fff;
  background: #777;
}
#CalendarTable .Today a {
  color: #fff;
}
#CalendarTable .Weekend {
  color: #997777;
}

如果對顏色、字型大小、靠左靠右有特別需求的,請自行改上面的樣式。第二個步驟是重點,就是抓 Feed、產生日曆主要的 Javascript 程式碼,一樣是貼在 <head> 標籤後面,如果你之前也有 Hack 放過 Javascript,放在一起就好:

<script type='text/javascript'>
//<![CDATA[
<!-- Script functions for generating Feed Calendar: generateCalendar(), collectPost(), BrowsePrev(), BrowseNext(),  BackToday() -->
var baseURL = '';
var currentDay = new Date();
var today = new Date();
var monthLabels = new Array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12');
var monthDays = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
var weekLabels = new Array('一','二','三','四','五','六','日');

function generateCalendar(){
  var thisYear = currentDay.getFullYear();
  var thisMonth = monthLabels[currentDay.getMonth()];
  var thisDay = today.getDate();
  var nDays = monthDays[currentDay.getMonth()];
  if (currentDay.getMonth() == 1 &&(((thisYear % 4 == 0) && (thisYear % 100 != 0)) || (thisYear % 400 == 0)))
    nDays = 29;
  var IsitNow = currentDay;
  IsitNow.setDate(1);
  var startDay = IsitNow.getDay() - 1;
  if (startDay < 0)
    startDay = 6;
  var sCalendarCode = '<table><tr>';
  for (var index=0;index<7;index++)
    sCalendarCode+='<th style="width:25px;">'+ weekLabels[index]+'</th>';
  sCalendarCode+='</tr>';
  var nTableCol=0;
  for (index=0;index<startDay;index++) {
    if (nTableCol == 0)
      sCalendarCode += '<tr>';
    sCalendarCode+='<td>&nbsp;</td>';
    nTableCol++;
  }
  for (index=1;index<=nDays;index++) {
    if (nTableCol==0)
      sCalendarCode+='<tr>';
    if (index==thisDay && today.getMonth()==currentDay.getMonth() && today.getFullYear()==currentDay.getFullYear())
      sCalendarCode+='<td id="Day'+index+'" class="Today">';
    else {
      if (nTableCol < 5)
        sCalendarCode+='<td id="Day'+index+'">';
      else
        sCalendarCode+='<td id="Day'+index+'" class="Weekend">';
    }
    sCalendarCode+=index;
    sCalendarCode+='</td>';

    if (nTableCol==6) {
      sCalendarCode+='</tr>';
      nTableCol=0;
    }
    else
      nTableCol++;
  }
  if (nTableCol>0) {
    for (index=0;index<(7-nTableCol);index++) {
      sCalendarCode+='<td>&nbsp;</td>';
    }
    sCalendarCode+='</tr>';
  }
  sCalendarCode+='</table>';
  document.getElementById('CalendarTable').innerHTML = sCalendarCode;

  var sFeedURL = baseURL + '/feeds/posts/summary?orderby=published&published-min='+thisYear+'-'+thisMonth+'-01T00:00:00&published-max='+thisYear+'-'+thisMonth+'-31T23:59:59&max-results=50&alt=json-in-script&callback=collectPost';
  var script = document.createElement('script');
  document.getElementById('CalendarCaption').innerHTML = '<span class="loading">Loading <blink>...</blink></span>';
  script.setAttribute('src', sFeedURL);
  script.setAttribute('type', 'text/javascript');
  document.documentElement.firstChild.appendChild(script); 
}

function collectPost(json) {
  document.getElementById('CalendarCaption').innerHTML = currentDay.getFullYear()+'-'+monthLabels[currentDay.getMonth()];
  var entries = json.feed.entry;
  if (entries == undefined)
   return;
  var nDay = 0, nCount = 0, nActual = 0;
  var posts = new Array();
  for (var i = 0, post; post = entries[i]; i++) {
    nDay = parseInt(post.published.$t.substr(8,2),10);
    if (i>0&&nDay==parseInt(entries[i-1].published.$t.substr(8,2),10)) {
      var actualDay = post.published.$t.substr(0,10);
      var actualTimezone = post.published.$t.substr(23,6);;
      posts[nActual-1][1] = posts[nActual-1][1]+', '+post.title.$t;
      posts[nActual-1][2] = baseURL +'/search?updated-min='+actualDay+'T00%3A00%3A00'+encodeURIComponent(actualTimezone)+'&updated-max='+actualDay+'T23%3A59%3A59'+encodeURIComponent(actualTimezone);
    } else {
      posts[nActual] = new Array(3);
      posts[nActual][0] = nDay;
      posts[nActual][1] = post.title.$t;
      var j = 0;
      while (j < post.link.length && post.link[j].rel != "alternate")
        j++;
      posts[nActual][2] = post.link[j].href;
      nActual++;
    }
  }   
  for (i=0;i<nActual;i++) {
    posts[i][1] = posts[i][1].replace('\"', '&#34').replace('\'', '&#39');
    document.getElementById('Day'+posts[i][0]).innerHTML = '<a title="'+posts[i][1]+'" href="'+posts[i][2]+'" target="blank_">'+posts[i][0]+'</a>';
  }
}

function BrowsePrev() {
  var thisMonth = currentDay.getMonth()-1;
  var thisYear = currentDay.getFullYear();
  if (thisMonth<0) {
    thisMonth = 11;
    thisYear = thisYear-1;
  }
  thisMonth = monthLabels[thisMonth];
  currentDay = new Date(thisYear+'/'+thisMonth+'/1 00:01');
  generateCalendar();
}

function BrowseNext() {
  var thisMonth = currentDay.getMonth()+1;
  var thisYear = currentDay.getFullYear();
  if (thisMonth>11) {
    thisMonth = 0;
    thisYear = thisYear+1;
  }
  thisMonth = monthLabels[thisMonth];
  currentDay = new Date(thisYear+'/'+thisMonth+'/1 00:01');
  generateCalendar();
}

function BackToday() {
  currentDay = new Date();
  generateCalendar();
}
//]]>
</script>

好啦!樣板原始碼的修改到此搞定存檔,接下來安排這個新日曆模組的位置。換到「網頁元素」的設定,在你想塞入日曆的地方新增一個網頁元素、選擇 HTML/JavaScript 類型。接下來給個標題,然後貼入以下的 HTML 程式:

<center>
  <table border="0" id="Calendar" cellpadding="0" cellspacing="0">
    <caption>
      <a href="#" onclick="javascript:BrowsePrev();return false;" title="Previous Month">&lt;&lt;</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <a href="#" onclick="javascript:BackToday();return false;" title="Back to Today"> <span id="CalendarCaption"> </span></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <a href="#" onclick="javascript:BrowseNext();return false;" title="Next Month">&gt;&gt;</a>
    </caption>
    <tr>
      <td id="CalendarTable" class="act"> </td>
    </tr>
  </table>
  <script type="text/javascript">
    generateCalendar();
  </script>
</center>

儲存搞定!接下來就可以檢查新的日曆模組能不能動囉~當然,根據以上的原理來說,這個新的日曆模組應該沒啥問題(我自己所有的 Blog 也都裝來用啦),不過如同大部份透過 Feed 抓文章的 Hack 一樣,由於要仰賴 Feed 即時連線抓取資料來源,這點會受到網路影響,所以快速猛點連結時切換的反應會比較慢(這是 Feed 的原罪,沒辦法),所以如果日曆有出現、但該日期沒有文章連結,請先檢查 Feed 的內容是不是正常,如果貼完程式發現怎麼只有兩個箭頭,那就是程式貼錯地方或貼漏了,請自己再仔細檢查一下。如果想要調整日曆裡的 Layout 這類「客製化」的問題(像是一週第一天是禮拜天、週末的顏色或標題調整),請先檢查之前那篇文章的回應斟酌修改(如果看不懂、那還是不要以身試法啦)。

BTW,根據前一篇文章提到的瘦身法,步驟二中的那一狗票 Javascript 用 Compresser 一整個壓縮後再塞回去(建議就壓 //<![CDATA[ 和 //]]> 這兩行中間的那些程式碼,處理完再貼回原來的位置),可以讓程式碼更小更節省讀取的時間,缺點當然是變得不可讀、修改不易,所以建議原始 Javascript 套用過沒問題後,再去進行壓縮瘦身。

回應: 53

 

2008-07-16

優化網站內的 Javascript

時代不一樣了,老早在寫「網頁程式」的時候,瀏覽器端只要 Parse 單純的 HTML(或下載顯示圖片),資料的處理,都由瀏覽器透過 POST/GET 方法和伺服器發出需求,伺服器處理完畢做完回應給瀏覽器才算完成。現在的網站服務大量套用 AJAX 和 Javascript,很多處理在 Client(瀏覽器)端就能先做,就算丟給伺服器處理也可以「非同步」完成,帶來的好處除了伺服器的負擔變輕、瀏覽器這邊也能夠有更快的反應速度(也可以搞一些畫面效果)。但也因為越來越多的 Javascript 被使用,即使是非同步在執行,應該也會發現處理的 Loading 被分擔到使用者端的瀏覽器上,這也是為什麼連到所謂「Web 2.0」相關的服務和網站時,查看自己的 CPU Loading,常常導致滿載的程式都是瀏覽器,使用效能差一點的電腦,連這類網站常常 Lag 到不行。我想每個架站、寫 Blog 或提供網路服務的人,應該都希望使用者或讀者在連線瀏覽時,不會有「連上你的網站要等很久」的感覺,除了網路頻寬外,如何優化網站程式碼也是一個重要的課題。除了增加伺服器端的處理效率(調整硬體或資料庫),交付給使用者執行的 Javascript 程式,或許也是增加效能的關鍵。

檢視使用的程式碼(Code Review)

我想看我 Blog、參考那些 Blogger Hack 的人會發現,首先,我會避免用外部 Include 進樣板範本的 Javascript 檔(像這類語法:<script type="text/javascript" src="xxx.js"></script>),因為一旦你外部引用的檔案掛了,等同於瀏覽器不能執行該 Javascript 的相關功能,利用該 Javascript 去實現操作介面或網頁模組全不能動,那連帶的衝擊有多大可想而知(當然,如果你網頁和引用的 Javascript 可以放在同一台機器上,同生死共患難,那反倒建議儘量用引用的比較好管理)。其次,我很多 Hack 其實也是參考人家的,但,我會「取其精華」、只保留該功能用得到的變數和函式,這樣做主要是簡化程式碼、減少載入時間,另一方面也讓自己能掌握每個函式的到底在幹嘛,能夠進一步優化和改寫(尤其很多特定功能的 Javascript,裡面殘留作者「懶得刪除」的無用程式碼,自己看了很礙眼)。不過由於 Script 的結構容忍度高(鬆散不嚴謹),不像要 Compile 的程式語言在 Build 的時候會做檢查,自己寫的時候也都只做到「能跑就好」,因此很多地方可能效率太差、沒測試到就會發生 Bug。後來看到 Will 的一篇文章:驗證你的 JavaScript 程式:JSLint,該文提到一個線上驗證 Javascript 程式的服務:JSLint,它會用嚴謹的標準去檢驗自己寫 Javascript,像是短少分號啦、變數沒有宣告之類的,小地方可以糾正自己不良的習慣,進而減少 Javascript 在客戶端出錯的機會,認為寫程式是一門「藝術」的人一定要參考看看。

程式壓縮瘦身

如果這個外掛的 .js 是自己能掌握的(就是放在自己的空間),或許可以先「壓縮」,經過「瘦身」以後放上網路,這樣檔案會變小,使用者下載讀取的速度也會變快。參考了這篇文章:「上線前用 JSMin 壓縮你的 JavaScript 檔案」中提到的 JSMin,還有線上的壓縮工具 Packer,以及很多 Javascript Library 愛用的 Javascript Compressor,這些都可以讓你自己外掛或自行撰寫的 Javascript 達到壓縮的效果(減少不必要的字元),檔案變小、程式碼變少自然載入就會變快,這樣也是一種優化的好辦法。

找出瓶頸、調整位置延緩執行

好吧,對不會改寫 Javascript、或迫不得已一定要引用別的外部 .js 檔的人來說,難道沒有優化的辦法嗎?有!在重灌狂人這篇文章提到,有個 Firefox 用的套件:Firebug 可以幫網頁測速度,抓出拖慢網站的元兇,如果自己網站用了一堆外掛 Widget,這是一個可以找出拖累網站瀏覽速度元兇的好辦法。那,找到元兇該怎麼辦呢?最簡單的方法就是不要用了(..XD),要不,就是把該功能儘量移到程式碼下方或後面,至少前面的部份能夠先順利顯示出來(AJAX 的好處)。那,如果該段程式碼就是要在前面、不能改動位置,還有一招,可以在 JavaScript 的宣告標籤裡加上 defer 屬性(一樣是從 Will 的文章,這篇:「不要讓 JavaScript 拉長你網站的反應時間 」學來的),例如:

<script type="text/javascript" src="xxx.js" defer="defer"></script>

不過以上各種優化的招式,請先確定真的知道自己在做什麼(那種什麼程式都不懂的小朋友不要學,叔叔有練過,這有危險性,不知道亂套用只會讓你的網站掛掉),而我檢視自己的 Blog,其實能夠調整的地方並不多(一方面有優化過了,另一方面我不用 include JS 的語法,都用 inline 的 Javascript,無從調整起),不過還有個地方可以玩,就拿來當作範例:我網站裡有裝 Google Analytics、一項 Google 提供用來分析流量的站長工具,沒辦法,它就要我一定要放一段 Code 才能啟用該項功能,所以套用以上提到的 defer 屬性,變成這樣:

<!-- Used for Google Analytics  -->
<script src='http://www.google-analytics.com/urchin.js' type='text/javascript' defer="defer"/>

放的位置不變,照理來說該 Script 會比較慢執行才是。接下來是 Google Analytics 會呼叫的函式 (就是有呼叫 urchinTracker 函式的那一段),本來人家是要擺在 <body> 的後面,我給它搬到 </body> 的前面,這樣就算是「最後執行」了。這個 Google Analytics 的範例用了幾個優化網站瀏覽的技巧,而且(應該)不會影響到顯示和相關功能,雖然我認為自己 Blog 的顯示效率已經一些搞一堆外掛的 Blog 好一些,但有時候還是會卡卡的不大滿意,看來有機會應該對整個網站程式碼做總體檢,用以上的工具和原則檢視一番,對網頁瀏覽時的效能說不定更進一步的明顯提昇。

回應: 10

 

2008-06-30

消失的圖示:快速編輯 (quick-edit) & 電子郵件發佈連結 (email-post)

之前就有發現,莫名其妙在 Post 尾巴 Blogger 內建的「快速編輯」和「電子郵件發佈連結」的圖示消失了,一直沒去追究為什麼、也沒去修正,直到這兩天心血來潮才去看看,到底發生了什麼事。這兩個圖示主要的功能,「快速編輯」是在文章後面多一個「鉛筆」的圖示,在管理帳號「登入」的狀態下,可以點圖示直接編輯該篇文章;「電子郵件發佈連結」則是允許讀者將正在閱讀文章的連結、透過點擊按鈕方便寄送到自己或朋友的電子郵件信箱。這兩個基本上都是 Blogger 原來就有的功能,不用 Hack,只要透過管理介面開啟就行了(版面配置 -> 網頁元素 -> 選擇「網頁文章」模組右下的「編輯」):

原則上一旦有設定,每篇文章的文末 (Post-footer) 區段裡,就應該要有這兩個功能圖示的樣式程式碼,類似這樣:(要「展開小裝置範本」才看得到,以下第一段是「快速編輯」、第二段是「電子郵件發佈連結」)

<!-- quickedit pencil -->
<b:includable id='postQuickEdit' var='post'>
  <b:if cond='data:post.editUrl'>
    <span expr:class='&quot;item-control &quot; + data:post.adminClass'>
      <a expr:href='data:post.editUrl' expr:title='data:top.editPostMsg'>
        <span class='quick-edit-icon'>&#160;</span>
      </a>
    </span>
  </b:if>
</b:includable>

<!-- email post links -->
<b:if cond='data:post.emailPostUrl'>
  <span class='item-action'>
  <a expr:href='data:post.emailPostUrl' title='Email Post'>
    <span class='email-post-icon'>&#160;</span>
  </a>
  </span>
</b:if>

功能正常的話,文末應該會有像這樣的圖示:

不管做過什麼 Hack,我都儘量避免去碰這類原生 Blogger 的樣式原始碼,但,不知道什麼時候開始,這兩個功能的圖示和連結就不見了!仔細去看網頁的原始程式碼,又簡單地 Debug 一下,發現連結還在,消失的只有「圖示」!怪了,那個圖示是 Blogger 系統內建的,沒有「不見了」的道理啊!而且神奇的是,有些人的 Blog 功能正常圖示也在、原始碼也一樣,為什麼就我或部份人的沒有圖示?用 Google 找了一下、找到 Blogger 的官方討論區,發現有這個狀況的人還真不在少數,而且「災情」持續至今也半年多了耶!大家七嘴八舌得到的結論,比較可信的該問題癥結還是在「Blogger/Google」上面。應該是 Blogger 採用 Cloud 分散式運算和儲存架構,但不知怎麼的,「部份」Blogger 的伺服器裡「剛好」就沒有那兩個圖示檔,導致這兩個在官方預設 CSS 樣板文件裡沒定義到、直接使用相對路徑連結的圖檔,就這麼地無法顯示,功能不是消失,只是負責連結這項功能的圖檔連結不到所致。

了解原因之後,解決方法也很簡單:補個圖給它不就好了!我拜讀了 iPlay99這篇文章、知道了原來那兩個消失的圖示的官方連結位置,不過該作者與網路上很多解決該問題的辦法一樣,將上面樣板裡定義圖示的 span 標籤「覆寫」成該圖檔的固定連結 (<img src=""/>),非常直覺的方法,但還要搜尋和改動版面原始碼、而且還 Hard-code 了圖檔連結在樣板裡!我想了自己的解決辦法,既然「預設的 CSS」連結不到圖檔,這還有現成的 span class 類別 ('quick-edit-icon', 'email-post-icon'),直接覆蓋繼承該 CSS 定義不就搞定了?所以我的辦法是:不用搜尋樣板原始碼裡、顯示這兩個圖示的程式在哪裡,只要在最前面、給定自己對這兩個 class 的 CSS 定義,並把圖檔連結改到官方圖檔連結位置就好了!(用 CSS 修正這類問題才是王道啊~)

以上是原理,看不懂也沒關係,如果一樣那兩個功能有開啟但圖示卻消失的問題,只要打開樣板原始碼、在定義 CSS 樣式的區段裡面 (<b:skin> </b:skin> ),直接插入以下幾行,就能覆蓋官方的 CSS 圖檔連結、解決這個問題了:

.email-post-icon {
background: url("http://www.blogger.com/img/icon18_email.gif") no-repeat left;
padding: 9px;
}
.quick-edit-icon {
background: url("http://www.blogger.com/img/icon18_edit_allbkg.gif") no-repeat left;
padding: 9px;
}

當然,如果有自己喜歡的圖示,可以把以上的圖檔連結換成自己的,更能一勞永逸解決問題。

回應: 11

 

2008-06-05

無題

想半天不知道該給這篇文章下什麼標題,因為內容雜亂無章,所以只好用「無題」了。一早收到王姓友人的提醒,沒想到這邊這個小小的 Blog,竟然在「部落格觀察」分屬的「宅度計」評量上,「榮獲」IT 類排行榜前十名的第二名(當然,就那一開始而已,現在沒了)!為避免流於嘴砲或以資紀念,先貼個擷圖真相在下面:

這不是閃光文,因為也沒啥好高興的(又沒獎牌、獎狀、獎金,哈),該指標的「公信力」見仁見智,因為我覺得更多「當之無愧」的 Blog 竟然沒上榜(或在我後面),而且,被冠上一個 IT 類「宅度」很高稱號的部落格,也不知道應該是要很開心還是難過...嗯,以上不是重點,我的 Blog 的確有不少「讀者」,會參考這邊的文章和筆記,不管是拿來改機或是改 Blog,自己也很高興亂寫的東西能夠給人家一點幫助。不過所謂樹大招風,這陣子以來一直有些讓我困擾的事情,所以利用這篇文章一併紀錄,要說是「公告」也行啦!以後遇到類似狀況,我就不用再寫 Mail、回應留言或是出陣撻伐了,直接 Redirect 到這篇文章、請人家自己看,不要浪費自己的口水了。

首先開宗明義,我開設 Blog 的原因,不是為了「衝人氣」(所以亂入的人看看就算了,不一定要留言、搶沙發、或推什麼鬼的),也不是發善心要「教學」(所以有些初學者看不懂、還有會批評我應該怎麼寫文章的,我沒有義務一定要「教會」你們、或是依照人家的「需求」寫東西),出發點很簡單、和我 Blog 的標題一樣,單純是我個人的「筆記」,主要能夠滿足我一個人就行了。紀錄的好處,就是可以把自己做過的事情化為實體文字,記性不好或又要用的時候,有地方可以查詢、剪貼或連結得到。因此和很多 Blog 不同的,我不喜歡寫些有時效性或像新聞單純介紹的文章(像什麼軟體又出新版、誰推出什麼東西服務自己沒去用過也能發表文章)、不喜歡寫容易引起爭議的東西(政治文),相反地,很多文章像是可以回味的日記、或可以反覆參考,我不大會管對大家有沒有用,重點是我覺得有意義、自己看得爽、寫得爽就行了。

正因我 Blog 是個人公開的「筆記」,行有餘力能幫到別人,那就是額外的 Bonus 了。不過很多人「反客為主」,文章看不懂、不會用,就會有一些奇怪的留言,甚至有「需求」,我如果無聊或行有餘力,可能會看一下回一下,會造成負擔或不快的,我根本不會回應。這就像我看了書、上了課寫了一點筆記和讀書心得,有人借去看、看完還說看不懂、指正你應該寫什麼、甚至直接問能不能幫他寫作業應付考試一樣。喂!有得看就不錯了(而且所謂「寫的好」的筆記,最好人家都看不懂、只有自己看得懂),討論可以,但看完應該是沒資格提要求或批評吧(除非我真的寫錯了,歡迎糾正)!所以呢,針對過去、最近和未來還會繼續發生的狀況,在這篇文章就一次講完,我懶得回就可以給這篇連結做解釋了。

1. 文章轉載的問題。基本上我的 Blog 下面是有註明 SOME RIGHTS RESERVED,也就是作者保有該文章的「著作權」,但願意公開分享散佈,所以如果覺得文章有用,歡迎來「引用」(就是提供本文連結,我自己也常引用人家文章),如果想要轉載、轉貼、轉 PO,在非商業營利、還有不亂改的前提下也允許,但一定要加註來源,當然能知會我更好。很多人轉用文章、不加註就發表的話會像是自己寫的(有的還會故意改得像自己寫的),對作者真的很不尊重。雖然人家不一定吹得長你、拉得破你,但惡性循環之下,公開的好文章和資源就會越來越少了(我就知道對岸有些好 Blog 最後只開放給特定人士看,就是為了避免被抄襲亂轉貼)。

2. 疑問解答的問題。我最常碰到的留言是:「參考你的文章、但是不會 Work」、「你的命令我下了有錯誤」這類,上面說了,這是我的「筆記」,在我的環境、我的機器上能跑,我當然就覺得沒問題。而且,看了文章也是有很多人完成啊,會失敗的話問題不一定在文章裡面。發生狀況首先應該自省的是,是不是自己的環境不一樣、自己那個步驟沒看懂、哪個步驟錯了漏了,有人看都沒看,就跑來說「不行」,這種留言我要回也不知從何回起。最近特別多的,直接把其樣板 XML 寄給我、請我幫他改好再寄還給他,甚至曾經有人砸銀彈、想說付點錢把機器給我,改機好整個搞定再給他!拜託,我一分鐘幾十萬上下,沒閒工夫也沒有義務幫忙啊,而且很多狀況要在自己的空間才能 Debug,光改樣板問題就都能解決的話,那幹嘛要一個模組一個模組地 Hack 啊~要改就自己來,還有我的經驗分享可以參考,不自己動手耕耘解決,那我也愛莫能助。

3. 完全沒基礎的勿入。我愛用 Hack 當標籤,所謂的 Hack 就是利用技術和相關知識,去修改或調整成自己要的東西。很多讀者發問前會先來一句:「我是新手」,更厲害的還會講「我什麼都不懂」、「我不會寫程式」、「我 Linux 不熟」之類的,很抱歉,就跟你說這個 Blog 被「宅度計」標記成 IT 「程式設計」類,很多文章我也講「有風險」、「需基本知識」,不懂任何 Linux 命令是要怎麼改機啊、一點都不懂 CSS、HTML 要怎麼改樣板啊?本 Blog 不是讓人家「入門」用的,要先自己學習有些基礎才行,網路上還有很多教學文可以給初學者上手,有基本能力才看得懂我寫什麼啦。

4. 和我伸檔要東西的免談。有人改無可改,想乾脆直接和我要樣版檔案去改(直接看網頁原始碼也可以參考啊,幹嘛和我要樣板),改機的部份,還有想直接拿改好的 Image 去「Ghost」回去哩!要知道,樣板點點滴滴都是心血,也能想像有人拿到檔案打算「直接用」再來改,但環境不同、不一定能直接用,偷懶的下場可能會導致更多的問題和狀況!改機的映像檔也是如此,裡面還可能會有「漏洞」、「後門」或是密碼隱私,你敢要我也不敢給啊!(不知死的等我種個木馬再把系統檔給你)

以上,說是公告,其實比較像是「抱怨」,因為常常不勝其擾,也曾一度想把 E-Mail 藏起來、把留言回應功能關閉。但網路本來就是公開自由的,如果是舉手之勞,說不定能幫到不少人(積極認真的人還是大多數啦),所以才一直沒這樣做。其實大部分的留言我都會回(除了那種單純「感謝」的留言,「害羞」的我愧不敢當、不一一回應),如果提問遲遲收不到回應(或回別人的獨漏你的),那可能是你的問題是屬於上面四大類,我不知道也無從回答起。所以請各位「親愛的讀者」在發問前,除了該有的禮貌尊重、該守的規則之外,也要注意自己的意見是否合理,不要一副理所當然,那樣的話人家不理你也會是理所當然的。

回應: 14

 

2008-05-08

讓自己的 Blog 網址變成 OpenID

話說剛寫了篇 OpenID 介紹的文章(前言請看這一篇),有提到 OpenID 是個「網址」,背後其實是「伺服器對伺服器」之間的相互驗證機制,為「支援 OpenID 登入」的網路服務和「提供 OpenID 註冊識別」服務兩造伺服器之間的溝通。很多人自己架設網站、或申請了 Blogger、Wordpress 等很有彈性的 Blog 系統,也都搞了個屬於自己的網域或網址,既然 OpenID 是網址格式、又有類似網路名片的應用,那,想要用 OpenID 登入別人服務的時候,能不能直接使用自己的網域、或 Blog 的網址,那不是更好記、更方便嗎?沒問題!大多提供註冊的 OpenID 服務商都有支援類似「轉址」的服務(專有名詞是 Delegation、「委任」),只要在網站或 Blog 首頁頁面檔裡加入一段程式碼,註明自己的 OpenID、服務商伺服器和相關資訊,當你想要登入支援 OpenID 的服務時,就敲自己的網域或 Blog 網址,該服務商會透過你的網址、解析那段檔頭程式去瞭解你的 OpenID 服務商,逕自完成兩造伺服器之間相互識別的機制!能夠拿自己的網址當 ID,這個 Idea 實在是太美妙了啊(中間會跳出服務商要求輸入你 OpenID 密碼的頁面確認是你本人,也可以透過保留 cookie 的方式來簡化步驟)~

這裡介紹「兩種」讓你的網址變成 OpenID 的方法。第一種比較通用,如果你已經擁有一個 OpenID,而你的網站和 Blog 也允許你去「編輯」首頁的檔頭(或是模板),只要在原始碼裡加上一段說明「委任」關係的程式碼,你的網址就可以透過該委任關係變成一個 OpenID。這段說明委任關係的程式碼會因為你 OpenID 供應商的不同而不大一樣,我以修改 Blogger 的模板、並用MyOpenID 這個 OpenID 的供應商當範例,實際狀況請依照個人使用環境的不同去調整。

首先進到版面配置、修改 HTML 原始碼,一樣在 <head> </head> 兩個標籤的區段內填入 MyOpenID 要求的委任識別碼(不同服務商內容請參閱其官方文件調整):

<link rel="openid.server" href="http://www.myopenid.com/server" />
<link rel="openid.delegate" href="http://帳號.myopenid.com/" />
<meta http-equiv="X-XRDS-Location" content="http://帳號.myopenid.com/xrds" />

這樣存檔後就搞定了(真簡單)!以後,當你要用自己的網址去登入 OpenID 時,就會跳出 MyOpenID 要求你輸入密碼、並且允許該網站來識別的詢問,確認(或紀錄密碼)後就能完成登入了。

第二種方法,如果你是 Blogger 的使用者、而且還沒申請 OpenID ,那就不用再另外註冊啦!因為 Blogger 提供給你的 Blog 網址(或你用自訂網域),其實就內建 OpenID 的功能!因為從另一個角度來看,個人的 Blog、不就是一種「個人識別」的工具嗎?所以拿 Blog 網址直接當 OpenID 識別網址,這樣只是剛好而已啦!我找了一下相關的說明文件,官方只找到這一篇,說什麼要在 Blogger in Draft 才能啟用,還要手動修改樣板、貼程式碼之類的。不過實際上,該設定好像已經偷偷變成正式版、而且嵌入現有的管理介面了。請到「設定」->「OpenID」的頁面看一下,應該會看到你的 Blog 網址「已經」是 OpenID URL !所以,當你在支援 OpenID 需要「識別」才能使用的網站,直接敲自己 Blogger 的 Blog 網址當做「帳號」,就可以登入囉!(當然,如果你硬要用其他供應商的 OpenID 來識別也 OK,套用以上說的第一種方法照樣行得通!)

其實,第二個方法之所以能運作,看 Blog 的原始碼就知道原因了。因為有一行程式(Blogger 自己服務的 Delegation)被系統預設自動加入,所以使用者才不需要用去手動修改樣板。該行如下,可以檢查看看自己 Blog 有沒有這一行(應該是只能用在 Blogger 吧):

<link rel="openid.server" href="http://www.blogger.com/openid-server.g" />

回應: 3

 

2008-02-29

Blogger 預約發文功能 (Scheduled Posts)

從我整理的 Hack 和種種應用看來,我認為 Blogger 已經是我用過最棒、最完整而且自由度最大的 Blog 系統,硬要我從裡面找出想要內建的新功能,大概只剩下「預約發文」了吧。所謂預約發文,就是你可以寫好一篇文章,指定在「未來某個時間」自動公諸於世,如果你有定期或指定時間發文的需求和壓力、手邊又有多篇文章想定期定量的發表(因為稿擠要分散流量),這個功能對個人出版、和把 Blog 當作新聞台的人就相當的重要。你不用時間一到就要上網去貼文、也不用事後再去修改發表時間(Microsoft Live Space 還不允許你線上改哩),事先寫好文章排定時間、時間一到就會自動乖乖地發表公佈了。

我很早就想要達到類似的目的,當時想到的方法和 Jas9 想到的一樣:透過定時寄發 Email、和 Blogger 提供用 Email 發表文章的功能兩相結合就能辦到。但,這實在太麻煩了啦!又因為 Blogger 可以自訂文章發表時間,我也測試過如果把發表時間設定在未來,該發表文章會怎樣,結果照樣馬上發佈,並不是我想像的有預約到。後來某天看到我訂閱的 Blogger Draft Blog 宣佈,預約發文的功能已經在 Blogger Draft 開始測試囉(名曰:Scheduled Post,我已經開用測試兩周了)!而且用法和我「夢想」的一樣、只要修改發文時間於「未來」,那就是預約發文啦!該文未發表前的狀態,不是草稿也不是發佈,而是「已排定」,完全就是我要的樣子啊!

不過一開始我有點失望,這新功能只能在 Blogger Draft 上用嗎?我又沒有在 Draft 上的 Blog 啊~但當我登入 Blogger Draft 的介面後,看到的是我「所有」的 Blog 空間,那是不是所有已經存在、不是專門建立在 Blogger Draft 上的 Blog,一樣可以套用 Draft 的新功能哩?是滴!只要你用 Draft 介面登入、套用 Draft 的模組和功能到已經存在的 Blog,Beta 新功能照樣可以套用在舊空間!經過一段時間的實測,果然 OK,實在太讚了啊!我到此時也才發現,原來 Blogger Draft 和以前的 Blogger Beta 完全不同,Draft 會不定期推出特定 Beta 的服務模組和功能,Draft 單指「功能」、而不是整個 Blog,所以新功能一公佈,你高興的話隨時都可以套用在正在使用的 Blog 上,這樣也才夠 Friendly 啦~

最後,仿照別的 Blog 系統,我也在我的版面上加了兩個登入系統管理介面的「入口」,像下圖所示:

其實只要有登入過、cookie 還有效的狀況下,點入連結就會導入 Blogger 的管理介面,而這兩個圖示分別讓「管理者」登入不同入口:橘色是帶你去「正式版」Blogger 的管理介面(http://www.blogger.com/home),藍色則是登入 Blogger Draft 的(http://draft.blogger.com/home),登入後只有佈景顏色不一樣,而在 Draft 裡才能使用和設定新功能。像我如果想做版面變更、管理文章留言和裝新 Hack,就登入正式版,像我想玩新功能或是預約發文,登入 Draft 把要發表的文章發文時間設定到我指定的時間,現有的 Blog 就可以享受預約發文的好處啦~整理介紹這個小技巧和新功能用法,提醒並分享給各位愛用 Blogger 的朋友們~(本文也是用預約發文發表滴~)有興趣的話,也可以多關注 Blogger 和 Blogger Draft 的官方 Blog,或像我一樣直接訂閱新消息,常常都會有新的驚喜喔~

備註補充:我發現有一些 Hack 在 Draft 登入的狀況下會失效,使用前要先測試一下喔!修正方法,只要用正式版登入、再發佈一次文章就好了。
補充二:我碰到的「失效」問題原來是 Draft 自己的 Bug,現在也修正了,請放心使用~

更新:此功能目前已經變成正式版的了。

回應: 9

 

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 好像能做到,但我還是偏好簡單、有效率又獨立安全的辦法。如果有找到,本文會再改個第四版「最後終極版」好了,哈哈~

回應: 87