2013年5月6日 星期一

jQuery 套件 DataTables 的測試

ExtJS 框架以其起家的 DataGrid 元件聞名, 而 jQuery 框架雖然沒有內建類似 DataGrid 的功能, 但也有 dataTables 套件可用. 其實這是產品設計策略的差異, ExtJS 是一次到位, 提供全方位解決方案 (但是比較龐大, 學習曲線陡峭); 而 jQuery 則是保持一個精簡快捷的核心, 應用功能則盡量用 plugin 來實現, 所以是百花齊放. 以下先就 dataTables 這個套件的使用方式作簡要說明後, 再進行功能測試.

dataTables 是蘇格蘭 SpryMedia 工作室的 Mr.Allan Jardine 之作品, 可以結合 jQuery UI 的主題佈景, 在網頁上為資料表格呈現豐富的功能, 例如搜尋, 排序, 以及透過 Ajax 動態擷取表格資料, 重點是~~免費 (好用的話捐點小錢, 鼓勵 Allan 一下吧).
下載 dataTables 請連至下列網址, 目前版本為 1.9.4 :

http://www.datatables.net/download/

將 DataTables-1.9.4.zip 解壓縮後, 在 media/js 目錄下可以找到兩個 dataTables 程式, 其中 jquery.dataTables.js 原始檔 (約 370K), 而 jquery.dataTables.min.js 則是壓縮檔 (約 70 K), 要研究人家怎麼寫的, 請參考原始檔, 我們應用上只要將壓縮檔放到專案的 jquery 目錄即可, 與 jQuery 主檔放在一塊. 另外在 media/css 目錄下有一個  jquery.dataTables.css 檔, 此檔也是放到專案的 jquery 目錄下 :

匯入函式庫

<link href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
<link href="jquery/jquery.dataTables.css" rel="stylesheet">
<script type="text/javascript" src="jquery/jquery.min.js"></script>
<script type="text/javascript" src="jquery/jquery-ui.min.js"></script>
<script type="text/javascript" src="jquery/jquery.dataTables.min.js"></script>


如果我們的專案是放在 Internet 上, 那麼也可以使用 微軟的 CDN 來匯入函式庫 :

從微軟 CDN 匯入函式庫

<link href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.2/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
<link href="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.0.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js"></script>


函式庫準備妥當後, 要讓 dataTables 能正常工作, 還有一個先決條件, 我們的 Table 元素裡一定要包含格式正確的 <thead> 與 <tbody> (但不要求有 <tfoot>), 例如 :

<table id="table1">
    <thead>
        <tr>
            <th>股票名稱</th>
            <th>股票名稱</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>台積電</td>
            <td>2330</td>
        </tr>
        <tr>
            <td>中華電</td>
            <td>2412</td>
        </tr>
    </tbody>
</table>

這樣只要取得 Table 元素的 jQuery 物件, 呼叫其 dataTable() 方法即可 (注意喔, 是單數的 dataTable, 不是複數的 dataTables) :

$(document).ready(function(){
    $("#table1").dataTable();
    });

測試範例 1 : http://tony1966.xyz/test/jquerytest/jquerytest/datatable_1.htm [看原始碼]

看到了嗎? 點一下各欄位表頭, 就會以該欄位值來排序, 也可以在右上角輸入關鍵字對整個表格做搜尋過濾, 還具有分頁功能 (但我們資料太少, 沒頁可分啦).
在範例 1 中, 我們在呼叫 dataTable() 方法時未傳入任何參數, 這樣 dataTables 套件會套用預設值, 預設是英文介面 (這是指 DataTables 的操作部分, 表格內容部分的語言仍然由我們決定). 要如何才能將 dataTables 的操作介面改成繁體中文呢 (也就是本地化, Localization)? 這就必須設定其參數物件中的 oLanguage 選項了. dataTables 的參數是一個物件實體, 每一個選項名稱就是其屬性. 在官網的 Usage/Internationalization 項下, 可以找到選項物件 oLanguage 的各屬性的說明, 但其中與本地化有關之屬性只有如下八個 (第八個還有跟分頁有關的 4 個次屬性), 下列便是繁體中文化的設定 :

var opt={"oLanguage":{"sProcessing":"處理中...",
                                     "sLengthMenu":"顯示 _MENU_ 項結果",
                                     "sZeroRecords":"沒有匹配結果",
                                     "sInfo":"顯示第 _START_ 至 _END_ 項結果,共 _TOTAL_ 項",
                                     "sInfoEmpty":"顯示第 0 至 0 項結果,共 0 項",
                                     "sInfoFiltered":"(從 _MAX_ 項結果過濾)",
                                     "sSearch":"搜索:",
                                     "oPaginate":{"sFirst":"首頁",
                                                          "sPrevious":"上頁",
                                                          "sNext":"下頁",
                                                          "sLast":"尾頁"}
                                     }
               };
$("#table1").dataTable(opt);

注意, 上面的中文字都可以改, 但 _MAX_, _TOTAL 等等都是變數, 不可以改或拿掉. 實際測試如範例 2. 有沒有注意到 dataTables 的選項名稱有一種規律, 凡是屬性值為字串者都以 "s" 開頭 (string), 而屬性值為物件者都以 "o" 開頭 (object)? 其實這是源自全錄公司的 Hungarian notation (匈牙利標示法), 從開頭的 1, 2 個字元, 就知道它的屬性值是哪種資料型態. 不過許多大師不太認同這種標示法, Linux 祖師爺 Linus Torvalds 甚至形容這種作法為 "腦殘 (brain damaged)". 我個人是覺得如果一個參數名稱可以放兩種以上資料類型時就會有點混亂. 
dataTables 所使用的字頭如下 :

 字頭so aaai fb nfn 
 資料類型 stringobjectarray (1維)array (2維)integer float bolean node function

測試範例 2 : http://tony1966.xyz/test/jquerytest/datatable_2.htm [看原始碼]

如果網頁中有好幾個表格, 每一個都得設定 oLanguage 選項的本地化屬性會很煩, 其實只要把 oLanguage 的屬性值, 即 "{sProcessing .... }" 部分存成一個文字檔, 例如繁體中文化檔案 dataTables.zh-tw.txt 內容如下 :

{"sProcessing":"處理中...",
 "sLengthMenu":"顯示 _MENU_ 項結果",
 "sZeroRecords":"沒有匹配結果",
 "sInfo":"顯示第 _START_ 至 _END_ 項結果,共 _TOTAL_ 項",
 "sInfoEmpty":"顯示第 0 至 0 項結果,共 0 項",
 "sInfoFiltered":"(從 _MAX_ 項結果過濾)",
 "sSearch":"搜索:",
 "oPaginate":{"sFirst":"首頁",
                        "sPrevious":"上頁",
                        "sNext":"下頁",
                        "sLast":"尾頁"
                        }
 }

