2007/01/30

embedded mozilla / YUI CustomEvent

今天用 mozilla 的 TestGtkEmbed 這支程式來測試我寫的 UI 時,發現會當掉,根據追蹤發現是因為 YUI 在對 CustomEvent 要 fire() 時產生的,真正原因尚未追蹤到。可疑的碼如下:

container_core.js: this.renderEvent.fire();

據查,不是因為 renderEvent 未定義或 fire() 未定義。這讓我無法用 YUI 中很多東西。

2007/01/29

如何善用 gmail 空間?

gmail 有接近 3G 的空間,雖然人家提供這樣的空間是為了讓你放心的使用 email, 拿來當網路儲存空間似乎濫用別人的好心,但是網路本就是娛樂、儲存、運算、知識多位一體,再加上 google 本就想擴大戰果,我們也只是推波助瀾而已。因此就來說說怎麼利用 gmail 吧。

若是 windows, 可以使用什麼 gdrive 之類的,不在我的思考內。而 linux 上有 gmailfs,經試用的結果,非常不好用,有個非常嚴重的缺點,反應非常遲頓。正因為反應遲頓,我也就沒多試其他的缺點。底下介紹一個 firefox 的擴充套件,叫 gspace, 請見 https://addons.mozilla.org/firefox/1593/ 。要讓它看得懂中文檔名作法有點麻煩,作法有二:
一、請先行安裝原作者發行的,Version 0.5.3, released on Nov 7, 2006,然後下載 http://wade.fs.googlepages.com/gspace.jar 取代原來的套件安裝後的同名檔即可。當然也可以直接下載 http://wade.fs.googlepages.com/gmail_space-0.5.3-fxfl.xpi 來安裝即可。
二、自行解開 gspace.jar 進去修改二個檔,一個是 gactions.js, 找 GSPACE 會有二處,在該行的前面(前一行)加入 fileName = encodeURIComponent(fileName); 此外這個 gactions.js 還有一個 DownloadFile(), 在最開頭加上 fname = decodeURIComponent(fname);
前者是將上傳檔名編碼,後者是將下載檔名解碼。另一個是 common.js 裡要找 gs_gRemoteFileInfo(), 在最前面加入 fileName = decodeURIComponent(fileName); 這一行。這個類似 ls 時要看的檔名,自然是要解碼。

使用第一步要小心,不保證別的版本使用上沒問題。事實上這個 gspace.jar 就是核心所在,若對新版 gspace.xpi 覆蓋上頭的 gspace.jar 的效果就是變成 0.5.3 版。另外若有新版的話,也不能保證會不會是上面的修正方式。

2007/01/26

配合動態載入,撰寫多國語言介面

我是還沒去了解 gmail 怎麼做的,自己想了個解決的方法,讓我們可以動態載入必要的語言檔,當然要搭配昨天的 loader.js, 因為這個 loader.js 需要用 new 產生,因此必須在網頁開始處先產生一個名為 JsLoader 的物件。


/*
author: wade.fs@gmail.com
version: 0.1.0 20070126
Code Licensed in GPL
Desc: for i18n, always using utf8
must: using loading.js + JsLoader in global variable space
*/
/*
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>WadeFS</TITLE>
<script type="text/javascript" src="js/yahoo.js"></script>
<script type="text/javascript" src="js/connection.js"></script>
<script type="text/javascript" src="js/loader.js"></script>
<script type="text/javascript" src="js/i18n.js"></script>

<script type="text/javascript">
var JsLoader;
function JsLoadHandler(file) {
if (file.substr(0,4) == "i18n") {
alert ("i18n.$('hdd') = " + i18n.$("hdd"));
}
}
function init() {
JsLoader = new YAHOO.Loader;
JsLoader.onFileLoad = JsLoadHandler;
i18n.load("zh_TW"); // 要在 JsLoader 之後使用
}
</script>
</HEAD>
<BODY onLoad="init()"></BODY></HTML>
*/
/* i18n/zh_TW.js, 請用 utf8 格式編輯
{
"mainboard": "主機",
"hdd": "硬碟",
"usb-disk": "隨身碟",
"cdrom": "光碟機",
"display": "顯示卡",
}
*/
var i18n = {
id: "en",
lang: {},
load: function(lang) {
if (typeof(lang) != "string") { this.id = "en"; }
else { this.id = lang; }
this.lang = {};
if (this.id !== "en") {
JsLoader.load("i18n/"+this.id+".js");
}
},
$: function(token) {
if (typeof(this.lang[""+token]) != "undefined") {
return this.lang[""+token];
}
else { return token; }
},
};

