2007/01/17

比程式語言還難的 Blogger Layout GML

竟然有這麼高深的樣版語言,我想只要會了這個,其他樣版語言就跟小蝦米一樣了吧XD
這個樣版語言就是脫離 Beta 的 Blogger 採用的新版面配置語言。
而 GML (Google Markup Language !?) 是 Google 自己寫在定義上面的字眼,所以這裡這麼稱呼它。
(xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr')
同理,舊有的傳統範本這裡稱之為 Template。

跟傳統 Template 不同的 GML,首先第一印象是極度的模組化,可自訂性大增,
甚至排版只要拖拉放就可以了,對於新進的使用者來說吸引力大增。
降低了修改門檻,但是真的是這樣嗎?
如果你鼓起勇氣,到範本→修改HTML,並勾選「展開小裝置範本」,看你還敢不敢這樣說XD

OK,我們知道這個 GML 實在是非~常複雜,不過不可懷疑的是它加強了不少東西,
比方說引入了判斷式和 Loop 迴圈語法,這使得資料可以自由處理,每一筆資料有可能是陣列,
而你只取有需要的來輸出即可,這在之前的 Template 是做不到的。
(但是我覺得 Template 也可以引入這些標籤,只不過這樣 GML 做出來幹嘛?)

首先我們需要了解 GML 的模組特性,以下說明:

1. 所有標記的屬性請用單引號 ' 包住,如果不屬於 GML 的標記 (例如 span) 當然可以用雙引號
2. 各種標記的屬性如果要插入資料,請用 expr: 敘述,底下會說明 (這邊很容易錯!)
3. 適當的空白 (或者說必須的空白) 要加上 (一樣很容易錯)

‧b:skin 字型和顏色 CSS (說明)

這是首先會碰到的第一個區塊設定。
主要就是設定 CSS 樣式表而已,只不過不是用 <style> 包裹而是用 <b:skin>。
當然後續解讀之後一樣會是 <style>。

與傳統樣式表不同的地方是 b:skin 支援變數設定,可以在前面先定義幾個變數 (如顏色、字型)
再在 CSS 內套入此變數,而且 Blogger 有後台可以方便的修改這些設定,很不錯。
我覺得它不支援數字屬性有點可惜,這樣還得手動更改。

先看範例:
<b:skin><![CDATA[
/* 色彩定義
<Variable name="bgColor" description="背景顏色" type="color" default="#cccccc">
<Variable name="txtColor" description="文字顏色" type="color" default="#000000">
*/

body { background: $bgColor; color: $txtColor; }
]]></b:skin>


其中<![CDATA[ ]]> 括號可加可不加,但我的建議是加,因為 CSS 符號很多,有可能違反了 XML 而不自覺。
另外變數設定標籤需要包在註解 /* */ 內,因為到時候解析時它是會印出來的。
如果不讓它在註解裡則會使 CSS 解析失敗。

<Variable>有幾個屬性 (全部必要):
  • name: 變數名稱 (爾後 CSS 可以以 $變數名稱 來取得值)
  • description: 此變數的說明。會出現在後台「字型與顏色」設定方便辨別
  • type: 變數類別。目前只有 color 和 font (顏色 / 字型)
  • default: 預設值。修改不滿意隨時可以回覆成範本預設值。

切記 Variable 的屬性性質要用雙引號! (好像在打架,一下雙一下單的) 使用單引號馬上報錯給你看。

另外<Variable ...>不需要加 / 使成為 <Variable ... />,
因為 Blogger 會把設定屬性加在 / 後面變成 <Variable ... / value="..."> 會很奇怪。

而剛才稍微提過,設定好的變數要套在 CSS 只需要類似 PHP 的變數般填入即可。
範例很清楚,請自行參看。

‧b:section 網頁元素 (說明)

這個網頁元素是一個區塊,用以放上其他小裝置 (Widget) 的劃分位置。
利用這個選擇區你可以先定好左右兩欄式設計或三欄式設計,藉以先訂出版面基本的配置。

範例:
<b:section id='contents' class='color-red' showaddelement='yes'>
</b:section>


被解析之後會成為一個 <div> 區塊,其中 id 屬性是必須且唯一的,class 屬性隨意。

  • id: (必要) 獨一無二的名稱,限使用字母和數字和一些符號
  • class: 指定套用 CSS 樣式。如果使用特定關鍵字 (navbar, header, main, sidebar和footer) 可以讓 GML 辨別其適當處理方式
  • maxwidgets: 此網頁元素區塊所能放置的小裝置 (Widget) 個數
  • showaddelement: 是否可以在其上再新增一個新的網頁元素,預設 yes (yes, no)
  • growth: (選用) 可以是 horizontal 或 vertical,vertical 為預設值。此屬性可判別此區段中的小裝置是並排還是堆疊。


‧b:widget 小裝置 (說明1, 2)

這個小裝置就是模組化的核心,簡單來說就類似桌面美化的各種組件,
可以在網頁元素區塊加入各種小裝置以達成其功能。
記住 b:widget 內不能使用 <!-- --> 註解標記,會回報 #comment 錯誤。

範例:
<b:widget type='Header' id='Header1' locked='true' title='Blog Title (Header)'>
</b:widget>


被解析之後一樣會成為一個 <div> 區塊。其中 id 屬性是必須且唯一的,type 屬性說明此小裝置的用途。

  • id: (必要) 獨一無二的名稱,限使用字母和數字和一些符號
  • type: (必要) 說明此小裝置的用途,至於是哪些請參考說明1
  • locked: 是否鎖定無法搬移或刪除,預設 no (yes, no)
  • title: 說明此小裝置的文字,例如 網誌本體 一類的
  • pageType: 設定此小裝置在何種頁面下顯示 (all、archive、main 或item)


‧b:includable 可納入項目 (說明)

正常來說你的小裝置只有設定其基本屬性,至於他如何運作你還沒有寫,
這時候就要使用多個小型的可納入項目 (或換個說法:副程式) 來分析資料排版等等。

一個小裝置至少得有一個 <b:includable id='main'></b:includable>。
叫用其他可納入項目的做法是使用 <b:include name='Subincludable' data='i' />。
可納入項目內可以使用 <!-- --> 註解標記,也建議使用以分辨各項目用途。

範例:
<b:includable id='main'>
</b:includable>


被解析並不會產生什麼,而是依照此可納入項目內的設計印出東西。

  • id: (必要) 獨一無二值,標明可納入項目的名稱,可讓 <b:include> 呼叫用
  • var: 傳入變數,想成副程式接收的變數即可


上面僅寫出 main 的做法,如果是比較複雜的例子是這樣的:
<b:includable id='main'>
<!-- 印出所有討論串 -->
<b:loop values='data:posts' var='post'>
<b:include name='thread' data='post' />
</b:loop>
</b:includable>

<b:includable id='thread' var='post'>
<!-- 印出單一討論串 -->
...(略)
<b:include name='comment' data='post' />
</b:includable>

<b:includable id='comment' var='post'>
<!-- 印出討論串所有回應 -->
<b:loop values='data:post.comments' var='comment'>
<div>
<data:comment.body />
</div>
</b:loop>
</b:includable>


範例中有三個可納入項目,有 main (b:widget 自動引入), thread, comment。
當 main 需要用到 thread 時,使用 <b:include name='thread' data='post' /> 呼叫。
其他同理。

‧b:include 納入項目

上面介紹過,這是類似呼叫可納入項目的 Call Function。

範例:
<b:include name='PrintName' data='i' />


  • name: (必要) 呼叫之可納入項目的名稱
  • data: 傳入變數,想成讓副程式接收的變數即可


上面複雜範例就操作了 posts (本文陣列) 這個變數,利用 b:loop 循序拆解出 post (類似程式語言的 For each)
然後把這個 post 傳給了 thread 去處理。
而 thread 又呼叫 comment,並 b:loop 拆解 post.comments (本文回應陣列) 出 comment。
最後以 comment.body 取出值來。這不就是一個另類的程式語言了嗎?

‧data: 資料 (說明)

這個標記應該淺顯易懂,顧名思義取出特定資料以供排版之用。
注意到資料有其小裝置適用範圍,例如說現在位於 Header 類型的小裝置,就不能使用 Blog 類型的資料。
算是有點嚴苛。

範例:
<data:blog.title />
<div expr:id='"TMP_" + data:post.id'></div>


如果你想知道所有 data: 的詳細列表,說明內有一份,條列分明。

‧b:if 判斷式 / b:loop 迴圈 (說明)

因為屬於決策類的就放在一起說明了。

- b:if

<b:if cond='data:blog.pageType == "item"'>
條件成立
<b:else />
條件不成立
</b:if>


b:if 只有一個屬性 cond,即是 b:if 的判斷式,注意到請用單引號括起來,上面的模組特性有提過。
至於遇到一般文字請用雙引號括住,假如一般文字包含 " 雙引號則請用脫逸字元 \"。
例如 "<div id=\"007\">" 這樣,此特性同樣適用於 expr: ,容後介紹。
操作子的話則類似 Java / C (==, !=, >, < 一類)。

<b:else /> 當然可以省略。

至於模組特性第三項所描述的「適當的空白」,則是每個操作子之間都用空白隔開。
例如上面的 data:blog.pageType == "item" , == 跟左右都有留空白。
注意這是強制性的空白,在 cond 可能感覺不出來,在 expr: 之中如果在屬性旁邊沒這樣做,
很容易一片空白在那邊而你不知道發生什麼事情。
底下先給個錯誤的範例:
<div expr:id='"Comment_"+data:post.id+"_TEST"'>


乍看之下好像正確嘛,用 + 號把一般文字 "" 跟 data 結合在一起成為新的文字啊?
錯,因為 + 號旁邊沒有適當(必須)的空白,這樣絕對是一片空白。
正確的形式為:
<div expr:id='"Comment_" + data:post.id + "_TEST"'>


- b:loop

<b:loop values='data:post.comments' var='comment'>
ID: <data:comment.id />
</b:loop>


基本上這邊的 loop 不能指定次數,倒覺得有像 for each。
有兩個屬性:

  • values: (必要) 某個變數陣列
  • var: (必要) 某個變數陣列的其中之一內容


上述的範例中,data:post.comments 是某文章的所有回應陣列。利用 b:loop 逐個拆解出來並用 comment 暫存。
這樣下面便可以用 comment.id 取出各回應的 ID。建議這邊弄不懂去翻翻程式語言有關於 for each 的語法。

‧expr: 表達式 / 敘述 (暫無說明)

這是一個很特別的東西,該說是 GML 的精華吧。
所有網頁元件的屬性如果需要動態改變 / 插入資料 (如<a href="本文章永久連結">),
不是<a href="<data:post.url />"> (違反 XML),而是在 href 前面加上 expr: 使成 expr:href。
這樣便成為了一個表達式。可以將資料插入屬性或做改變。
跟 b:if 的 cond 一樣,該注意的單引號包圍、雙引號包一般文字、" -> \" 皆須留意。
最最最重要的則是方才提過的「必要的空白」。

以下為正確範例:
<a expr:href='data:post.url'>XXX</a>
<div expr:id='"Comment_" + data:post.id'></div>
<a href="#" expr:onclick='"javascript:OpenNewWindow(\"" + data:blog.url + "\", null, \"" + data:blog.title + "\");"'>XXX</a>

最後一個範例無比複雜,最易出錯,需多加留意。
尤其是單雙引號加上脫逸字元糾結在一起,很難發現自己出了錯。


最後談點結構,基本上 b:section, b:widget, b:includable 是成巢狀排列:
<b:section id='header2' maxwidgets='4' showaddelement='yes'>
<b:widget type='Header' id='Header1' locked='true' title='Blog Title (Header)'>
<b:includable id='main'>
<h1><data:title /></h1>
<hr class="top" />
</b:includable>
</b:widget>
</b:section>


但是一個 Widget 如果沒有設為 locked='true',是可以自由搬移至任意的 <b:section>
所以說 b:section 的版面配置相當重要,需要搭配 CSS 來排版。以 id 和 class 屬性套用特定 CSS,
作出三欄式 / 兩欄式變化,當然 Header / Footer 也可以如此設計。

而在 <b:section> 之外的東西全部都是固定的,例如說一些表單什麼的就可以放這。
可以放點諸如版權宣告的文字在此,在後台的網頁元素可以看到迷你型的版面配置。

2 則留言:

  1. 讓我想記逍遙的那篇了...
    http://limouren.blogspot.com/2006/11/blogger-beta.html
    這真是他媽的鬼哭神嚎啊 XDrz

    回覆刪除
  2. >>RT
    昨天我心血來潮要來碰這個 GML,奮戰了5個小時以上,
    除錯除到想睡覺 zZzZ
    這一篇已經把個人的認知打出來了,順便加上易錯提示。
    希望對後來人有點功用。 (很少人自己改GML吧?)

    鬼哭神嚎...如果舊有的 Template 也有 loop, if, expr 該有多好。
    目前主要的 Blog 還是在用 Template,但用 GML 的實驗 Blog 則也在製作中。
    哈哈...當然不是玩雙 Blog 就是了。

    回覆刪除