再將 oLanguage 的 sUrl 屬性設為此檔案位址即可 :

$(document).ready(function(){ 
    var opt={"oLanguage":{"sUrl":"dataTables.arabic.txt"}};
    $("#table1").dataTable(opt);
    });

dataTables 已經為各語言製作了本地化選項值, 可以在 Plug-ins/internationalization 下找到, 請點選語言名稱下的 Show details, 再複製其內容, 然後存成 utf-8 格式之純文字檔案, 再上傳到伺服器即可. 範例 3 是以這種方式對繁體中文, 日文, 以及阿拉伯文進行本地化. 

測試範例 3-1 (繁體) : http://tony1966.xyz/test/jquerytest/datatable_3_1.htm [看原始碼]
測試範例 3-2 (日文) : http://tony1966.xyz/test/jquerytest/datatable_3_2.htm [看原始碼]
測試範例 3-3 (阿拉伯文) : http://tony1966.xyz/test/jquerytest/datatable_3_3.htm [看原始碼]

接著來看 bJqueryUI 選項, 這是用來設定是否要套用 jQuery UI 布景主題, 預設為 false (不套用). 如果我們有匯入 jQuery UI 函式庫, 那麼只要將此選項設為 true 即可套用所選之布景. 將範例 3-1 加上 bJqueryUI 選項即成下列範例 4 (要特別注意大小寫, 其中 J/Q/U/I 為大寫, 若寫成小寫將不會動) :

$(document).ready(function(){ 
   var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
                  "bJQueryUI":true
                  };
   $("#table1").dataTable(opt);
   });

測試範例 4 : http://tony1966.xyz/test/jquerytest/datatable_4.htm [看原始碼]

在範例 4 中, 將 bJQueryUI 設為 true 後, 因為我們載入的是 hot-sneaks 布景, 所以整個表格就套上了 hot-sneaks 的主題了, 這樣是不是比較有 fu 了呢? 其實 jQuery UI 提供了24 個佈景, 把範例 4 加上一個下拉式選單從微軟的 CDN 挑選佈景 css 檔, 改成 PHP 程式如範例 5, 看看哪一個佈景比較順眼 :

測試範例 5 : http://tony1966.xyz/test/jquerytest/datatable_5.php [看原始碼]

以上測試都以 DOM 元素中的表格為資料來源 (也就是 HTML 的 TABLE 啦), 其實, dataTables 可以從四種來源取得表格資料 :
  1. DOM
  2. 陣列
  3. Ajax JSON
  4. 後端伺服器處理分頁過濾與搜尋
現在就來看看如何利用陣列來提供表格內容. 以陣列作為資料來源時, 表格元素只要留下 table 即可, 不需要 thead 與 tbody, dataTables 物件會自動建立 :

<table id="table1"></table>

表格的欄位標題要使用選項 aoColumns 來指定, 從開頭的 "ao" 可知此為一物件陣列 (array of objects), 其屬性名稱 "sTitle" 用來設定欄位的標題名稱, 例如 "股票名稱" 等等.  而表格內容則要用選項 aaData 來設定, 從起首的 "aa" 可知此為二維陣列, 其中每一個元素都是一個一維陣列, 表示一筆或一列資料, 其順序與 aoColumns 中的 sTitle 屬性相對應 :

var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
              "bJQueryUI":true,
              "aoColumns":[{"sTitle":"股票名稱"},
                                   {"sTitle":"股票代號"},
                                   {"sTitle":"收盤價 (元)"},
                                   {"sTitle":"成交量 (張)"}],
              "aaData":[["台積電","2330",111.5,19268],
                             ["中華電","2412",95.1,7096],
                            ["中碳","1723",145.0,317],
                            ["創見","2451",104.0,459],
                            ["華擎","3515",104.0,95],
                            ["訊連","5203",98.5,326]]
             };
$("#table1").dataTable(opt);


測試範例 6 : http://tony1966.xyz/test/jquerytest/datatable_6.htm [看原始碼]

從範例 6 可知, 所呈現的表格與上面直接寫在 HTML 裡的效果完全一樣. 但是當表格很大時, 若表格內容直接寫在 HTML 中, 則瀏覽器要建立 DOM 樹會花比較多時間, 這時改用陣列可以加快網頁呈現之速度.

其實 aaData 選項除了用二維陣列以外, 像 aoColumns 那樣使用物件陣列也是可以的, 這時選項 aaData 裡的每一欄位資料都要加上欄位名稱作為屬性, 而且 aoColumns 選項也要多一個屬性 mData, 用來設定每一個欄位對應 aaData 中的哪一個屬性 :


var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":true,
               "aoColumns":[{"sTitle":"股票名稱","mData":"stock_name"},
                                      {"sTitle":"股票代號","mData":"stock_id"},
                                      {"sTitle":"收盤價 (元)","mData":"close"},
                                      {"sTitle":"成交量 (張)","mData":"volumn"}],
               "aaData":[{"stock_name":"台積電",
                                "stock_id":"2330",
                                "close":111.5,
                                "volumn":19268},
                               {"stock_name":"中華電",
                                "stock_id":"2412",
                                "close":95.1,
                                "volumn":7096},
                               {"stock_name":"中碳",
                                "stock_id":"1723",
                                "close":145.0,
                                "volumn":317},
                               {"stock_name":"創見",
"stock_id":"2451",
                             "close":104.0,
                                "volumn":459},
                               {"stock_name":"華擎",
                                "stock_id":"3515",
                                "close":104.0,
                                "volumn":95},
                               {"stock_name":"訊連",
                                "stock_id":"5203",
                                "close":98.5,
                                "volumn":326}]
                 };
$("#table1").dataTable(opt);


測試範例 7 : http://tony1966.xyz/test/jquerytest/datatable_7.htm [看原始碼]

我們把範例 6 稍做修改成範例 7, 可見結果完全一樣, 但我覺得範例 7 比較囉唆, 還是範例 6 較精簡. 所以忘了範例 7 吧, 此處純粹只是測試一下而已, 沒有人願意捨簡就繁啊!

接下來, 項莊舞劍意在沛公, 重點來了, 我們來看第三種資料來源, 也就是透過 Ajax 由遠端資料庫提供表格內容. 這就要用到選項 sAjaxSource 了, 此選項是一個 URL 字串, 指向遠端伺服器的一個能產生 JSON 內容的程式. 這個程式要能輸出一個含有 aaData 單一屬性的物件, 其值為一個表示表格內容的二維陣列, 例如我們要從遠端伺服器的 stocks_list 資料表取出全部台股上市公司股票名稱與代號, 其 JSON 格式如下 :