2007/01/25

動態載入 javascript

在發展 AJAX 應用時常常會把 javascript 寫的太大了,分成好幾個檔當然是應該的,但是對載入卻無法加速,底下片斷是改來的,不過寫法差異很大,想要真正能用的話還得下載YUI才行。


/**
* Author: wade.fs@gmail.com
* Original author Dav Glass <dav.glass@yahoo.com>
* version 0.2 20060126
* desc: 本程式必須配合 YUI 使用
*/
/*
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>YAHOO</TITLE>
<script type="text/javascript" src="js/yahoo.js"></script>
<script type="text/javascript" src="js/connection.js"></script>
<script type="text/javascript" src="js/loader.js"></script>

<script type="text/javascript">
var ob = {
'A': "a char A",
'Z': "char Z here",
};
function act(o, keyCode, status) {
if (keyCode == 65) { alert (o['A']); }
else { alert (o['Z']); }
}
function keyLoad() {
document.onkeydown = Key.pressed;
Key.addListener(act, ob, ""); // register any key for ob
}
function init() {
var myJsLoader = new YAHOO.Loader;
myJsLoader.onFileLoad = keyLoad;
var urls = [ "js/key.js" ];
myJsLoader.add(urls);
myJsLoader.load();
}
</script>
</HEAD>
<BODY id="ss-body" onLoad="init()">
</BODY>
</HTML>
*/
YAHOO.Loader = function() { this.init(); };

YAHOO.Loader.prototype = {
id: null,
scripts: {},
isLoaded: {},
holder: null,
init: function(id) {
this.holder = document.createElement('script');
this.holder.id = 'wadefs_loader_holder';
this.holder.type = 'text/javascript';
document.getElementsByTagName('body')[0].appendChild(this.holder);
},
onFileLoad: function(fileName, o) {},
onFileFail: function(fileName, o) {},
transActions: [],
add: function(file) {
if (file instanceof Array) {
for (var i = 0; i < file.length; i++) {
this.scripts[file[i]] = file[i];
}
} else {
this.scripts[file] = file;
}
},
insertScript: function(name) {
if (typeof(name) == "undefined") return;
if (!this.isLoaded[name]) {
var req = YAHOO.util.Connect.asyncRequest('GET', name, {
success: this.insertGood,
failure: this.insertBad,
caller: this,
});
this.transActions[req.tId] = name;
this.isLoaded[name] = name;
}
},
insertGood: function(o) {
if (o.responseText !== "undefined"){
var file = this.caller.transActions[o.tId];
if (file.substr(0,4) == "i18n") {
i18n.lang = eval('(' + o.responseText + ')');
}
else
this.caller.holder.innerHTML += o.responseText;
this.caller.onFileLoad(this.caller.transActions[o.tId], o);
}
},
insertBad: function(o) {
o.fileName = this.caller.transActions[o.tId];
this.caller.onFileFail(this.caller.transActions[o.tId], o);
},
load: function(thisFile) {
if (thisFile) {
if (thisFile instanceof Array) {
for (var i = 0; i < thisFile.length; i++) {
this.insertScript(thisFile[i]);
}
} else {
this.insertScript(thisFile);
}
} else {
for (var i in this.scripts) {
this.insertScript(this.scripts[i]);
}
}
},
};