{"aaData":[["\u5bcc\u90a6","0015"],["\u53f0\u706350","0050"],...,["\u65fa\u65fa\u4fdd","2816"]]}

此 JSON 只有 aaData 一個屬性, 其值為二維陣列, 每一個元素代表表格的一列資料, 其第一欄位為股票名稱, 第二欄位為股票代號. 此資料表 stocks_list 的製作方法詳見 "jQuery UI 的自動完成器 autocomplete 測試" 中的範例 4 說明, 此處不再贅述. 產生此 JSON 輸出的 PHP 程式 get_stocks_list_aa.php 如下 :


<?php
header('Content-Type: text/html;charset=UTF-8');
$host="mysql.1freehosting.com"; //MySQL 主機位址
$username="tony1966_test"; //MySQL 使用者名稱
$password="123456"; //MySQL 使用者密碼
$database="testdb"; //資料庫名稱
$conn=mysql_connect($host, $username, $password); 
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$SQL="SELECT * FROM `stocks_list`"; 
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$stock=array(); //儲存表格內容之二維陣列
for ($i=0; $i<mysql_numrows($result); $i++) { //走訪紀錄集 (列)
     $row=mysql_fetch_array($result); //取得列陣列
     $stock_name=$row["stock_name"];
     $stock_id=$row["stock_id"];
     $stock[$i]=array($stock_name, $stock_id); //存入二維陣列
     } //end of for
$arr["aaData"]=$stock; //表格內容存入關聯式陣列echo json_encode($arr);  //將陣列轉成 JSON 資料格式傳回
?>

上述程式中, 我們從 stocks_list 資料表取得全部股票之名稱與代號後, 先存入二維陣列 $stock 中, 最後將其放入一個一維之關聯式陣列, 索引為 "aaData" (所以 $arr 陣列是三維), 這樣子在將其傳入 json_encode() 後, 就會被轉成具有單一屬性 aaData 的 JSON 物件了. 

然後回到網頁部分, 同樣地, 我們只需要一個空的 TABLE 元素即可, 不需要 THEAD 與 TBODY 元素, dataTables 物件會自動產生. jQuery 程式設定如下 :


var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
              "bJQueryUI":true,
              "bProcessing":true,
              "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
              "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
              };
$("#table1").dataTable(opt);

在上面程式中, 選項 aoColumns 用來設定表格標題, 而選項 sAjaxSource 則用來設定提供表格內容的遠端伺服器程式的 URL, 這樣 dataTables 物件會自動發出 Ajax 要求向遠端伺服器擷取表格內容 (預設是用 GET 方法, 可以用 sServerMethod 選項設為 "POST" 即可). 然後預期伺服器回應一個含有 "aaData" 屬性之 JSON 資, 注意 aoColumns 中的標題順序必須與 JSON 內容的欄位相對應. 為什麼要用 "aaData" 呢? 這其實是 dataTables 套件的預設值, 這可以利用 sAjaxDataProp 選項去更改, 例如 "sAjaxDataProp":"data" 表示伺服器傳回的 JSON 資料必須以 "data" 作為屬性名稱, 這樣上面的後端程式 get_stocks_list_5.php 最後一行就要配合改為 $arr["data"]=$stock 才行. 
但我覺得不要無事生事, 一切用預設值最省時省事. 
另外有沒有注意到, 這裡還多了一個選項 bProcessing (預設為 false), 設為 true 的話, 當表格資料很多, 需要一些時間處理 (例如排序) 時, 表格上會顯示 "處理中 ..." 
完整範例請看範例 8.

測試範例 8 : http://tony1966.xyz/test/jquerytest/datatable_8.htm [看原始碼]

當然我們也可以自行用 jQuery 的公用函式 $.ajax 來取得表格內容, 這樣就不能用 sAjaxSource 選項了, 而要改用 aaData 選項. 顧名思義, aaData 需要一個二維陣列當作其值, 所以上述的 get_stocks_list_aa.php 不能用, 因為那是三維陣列, 必須做一些修改為 get_stocks_list_6.php 如下 :

<?php
header('Content-Type: text/html;charset=UTF-8');
$host="mysql.1freehosting.com"; //MySQL 主機位址
$username="tony1966_test"; //MySQL 使用者名稱
$password="123456"; //MySQL 使用者密碼
$database="testdb"; //資料庫名稱
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$SQL="SELECT * FROM `stocks_list`"; 
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$stock=array(); 
for ($i=0; $i<mysql_numrows($result); $i++) { //走訪紀錄集 (列)
     $row=mysql_fetch_array($result); //取得列陣列
     $stock_name=$row["stock_name"];
     $stock_id=$row["stock_id"];
     $stock[$i]=array($stock_name, $stock_id); //存入二維陣列
     } //end of for
echo json_encode($stock);  //將陣列轉成 JSON 資料格式傳回
?>

只要把二維陣列 $stock 直接轉成 JSON 格式傳回即可. 而 jQuery 程式部分則修改為 Ajax, 我們把遠端傳回的 JSON 資料 (參數 resultData) 設定給 aaData 選項就可以了 (當然也不要忘了 aoColumns 選項所負責的表格標題) :


$.ajax({
  type: "POST",
  url: "http://tony1966.xyz/test/jquerytest/get_stocks_list_6.php",
  data: "",
  dataType: "json",
  success: function(resultData) {
      var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
            "bJQueryUI":true,
            "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
            "aaData": resultData
            };         
       $("#table1").dataTable(opt);
       }
 });

完整測試範例請看範例 9:

測試範例 9 : http://tony1966.xyz/test/jquerytest/datatable_9.htm [看原始碼]

測試完第三種資料來源 (從後端伺服器取得表格內容之 JSON 資料) 後, 接著來看看第四種資料來源. 
第四種方式其實與第三種一樣都是從後端伺服器取得表格內容, 但差別是第四種方式把分頁, 過濾與搜尋功能都交由後端程式處理, dataTables 只是一個事件模組與呈現模組. 這種方式要將  bServerSide 選項設為 true, 同時如方式三那樣設定 sAjaxSource 選項為後端伺服器程式之 URL. 但本文不會對第四種方式進行測試, 因為那太複雜啦, 有違我們使用套件的初衷-使網頁應用簡化, 可以交給 dataTables 的事為何還要自己做? (果然, 科技始終來自於惰性啊!).

接下來我們要測試跟分頁功能有關的選項 (因為表格內容夠多了, 有頁可分了). 分頁相關選項有四個 : 
  1. iDisplayLength (數字, 預設 10)
  2. bLengthChange (true/false, 預設 true)
  3. aLengthMenu (1/2 維陣列, 預設 [10, 25, 50, 100])
  4. sPaginationType ("two_button"/"full_numbers", 預設 "two_button")
  5. bPaginate (true/false, 預設 true)
首先, iDisplayLength 這選項用來設定每一頁顯示幾列資料, 預設是 10 列, 所以 100 筆資料就會被分成 10 頁. 而 bLengthChange 選項用來控制是否要在表格左上角顯示一個可以更改每頁顯示列數的下拉式選單, 其選項值有 10, 25, 50, 與 100. 當使用者選擇時, 其值就會覆蓋 iDisplayLength  選項所設之預設值 10. 如果我們想要固定顯示每頁 80 列, 那麼就要先將 bLengthChange 設為 false 關掉左上角的下拉式選單, 然後將 iDisplayLength 設為 80 即可, 如下列範例 10 所示 (由範例 8 修改而來) :

var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":true,
               "bLengthChange":false,
               "iDisplayLength": 80,
               "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
               "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
               };
$("#table1").dataTable(opt);

測試範例 10-1 : http://tony1966.xyz/test/jquerytest/datatable_10_1.htm [看原始碼]

當 bLengthChange 選項設為 true, 如果對左上角的 10, 25,50,100 預設選單不滿意的話, 可以用 aLengthMenu 選項來更改. 此選項可以是一維陣列, 例如預設的 [10, 25,50,100], 這時選單的 option 元素內容與值 (value) 是一樣的. 如果是二維陣列的話, 例如下面這樣 :

"aLengthMenu":[[5, 30, 60, -1], [5, 30, 60, "All"]]

其中前面元素是 option 的值 (value, -1 表示全部), 後面是顯示 (option 的內容). 請看測試範例 10-2 :

測試範例 10-2 : http://tony1966.xyz/test/jquerytest/datatable_10_2.htm [看原始碼]

而 sPaginationType 選項是指右下角的分頁控制按鈕類型, 預設是 "two_button", 亦即只有上一頁與下一頁兩個按鈕, 上面所有範例皆是如此. 如果設為 "full_numbers", 則除了上頁, 下頁外, 還有首頁, 尾頁, 以及目前頁數前後 2 頁的按鈕, 如下列範例 11 :

var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":false,
               "sPaginationType":"full_numbers",
               "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
               "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
               };
$("#table1").dataTable(opt);

測試範例 11 : http://tony1966.xyz/test/jquerytest/datatable_11.htm [看原始碼]

有沒有注意到範例 11 中我們將 bJQueryUI 選項關閉了 (不套用 jQuery UI 佈景)? 這似乎是 dataTables 的一個小臭蟲, 如果套用主題佈景的話, "full_numbers" 的分頁按鈕會擠在一團, 如下列範例 12 所示 :

測試範例 12 : http://tony1966.xyz/test/jquerytest/datatable_12.htm [看原始碼]

接著來看 bPaginate 選項, 這是用來打開或關閉分頁功能的, 如果設為 false, 則其他分頁相關選項都將失去作用, 例如範例 12 所示 :

var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":true,
               "bPaginate":false,
               "bLengthChange":true,
               "sPaginationType":"full_numbers",
               "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
               "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
               };
$("#table1").dataTable(opt);

測試範例 13 : http://tony1966.xyz/test/jquerytest/datatable_13.htm [看原始碼]

可見只要 bPaginate 設為 false, 則左上角的每頁列數選單, 以及右下角的分頁切換按鈕全都不見了, 全部資料以一頁呈現. 

接下來看看跟卷軸有關的三個選項 :
  1. sScrollY (字串, 垂直卷軸, 預設值 "")
  2. bScrollCollapse (true/false, 卷軸高度設定瓦解, 預設 false)
  3. sScrollX (字串, 水平卷軸, 預設值 "")
如果不想分頁, 但表格資料又有點大, 把網頁撐得很長, 能不能用卷軸來控制呢? 可以的, 這要用到 sScrollY 選項 (字串), 它是用來控制垂直卷軸的 (但要配合將 bPaginate 設為 false 才行), 預設值為空字串 (無垂直卷軸), 其值可以為 CSS 的任何長度表示, 例如 "sScrollY":"400px" 表示, 表格內容部分將被放到一個 400px 的 viewport 容器裡, 只要資料高度超過 400px, 右方便會出現垂直卷軸. 我們將範例 13 稍微修改為範例 14-1 如下 :


var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
              "bJQueryUI":true,
              "bPaginate":false,
              "sScrollY":"400px",
              "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
              "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
              };
$("#table1").dataTable(opt);

測試範例 14-1 : http://tony1966.xyz/test/jquerytest/datatable_14_1.htm [看原始碼]

看到了嗎? 由於我們的上市股票超過 800 支, 在取消分頁功能時網頁會很長, 但若設定 sScrollY 選項, 就可以將網頁限縮在一頁的高度, 然後用卷軸來移動視野. 這比瀏覽器本身卷軸好的地方是, 表頭欄位名稱會一直都在那兒不動, 不像上面範例 13, 往下拉時表頭欄位名稱就看不到啦! 

另外, 跟垂直捲軸很有關係的便是 bScrollCollapse 選項, 當設定垂直捲軸高度時, 若使用搜尋功能找出的資料高度小於卷軸高度時, 這是資料底端與表格底端就會一片空白, 看來很突兀, 如果把 bScrollCollapse 設為 true, 就能瓦解卷軸高度設定, 使得表格底部與資料底部相接, 當然如果搜尋出來的資料高度大於卷軸高度, 仍然被捲軸高度所限制. 例如下列範例 14-2 所示 :

var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
              "bJQueryUI":true,
              "bPaginate":false,
              "sScrollY":"400px",
              "bScrollCollapse":true,
              "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
              "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
              };
$("#table1").dataTable(opt);


測試範例 14-2 : http://tony1966.xyz/test/jquerytest/datatable_14_2.htm [看原始碼]

水平卷軸的設定也是類似, 不過它跟資料筆數無關, 也跟分頁功能無關, 而是欄位太多, 或欄位內資料太長, 以至於表格寬度向右撐破頁面, 這樣瀏覽器下頭就會出現水平卷軸. 利用 sScrollX 選項就能把表格內容放入指定寬度的 viewport 容器中, 由 dataTables 物件自己產生水平卷軸.

由於我們的 stocks_list 資料表只有兩個欄位, 所以將範例 1 稍作修改成範例 15. 為了產生水平卷軸效果, 我們在 table 元素外面再包一個 div 元素, 並將其寬度限制為 300px :