Firefox 的按鍵事件簿

在以 firefox/embedded mozilla 當 UI 或是設計網頁時,按鍵是非常需要處理的元件,底下的程式碼目前只能處理任意按鍵,可自行下載修改成讓物件只聆聽特殊按件。測試網頁在註解中。


// Author: wade.fs@gmail.com
// Version: v0.1 20070126
// Code licensed under GPL
// Desc: javascript keyboard event handler
// 預計將來會處理中文輸入法,目前只有簡單的按鍵處理
/*
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>WadeFS</TITLE>
<script type="text/javascript" src="js/key.js"></script>

<script type="text/javascript">
var ob = {
'A': "a char A",
'Z': "other char",
};
function act(o, keyCode, status) {
if (keyCode == 65) { alert (o['A']); }
else { alert (o['Z']); }
}
function init() {
document.onkeydown = Key.pressed;
Key.addListener(act, ob, ""); // register any key for ob
}
</script>
</HEAD>
<BODY id="ss-body" onLoad="init()">
</BODY>
</HTML>
*/
var Key = {
status: 0, // NumLock, CapsLock, ScrollLock, Full(全半)
registered: [],
keyCode: 0,
addListener: function(f, o, keys) {
this.registered.push ([f, o, keys]);
return this.registered.length;
},
setNumLock: function(numLock) {
if (numLock) { this.status |= 0x01; }
else { this.status &= 0xFE; }
},
getNumLock: function() {
return this.status &= 0x01;
},
setCapsLock: function(capsLock) {
if (capsLock) { this.status |= 0x02; }
else { this.status &= 0xFD; }
},
getCapsLock: function() {
return this.status &= 0x02;
},
setScrollLock: function(scrollLock) {
if (scrollLock) { this.status |= 0x04; }
else { this.status &= 0xFB; }
},
getScrollLock: function() {
return this.status &= 0x04;
},
keyName: { 8: "BackSpace", 9: "Tab", 13: "Enter", 19: "Pause", 27: "Esc",
32: "Space", 33: "PgUp", 34: "PgDn", 35: "End", 36: "Home",
37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScreen",
45: "Ins", 46: "Del", 106: "NMultiply", 107: "NPlus",
109: "NMinus", 110: "NDot", 111: "NDivide",
},
showKeyMsg: function() {
var keyCode = this.keyCode & 0xFF;
if (keyCode > 0) {
var msg='(';
msg += (this.status & 0x01) ? 'N' : 'n';
msg += (this.status & 0x02) ? 'C' : 'c';
msg += (this.status & 0x04) ? 'S' : 's';
msg += ')';
if (this.keyCode & 0x100) msg += 's';
if (this.keyCode & 0x200) msg += 'c';
if (this.keyCode & 0x400) msg += 'a';
if (this.keyCode >= 0x100) msg += '-';
// 數字鍵
if (keyCode >= 96 && keyCode <= 105) { keyCode -= 48; }
// 文數字
if ((keyCode >= 65 && keyCode <= 90) ||
(keyCode >= 48 && keyCode <= 57))
{
msg += String.fromCharCode(keyCode);
}
// F1-F12
else if (keyCode >= 112 && keyCode <= 123) {
keyCode -= 111;
msg += 'F' + keyCode;
}
else if (typeof (this.keyName[""+keyCode]) != "undefined") {
msg += this.keyName[""+keyCode];
}
else { msg += keyCode; }
alert (msg);
}
},
processKey: function() {
if (this.registered.length <= 0) { this.showKeyMsg(); }
else {
// [[f, o, key],...]
var idx;
for (idx=0; idx<this.registered.length; idx++) {
if (this.registered[idx][2].length == 0) { // for any key
var f = this.registered[idx][0];
var o = this.registered[idx][1];
f(o, this.keyCode, this.status);
}
}
}
},
pushKey: function(key,shift,ctrl,alt) {
this.keyCode = 0;
if (key == this.K_NumLock) { this.status ^= 0x01; }
else if (key == this.K_Caps) { this.status ^= 0x02; }
else if (key == this.K_ScrollLock) { this.status ^= 0x04; }
else {
var ctrlKey = 0;
// check if composed with control key
if (shift) { ctrlKey |= 0x100; }
if (ctrl) { ctrlKey |= 0x200; }
if (alt) { ctrlKey |= 0x400; }
// check if control key only
if (key == this.K_Shift) { this.keyCode = 0x100 | ctrlKey; }
else if (key == this.K_Ctrl) { this.keyCode = 0x200 | ctrlKey; }
else if (key == this.K_Alt) { this.keyCode = 0x400 | ctrlKey; }
else if (key == this.K_shiftAlt) { this.keyCode = 0x100|0x300|ctrlKey; }
else {
this.keyCode = key | ctrlKey;
}
this.processKey();
}
},
pressed: function(e){
Key.pushKey(e.keyCode, e.shiftKey, e.ctrlKey, e.altKey);
// stop key event propagation to browser
e.preventDefault();
e.stopPropagation();
},
K_: function(ctrl) {
var i=0, keyCode=0;
while (i < ctrl.length) {
if (ctrl.charCodeAt(i) == 'a') { keyCode += 0x400; }
else if (ctrl.charCodeAt(i) == 'c') { keyCode += 0x200; }
else if (ctrl.charCodeAt(i) == 's') { keyCode += 0x100; }
else if (i == (ctrl.length-1)) { keyCode += ctrl.charCodeAt(i); }
else { keyCode = 0; }
}
return keyCode;
},
K_BackSpace: 8,
K_Tab: 9,
K_Enter: 13,
K_Shift: 16,
K_Ctrl: 17,
K_Alt: 18,
K_Pause: 19,
K_Caps: 20,
K_Esc: 27,
K_Space: 32,
K_PgUp: 33,
K_PgDn: 34,
K_End: 35,
K_Home: 36,
K_Left: 37,
K_Up: 38,
K_Right: 39,
K_Down: 40,
K_PrintScreen: 44,
K_Ins: 45,
K_Del: 46,
K_N0: 96,
K_N1: 97,
K_N2: 98,
K_N3: 99,
K_N4: 100,
K_N5: 101,
K_N6: 102,
K_N7: 103,
K_N8: 104,
K_N9: 105,
K_NMultiply: 106,
K_NPlus: 107,
K_NMinus: 109,
K_NDot: 110,
K_NDivide: 111,
K_F1: 112,
K_F2: 113,
K_F3: 114,
K_F4: 115,
K_F5: 116,
K_F6: 117,
K_F7: 118,
K_F8: 119,
K_F9: 120,
K_F10: 121,
K_F11: 122,
K_F12: 123,
K_NumLock: 144,
K_ScrollLock: 145,
K_shiftAlt: 224,
};