<div style="width:300px;">
  <table id="table1">
    <thead>
      <tr>
        <th>股票名稱</th>
        <th>股票代號</th>
        <th>收盤價 (元)</th>
        <th>成交量 (張)</th>
      </tr>
    </thead>
  <tbody>
    <tr>
      <td>台積電</td>
      <td>2330</td>
      <td>111.5</td>
      <td>19268</td>
    </tr>
  ...
  ...
  </table>
</div>

這樣整個表格就被 div 限制在 300px 的寬度, 然後設定 sScrollX 選項為 400px 如下 :

$(document).ready(function(){
  var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
           "bJQueryUI":true,
           "sScrollX":"400px"
           };
  $("#table1").dataTable(opt);
  });

完整程式碼請看範例 15 :

測試範例 15 : http://tony1966.xyz/test/jquerytest/datatable_15.htm [看原始碼]

範例 15 顯示, 由於我們只給予表格 300px 寬度空間 (利用 div 元素來限制), 但 sScrollX 卻要求 400px 的寬度來放表格內容, 撐破了水平寬度限制, 因此 dataTables 物件就產生水平卷軸來移動視野.

接下來看看跟欄位有關的四個選項 :
  1. sTitle (字串)
  2. sWidth (字串, "20%"/"20px")
  3. sType (字串, "string"/"numeric"/"date"/"html", 預設 "string")
  4. sClass (字串, 樣式類別的名稱, 預設 "")
其實這些是 aoColumns 選項中的子選項, 前面我們已經用過 sTitle 選項, 這是用來設定欄位標題的. 而 sWidth 則是用來設定欄位寬度的, 可用百分比如 "20%", 或長度如 "100px". 而 sType 則是用來設定欄位排序時比對用的資料類型, 有四種 : "string"/"numeric"/"date"/"html", 預設是 "string", 把資料當作字串來排序 (即使資料全部是數字). 選項 sClass 則是用來設定欄位的 CSS 樣式類別, 我們可以自行在網頁中定義一個樣式類別, 然後用 sClass 指定某欄位套用此樣式類別. 下列範例 16 中, 我們增加一個編輯欄位放在第一欄 (寬度 20px), 此欄位內放一個編輯筆的 gif 圖檔, 我們此欄位內容居中對齊, 且文字不會折回 (nowrap), 這可以用一個自訂樣式類別 editClass 來控制 :

.editClass {text-align:center;white-space:nowrap;}

而第二欄位 (股票名稱) 寬度設為 20%, 以字串排序, 第三欄 (股票代號) 為 80%, 以數值排序 :

var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":true,
               "aoColumns":[{"sTitle":"編輯","sWidth":"20px","sClass":"editClass"},
                                        {"sTitle":"股票名稱","sWidth":"20%","sType":"string"},
                                        {"sTitle":"股票代號","sWidth":"80%","sType":"numeric"}],
               "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_5.php"
               };
$("#table1").dataTable(opt);

當然後端程式也要配合修改, 增加輸出一個編輯欄位, 我們修改上面範例 10~14 所用的 get_stocks_list_aa.php, 加上編輯欄位為 get_stocks_list_5.php 如下 :



$stock=array(); 
for ($i=0; $i<mysql_numrows($result); $i++) { //走訪紀錄集 (列)
     $row=mysql_fetch_array($result); //取得列陣列
     $stock_name=$row["stock_name"];
     $stock_id=$row["stock_id"];
     $edit="<img src='edit.gif' style='border-width:0px;width:15px;height:15px;'>";
     $stock[$i]=array($edit, $stock_name, $stock_id);
     } //end of for
$arr["aaData"]=$stock;
echo json_encode($arr);  //將陣列轉成 JSON 資料格式傳回



測試範例 16 : http://tony1966.xyz/test/jquerytest/datatable_16.htm [看原始碼]

從範例 16 可知, 透過 sClass 的樣式設定, 把圖檔在 td 元素裡置中對齊了, 而 sType 的設定使第三欄股票代號排序完全以數值大小為依據, 與範例 13 的以字串排序不同, 例如範例 13 中, 006203 是排在 0061 後面 (當作字串逐字元排序), 而在範例 16 中, 006203 排在 6201 後面 (當作數值排序).

以上都只談到選項的用法, 還沒介紹方法. 現在來看看 dataTables 套件最常用的方法 (API) : fnGetData(). 此處我們要用一個常見的應用來介紹此方法, 就是當我們點一下表格中的某列, 可以連結到某一個網頁, 這在以前的做法通常是在某一列的某欄資料加上超連結, 或是用 script 更改 window.location.href 屬性值. 下列範例 17 我們就來為上面範例 14 的股票列表添加一個功能, 當使用者點選某一列時, 就開啟一個新畫面, 顯示 Yahoo 股市網站中該股的當日行情.  Yahoo 股市當日行情的網址格式如下 (以中華電為例) :

http://tw.stock.yahoo.com/q/q?s=2412

所以, 我們只要取得股票代號就可以製作該股票的 URL 了. 但要如何取得使用者點選的那一列的股票代號呢? 這要從 dataTables 物件著手. 上面的所有範例中, 我們都呼叫了表格元素的 jQuery 物件的 dataTable() 方法, 這方法其實是有傳回值的, 它會傳回 dataTables 物件 :

var oTable=$("#table1").dataTable();  //傳回 dataTables 物件

利用此 dataTables 物件就可以知道使用者點了哪一列資料, 然後讀取該列第二欄位就是股票代號了. dataTables 物件有一個 $ 方法, 可用來執行 jQuery 的選擇器功能, 可以選取表格的 tbody 內容 , 例如列 (tr) 或 元素 (td), 然後我們就可以為選取的列加上 click 動作, 再用 fnGetData() 這個方法來取得列的內容. fnGetData() 方法可以傳入 tr 或 td 節點元素, 若傳入 td 節點, 則傳回該 td 的內容; 若傳入 tr 節點, 則傳回一陣列, 其元素就是該 tr 列中的所有 td 內容. 此處我們傳入 this 表示 tr 節點, 這樣就會顯示該列所有 td 內容, 例如 "台積電,2330,111.5,19268". 
我們將範例 1 修改為範例 17 如下 :


$(document).ready(function(){ 
   var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
                 "bJQueryUI":true
                 };
var oTable=$("#table1").dataTable(opt);
oTable.$('tr').click(function() {
    var row=oTable.fnGetData(this); //取得 tr 內容, 傳回陣列
    alert(row); //顯示陣列內容
    alert(row[1]); //顯示
    });
});

測試範例 17 : http://tony1966.xyz/test/jquerytest/datatable_17.htm [看原始碼]