2007/01/17

cdrom tray status: 偵測光碟機狀態



/*
* cdstatus.c <device>
*
* This loads a CDROM from a specified slot in a changer, and displays
* information about the changer status. The drive should be unmounted before
* using this program.
*
* Based on code originally from Gerhard Zuber .
* Changer status information, and rewrite for the new Uniform CDROM driver
* interface by Erik Andersen <andersee@debian.org>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h>

int
main (int argc, char **argv)
{
char *program;
char *device;
int fd; /* file descriptor for CD-ROM device */
int status; /* return status for system calls */

if (argc != 2) {
fprintf (stderr, "usage: %s \n", program);
exit (1);
}

program = argv[0];
device = argv[1];

/* open device */
fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
fprintf (stderr, "%s: open failed for `%s': %s\n",
program, device, strerror (errno));
exit (1);
}

status = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
if (status<0) {
perror(" CDROM_DRIVE_STATUS");
} else switch(status) {
case CDS_NO_INFO: printf ("NO_INFO\n"); break;
case CDS_NO_DISC: printf ("NO_DISC\n"); break;
case CDS_TRAY_OPEN: printf ("TRAY_OPEN\n"); break;
case CDS_DRIVE_NOT_READY: printf ("NOT_READY\n"); break;
case CDS_DISC_OK: printf ("DISC_OK\n"); break;
default: printf ("ERROR: %d\n", status); break;
}