範例 17 顯示, 點選任一列就會顯示該列所有 td 的內容以及第二個 td (股票代號) 內容. 所以我們就可以利用 row[1] 來製作超連結了, 如範例 18 所示 :


$(document).ready(function(){ 
   var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
                 "bJQueryUI":true
                 };
var oTable=$("#table1").dataTable(opt);
oTable.$('tr').click(function() {
    var row=oTable.fnGetData(this); //取得 tr 內容, 傳回陣列
    var url="http://tw.stock.yahoo.com/q/q?s=" + row[1]; //Yahoo 股市當日行情 url
    window.open(url); //開啟新視窗
    });
});

測試範例 18 : http://tony1966.xyz/test/jquerytest/datatable_18.htm [看原始碼]

範例 18 中, 點任何一列就會開啟 Yahoo 股市網站上該股的當日行情. 一切似乎都很美好不是嗎? 
但是有沒有注意到, 範例 17 與 18 都是以網頁中的表格內容為資料來源, 如果改從遠端伺服器取得表格資料, 上述用 $("tr").click 綁定 click 事件的作法就破功了, 點任何一列都不會動, 我們把範例 14 稍作修改為下列範例 19  :


var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
              "bJQueryUI":true,
              "bPaginate":false,
              "sScrollY":"400px",
              "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
              "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
              };

var oTable=$("#table1").dataTable(opt);
oTable.$('tr').click(function() {
    var row=oTable.fnGetData(this); //取得 tr 內容, 傳回陣列
    var url="http://tw.stock.yahoo.com/q/q?s=" + row[1]; //Yahoo 股市當日行情 url
    window.open(url); //開啟新視窗
    });

測試範例 19 : http://tony1966.xyz/test/jquerytest/datatable_19.htm [看原始碼]

沒錯吧! 範例 19 顯示, 當表格資料來源是遠端伺服器提供的 JSON 資料時, 用 $("tr").click 綁定 click 事件是沒作用的. 根據 dataTables 套件作者 Allan 的回覆是, 要用 on 方法綁定事件 (jQuery 1.7 開始) 才行, 詳情請參考 dataTables 論壇上的 "Use fnGetData w/Ajax data source" 這篇文章. 所以把範例 19 改為如下的範例 20 :


var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
              "bJQueryUI":true,
              "bPaginate":false,
              "sScrollY":"400px",
              "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
              "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
              };
var oTable=$("#table1").dataTable(opt);
oTable.on('click','tr',function() {
    var row=oTable.fnGetData(this); //取得 tr 內容, 傳回陣列
    var url="http://tw.stock.yahoo.com/q/q?s=" + row[1]; //Yahoo 股市當日行情 url
    window.open(url); //開啟新視窗
    });

測試範例 20 : http://tony1966.xyz/test/jquerytest/datatable_20.htm [看原始碼]

範例 20 顯示這招奏效了.  關於 jQuery 的 on() 方法, 可參考這篇 "jQuery 1.7筆記".

最後, 我們以範例 21 將以上測試作個總結, 以範例 20 的開啟 Yahoo 今日行情網頁為基礎, 結合範例 16 具有編輯欄位的表格, 並希望給這些編輯圖樣加上超連結, 點擊後會帶出 stock_id 與 stock_name 兩個變數給另一個編輯網頁 edit_stocks_list.php 如下  :


<form id="edit_form">
  <p>股票代號 : <input type="text" id="stock_id" value="<?php echo $stock_id ?>"></p>
  <p>股票名稱 : <input type="text" id="stock_name" value="<?php echo $stock_name ?>"></p>
  <p><input type="button" id="submit" value="確定"></p>
</form>
<script language="JavaScript">
  $(document).ready(function(){ 
    $("#submit").click(function() {
      alert("更新!");
      });
    });
  </script>

同時把範例 16 中的後端程式 get_stocks_list_5.php 修改為  get_stocks_list_7.php, 給圖檔添加超連結 :


for ($i=0; $i<mysql_numrows($result); $i++) { //走訪紀錄集 (列)
     $row=mysql_fetch_array($result); //取得列陣列
     $stock_name=$row["stock_name"];
     $stock_id=$row["stock_id"];
     $edit="<a href='edit_stocks_list.php?stock_id=".$stock_id."&stock_name=".
               $stock_name."' target='_blank'><img src='edit.gif' ".
               "style='border-width:0px;width:15px;height:15px;'></a>";
     $stock[$i]=array($edit, $stock_name, $stock_id);
     } //end of for
$arr["aaData"]=$stock;
echo json_encode($arr);  //將陣列轉成 JSON 資料格式傳回

而 jQuery 程式部分則必須修改, 在範例 20 中我們是給整列 tr 綁定 click 事件, 但是因為現在第一欄放了一個有超連結的編輯圖檔, 這樣編輯超連結就會被 tr 的 click 事件覆蓋過去, 亦即點擊編輯圖檔時不會開啟編輯網頁了, 而是開啟 Yahoo 每日行情網頁. 解決此問題的方法有二, 那就是也在後端程式中對 stock_id 與 stock_name 欄位加上超連結, 第二個方法是修改功能, 就是不要給 tr 綁定 click 事件, 改成給 td 綁定 click 事件, 而且只有點擊 stock_id 欄位時才開啟 Yahoo 每日行情網頁, 具體作法是判斷 fnGetData() 方法讀取 td 的內容, 如果是數字 (即 stock_id), 就開啟 Yahoo 每日行情網頁, 若不是數字 (stock_name 與編輯欄位) 就不動作 :


    $(document).ready(function(){ 
      var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":true,
               "bPaginate":false,
               "sScrollY":"400px",
               "aoColumns":[{"sTitle":"編輯","sWidth":"20px","sClass":"editClass"},
                                      {"sTitle":"股票名稱"},
                                      {"sTitle":"股票代號"}],
               "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_7.php"
               };
      oTable.on('click','td',function() {
          var cell=oTable.fnGetData(this); //取得 td 內容
          if (!isNaN(cell)) { //點擊股票代號欄位
              var url="http://tw.stock.yahoo.com/q/q?s=" + cell;
              window.open(url);
             }
         });
      });

完整程式碼如範例 21 :

測試範例 21 : http://tony1966.xyz/test/jquerytest/datatable_21.htm [看原始碼]

dataTables 套件中還有許多選項, 但我想上面所測試的應該是專案開發中最常用到的了, 其他的請參考官網的 Usage 與 API 項目下的說明, 下列連結是 dataTables 套件所有選項與方法的列表 :
http://datatables.net/ref

~全劇終

** 編後語 :

在範例 11 中提到一個 dataTables 的 bug, 若分頁時同時套用 jQuery UI, 則右下角的分頁按鈕會擠成一團 (no padding), 根本無法正常選擇分頁. 本來要寫信給 Allan, 但昨日在論壇找到解藥了, 原來套用 jQuery UI 時比較合用的 css 樣式表不是本文開頭所用的 media\css 下的 jquery.dataTables.css, 而是同目錄下的 jquery.dataTables_themeroller.css, 原文詳見 :

http://datatables.net/forums/discussion/8898/full-number-pagination-jquery-ui/p1

所以上面匯入函式庫的部分要改為如下 :


匯入函式庫

<link href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
<link href="jquery/jquery.dataTables_themeroller.css" rel="stylesheet">
<script type="text/javascript" src="jquery/jquery.min.js"></script>
<script type="text/javascript" src="jquery/jquery-ui.min.js"></script>
<script type="text/javascript" src="jquery/jquery.dataTables.min.js"></script>


而從微軟 CDN 匯入的話, 也要改成 :


從微軟 CDN 匯入函式庫

<link href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.2/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
<link href="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables_themeroller.css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.0.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js"></script>



上面套用 jquery.dataTables.css 的測試就不再修改了 (做個對照), 這裡將範例 11 做些修改, 把 bJGueryUI 選項打開, 改為匯入 jquery.dataTables_themeroller.css 樣式表, 同時加上主題布景選擇器, 如下列測試範例 11-1  :


var opt={"oLanguage":{"sUrl":"dataTables.zh-tw.txt"},
               "bJQueryUI":true,
               "sPaginationType":"full_numbers",
               "aoColumns":[{"sTitle":"股票名稱"},{"sTitle":"股票代號"}],
               "sAjaxSource":"http://tony1966.xyz/test/jquerytest/get_stocks_list_aa.php"
               };
$("#table1").dataTable(opt);


測試範例 11-1 : http://tony1966.xyz/test/jquerytest/datatable_11_1.php [看原始碼]

呵呵, 經過這番更改後, 果然變成美美的分頁按鈕了. 除了 dark-hive 與 black-tie 主題太暗, 看不清楚分頁按鈕外, 其他都還不錯.
那如果取消分頁, 改用垂直卷軸, 套用 jquery.dataTables_themeroller.css 樣式表效果如何? 我們修改範例 14-2, 只是改了其中 css 匯入部分如範例 14-3 :

測試範例 14-3 : http://tony1966.xyz/test/jquerytest/datatable_14_3.htm [看原始碼]

感覺有比較好一點, 至少欄位標題沒那麼厚, 上下框框高度加大, 還不錯啦.


2020-07-24 補充 :

jQuery UI dataTables 外掛早已經有了新版, 參見 :

# 發現新的 jQuery UI Datatable

用法與上面舊版的有些不同, 參考 :

# https://datatables.net/

2013年5月4日 星期六

有了智慧沒了感情

每個禮拜五照例都會去喜多園吃晚餐, 昨晚吃到一半時, 鄰桌來了一對母子, 點完菜後, 年輕的媽媽就開始目不轉睛地按手上的智慧型手機, 而大班左右的兒子則手上拿著紅紙紮的康乃馨, 不斷地跟媽媽說話, 但這媽媽不知是玩遊戲, 還是上 Line 或 Facebook, 對於兒子的話充耳不聞, 只是被問煩了, 回一句 "這是餐廳, 不要講那麼大聲". 我在旁邊聽這孩子的童言童語甚感有趣, 我的孩子以前這年紀也是這樣說話. 我轉過頭去時, 剛好他也看到我, 我跟他笑了笑, 他似乎找到知音般也給了我一個微笑. 而他的母親, 一直到我們結帳離開, 還是沉浸在手機操作中.
上一次更奇. 來了一對情侶, 坐定點完菜只講了兩句話, 就開始各自玩手機 (當然是智慧型的啦). 然後就邊吃邊玩手機, 都沒再講話喔.
這兩件事讓我覺得, 智慧型手機風行之後, 人與人之間的距離愈來愈遠, 感情越來越淡, 可以面對面講的, 都躲到虛擬世界去溝通了. 什麼都虛擬化了, 那還有什麼是真實的呢?
我那在上海工作的國中同學說, 你買什麼智慧型手機啦, Line 都沒開. 是啊, 我是在必須用手機上網時, 才會聽到叮咚的 Line, 原來他是前天跟我說早安. 我只好回他晚安.
我從 CHT9100 就開始用智慧型手機了, 因為他有 PDA 功能, 可以提醒我幼稚園何時運動會, 何時要戶外教學, 是我的生活秘書. 即使後來申請了 mPro 吃到飽, 也從來不會一天到晚掛在網路上, 手機對我而言, 就是打電話+查資料+生活秘書的角色. 看看現在捷運, 車站, 等人的地方, 全都是人手一支, 全部當低頭族. 打發時間我認同啦, 但像上面這樣, 像母子嗎? 像情侶嗎?
被手機綁架的, 像人嗎?
手機無罪, 問題是在人啦.

笨死了

最近 014 輕判讓整個社會傻眼, 法官說沒有實質影響力, 所以不是貪污, 還可以拿回 "喬事費", 可見公營事業民營化好處真多, 政府只是釋出部份股權, 卻可以避開立法院監督, 預算不用送審, 控制權又還是牢牢掌握在政府手裡, 污錢從貪污治罪改成背信罪, 出事了還可以辯稱 "沒有實質影響力" ... .

這件事我的感想有二 :

一. 我要當監獄教誨師
如果可以的話, 我要去當監獄教誨師 (沒有離題喲, 往下看), 是的, 我要去跟那些動刀動槍殺人放火的黑道說 : "做黑道的是最笨的, 聰明的都努力讀書, 因為白道犯罪不是罪, 唉, 你們怎麼會笨到去當黑道啦, 笨死了". 特別是那些政客, 那些白面書生, 這些都是 "常業詐騙慣犯" , 有被關嗎? 保證他們都會開始秉燭夜讀, 目標 : T 大法律系.

二. 司法獨立不是法官獨裁
其實司法從來沒有獨立過, 有的只是法官獨裁 (所謂的心證). 檢察官也不是代表國家追訴犯罪, 有的只是奉命追殺政敵. 走過法院的人, 當知司法只是一個工具, 可以是整人工具, 也可以是殺人工具, 當然也不要污衊它, 它也是一個正義工具, 只是當它發出微弱光芒時, 當事人已經被磨成半條命了. 我的疑問是, 裁判體系的司法院長與檢察體系的檢察總長都是總統提名立法院任命, 這樣還敢說司法獨立嗎?

2013年5月1日 星期三

修改 Praat 的共振峰擷取程式