/* close device */
close (fd);
exit (0);
}

2007/01/11

reverse text file order

今天想著要怎樣把一個純文字檔反向排列,當然寫個 c file 是很簡單,但是我現在的原則是儘量用 busybox 能提供的工具來完成,底下有個小片斷可以參考一下,或使用 tac 命令:


  • cat -n $FILE | sort -rn | cut -f2


當然上面的做法比較能理解,不過還有兩個用 sed 完成的範例:


  1. sed '1!G;h;$!d'
  2. sed -n '1!G;h;$p'



順便提供反轉行的寫法, 或使用 rev 命令:

  • sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//'

2007/01/09

AJAX, Javascript 處理 XML/JSON 資料

在 javascript 中處理 XML 一直沒機會玩,最近玩還真的覺得怎麼跟很多人在網站上說的不一樣,後來找到 Ajax 處理資料方法比較 裡頭說的非常清楚。僅供參考

另外,因為資料型態不一樣,server 端的 Content-type 要設對,例如 XML 要設成 text/xml, 而 JSON 則是 text/plain

最後,我選擇了 JSON 方法,因為一來這是瀏覽器剖析資料的格式,想來會是最有效率的,另外,雖然 XML 較一般性,但是其實就閱讀而言 JSON 也差不多,只是有一點,刮號配對也許較易出錯,對於 {}, [] 的使用時機若怕搞錯,就多比較看看吧。

註: {} 是用來形成物件,而 [] 則用來建構陣列
註二:{} 用來形成不同屬性的物件,而 [] 用來建構相同屬性名稱的不同物件相當好用

2007/01/05

在 shell script 內處理網頁的 Form Data

為了讓 geexbox 方便遠端管理,就上網找了個 ProcCGI,底下是一般流程。

  1. 選定 Applet 名稱,這是用來設定 CONFIG_NAME 用的,此處是用 PROCCGI,也就是 CONFIG_PROCCGI
  2. 將 source code 放到適當的目錄,我是放到 networking 下,並將proccgi.c的 main() 改成 Applet_main(),即 proccgi_main()。
  3. 修改networking 目錄下的 Config.in,找適當的地方新增選項,請參考其他選項作適當的編修,我是放在照字母順序放在 Router 前:

    config CONFIG_PROCCGI
    bool "proccgi"
    default y
    depends on FEATURE_HTTPD_CGI
    help
    proccgi is s utility for web cgi-bin

  4. 同樣在 networking 目錄下,修改 Kbuild,在適當的地方加入:

    lib-$(CONFIG_PROCCGI) += proccgi.o

  5. 修改 include/applets.h,它與底下的 usage.h 都必須按照字母順序排列,因此請自行在適當的地方加入:

    USE_PROCCGI(APPLET(proccgi, _BB_DIR_USR_BIN, _BB_SUID_NEVER))

    不知道您有沒有注意到這跟前二條的說明是配合的。
  6. 修改 include/usage.h,加入命令輔助訊息,一樣要注意按照字母順序:

    #define proccgi_trivial_usage "eval \"`proccgi $*`\""
    #define proccgi_full_usage "eval \"`proccgi $*`\"\n" "will set the form variable into env"

  7. 記得將 proccgi.c 中的 main() 改成 proccgi_main()
  8. 參考適用於busybox-1.3.1patch

跑馬燈

原本只有 IE 支援的 <marquee>, 現在連 firefox 也支援了,省卻自己寫 javascript 控制的時間。這邊分享個小心得,我無法透過 CSS 將 <marquee> 定位在特定位置,是用 <table> 才做到,僅供參考。