這幾天電腦送修, 沒辦法幫學姊看她做 formant 分析所遇到的程式問題. 昨天抽空把她那高達 240M 的資料下載下來 RUN 看看, 結果發現 PITCH 跑到大概第 52 個受試者時就出現錯誤訊息,   指向 formant.htm 中的一道 SQL 指令, 但那已經執行 51 個受試者都沒問題呀. 查了微軟說明文件, 大概知道可能是語料太多了 (64 個人!), 超過 ACCESS 資料庫能耐. 但是找不到解決方案, 只好試著把資料拆兩半去跑, 果然順利通過天險. 但是跑到抓 formant 時又卡住了 (第 33 個受試者). 仔細看了程式碼, 發現原來當初只寫 32 個受試者, 學姊這 64 個 ... 難怪會跑不下去. 這也讓我發現當初寫說明書時漏掉這一個重要的部份, 那就是抓 formant 的三個 script 要依據實驗設計中的受試者性別來做客製化處理, 這三個抓 formant 的 praat script 不是一體適用的 (除非用 AutoIt 改寫時能夠加入自動設定功能). 所以即使是自己寫的程式, 太久沒用也是會忘掉當初為什麼要這樣寫的. 更改方式如下 :

有三個 praat 程式需要修改 :

1. fetch_formant_list.praat
2. fetch_formant_mean_max_min.praat
3. fetch_percent_formant.praat

打開這三個程式, 搜尋 "#產生 formant 物件" 可以看到一堆 if ... elsif 如下 :

  #產生 formant 物件 (女:1,4,5,6,8,10,13,15,21,24,25,26,28,29,30)
  if k$="01"
     To Formant (burg)... 0 5 5500 0.025 50
  elsif k$="02"
     To Formant (burg)... 0 5 5000 0.025 50
  elsif k$="03"
     To Formant (burg)... 0 5 5000 0.025 50
  elsif k$="04"
     To Formant (burg)... 0 5 5500 0.025 50
  .....
  endif

這部份用途是依據載入的 wav 檔發音者性別來設定 formant 分析的頻率參數, 第三參數為最高 formant 女生要設 5500 Hz, 男生 5000 Hz, 第四參數為分析窗寬度, 窄頻設 0.025 秒, 寬頻設 0.005 秒, 其餘不用改. 然後看 subject 有幾個, 就複製 if ... elsif 段落, 然後看是男是女, 設定其最高共陣峰頻率即可.

勞動節要勞動

今天是 5/1 勞動節, 多年來都是跑去上班, 今年老張原本在邀去美濃爬山, 之前是蠻想去的, 真正過一個勞工的假期. 但五月中要交專案, 所以還是咬牙來上班吧. 然後振振有辭說, 勞動節, 顧名思義就是鼓勵勞動, 不是放假ㄚ.
前天去建國路把修好的電腦抱回來, 從上週起就會不定時關機重開, 幾次資料漏失讓我非常火大. 原本以為 ghost 後就會恢復正常, 哪知更嚴重了. 電腦店的人說是 power 已經劣化, 要換才行, 結果一問才知要 $1250 (最初還挑一個 $2500 的, 都比當初主機板要貴了), 天啊, 什麼時候變那麼貴了? 以前不是才幾百塊? 該不會是隨油電調漲的吧! 原來 633 政見早已實現了! 物價六年內漲三成三!

關於柯遠芬

今天無意中看到這篇 "三月大屠殺", 提到 228 事變中的要角柯遠芬 (另一人為彭孟緝), 激起了我的研究意志, 此何許人也? 查了維基, 摘要如下 :
柯遠芬, 廣東梅縣人 (客籍), 1908 年生, 黃埔四期, 1947 年 228 事變時任台灣警備總部參謀長. 中將退伍後移居美國, 1997 年死於美國.
在中研院的口述歷史中, "自認爲對事件的處理是正確的,至於造成那樣的後果,是始意料未及,所以他不認爲要負責任。". 但 "國防部長白崇禧上簽呈給蔣介石指稱「柯遠芬處事操切、濫用職權,對此次事變舉措尤多失當,且賦性剛愎、不知悛改,擬請與以撤職處分,已示懲戒,而平民忿。」(《大溪檔案》)".
看了 "三月大屠殺" 的描述, 有些人可能會說那是 "大時代的悲劇", 以幽幽一語帶過, 但我看歷史, 常常以看穿越劇的心情, 想像自己就置身於那腥風血雨的現場, 感受一絲絲當事人的痛楚, 我想那是不及於千萬分之一吧.
二十年前還在台北做 IC 設計時, 晚上加班完轉公車的路上, 會經過青島東路, 從 1 號, 3 號, 5 號一直走過去, 思緒不免又會穿越到過去, 這一帶從前是警總軍法處的看守所, 關押的大都是所謂的叛亂犯. 但這裡只是訊問拷打的中途站, "頑劣份子", 下一站便是馬場町了. 我很好奇, 這些校長, 老師, 醫生, 青年學生, 在被槍決前到底是做何感想呢? (%ㄍ!2@ㄢ4**^! 祖國).

因果報應, 絲毫不爽. 文中提到史宏熹其人 : "要塞司令史宏熹也率領武裝軍人逐日大捕大殺,其屠殺方法,殘酷絕倫,二十名青年學生,被割去耳鼻及生殖器,然後用刺刀戳死。因此,被陳儀記大功一次。不但所謂的「奸匪暴徒」殺光了,殺得興起,連基隆參議會副議長楊元丁也被當成「奸匪」投入海中。". 用 Google 搜尋此人, 可找到 "二二八人物小傳-李瑞漢", 文中提到 "曾與三弟李瑞峰共同擔任迎婦產科與基隆要塞司令史宏熹夫人醫療糾紛之訴訟代理人,史宏熹夫人在迎婦產科生產,因注射盤尼西林體質不合而不治死亡,主治日籍醫師迎詣因此以過失殺人罪嫌遭到羈押,而為臺北市參議員兼臺北市人權保障委員會總幹事徐春卿等人保出。史宏熹於向迎婦產科索賠因金額過鉅和解不成後,乃提起刑事訴訟,迎婦產科求助於臺灣省醫師公會,公會乃委託李瑞漢兄弟為被告辯護,該案由臺灣臺北地方法院推事吳鴻麒承審,並委請國立臺灣大學醫學院院長杜聰明鑑定,審判結果,迎婦產科勝訴,法院認定病患之死因為當時人力不可抗拒,迎詣應為無罪之判決,並裁定毋須民事賠償。". 吳伯雄的伯父吳鴻麒也真有勇氣, 敢得罪有槍的, 據維基所述, 其後吳氏被殺, 恐怕與此殺人魔不無關聯吧.

>> 寫給台灣的情書