2008/06/25

Plurk - Widget

  Plurk 的官方版 widget 無法修改 logo,導致許多人 CSS 改了老半天,把所有的顏色都和 blog 調到一致了,最後卻和這顆火腿不搭:



  另外原始版本還有 SMALL 模式顯示亂掉、interval 功能在 IE 下不會動的問題。因此我花了一點時間把程式簡化,拿掉不需要的部份,調整了參數傳入方式並加上註解。有興趣的人可以把底下整段複製走,修改必要的參數與色彩後,加進 Blogger 裡。(別家的部落格我沒測過,請自行實驗,能用就算賺到)

修改重點:
  • 有加「/* */」附註的行,是較有機會改動的地方。
  • CSS 中的 background 可以選擇用顏色或底圖,我放了範例,但最後記得留下一筆就好。
  • JavaScript 裡面有一排連續的「PWARG_xxx」是參數設定,大部份和原本的名稱一樣。
  • PWARG_USERID:請參考 Plurk 畫面右下方「Share your plurk page with friends: 」的格子裡,「from_uid=」接著一串數字,把它填進去。當然如果你暗戀我的話,不改也沒關係。
  • PWARG_WIDGETLOGO:請自行製作適當大小的 logo,並同時調整 CSS 中的圖寬。
完整程式碼:


<style type="text/css">
.plurk-widget {
    font: 11px 'Lucida Grande', 'Lucida Sans Unicode','Lucida Sans Regular', Tahoma, Verdana, sans-serif;
    background: #cf682f;    /* 主版底色 */
    /* background: url('http://www.plurk.com/static/theme/image/wallpaper.png'); 主版底圖 */
    padding: 4px;
    border: 1px solid #a14a19;    /* 主版外框 */
    max-width: 300px;
    width: auto;    /* Widget 寬度 */
    position: relative;
    padding-top: 40px;
    color: black;    /* 文字顏色 */
    text-align: left;
    padding-bottom: 0;
}

.plurk-widget a {
    color: #126cb8 !important;    /* 鏈結, 包括個人暱稱與回應 */
    padding: 0 !important;
    border: none !important;
    background: none;
    text-decoration: underline;
}

.plurk-widget img {
    border: none !important;
}

.plurk-widget a:hover{
    text-decoration: underline!important;
}

.plurk-widget .plurk-logo {
    display: block;
    padding: 0;
    margin: 0 6px 8px 0;
    width: 40px;    /* logo 寬度 */
    position: absolute;
    right: 0;
    top: 4px;
    height: 31px;
    padding: 0 !important;
}

.plurk-widget .plurk-timeline {
    clear: both;
    overflow-x: hidden;
    overflow-y: scroll;
    background: #cae7fd;    /* 時間軸底色 */
    /* background: url('http://www.plurk.com/static/theme/image/water.gif'); 時間軸底圖 */
    word-wrap: break-word;
    border: 1px solid #99705f;    /* 時間軸外框 */
    padding: 3px 8px;
}

.plurk-widget .plurk-nickname {
    color: black;
    text-decoration: none;
    font-weight: bold;
}

.plurk-widget .plurk-message {
    background: url(plurk-widget-separator.gif) repeat-x left bottom;
    padding: 6px 0 10px 0;
    margin: 0 0 4px 0;
}

.plurk-widget .plurk-meta {
    font-size: 0.9em;
    text-align: right;
    color: #578ebd;    /* 留言時間文字顏色 */
    padding-top: 2px;
}

.plurk-widget .plurk-qualifier {
    border-right: 1px solid #333;
    border-bottom: 1px solid #333;
}

.plurk-widget .plurk-empty {
    margin: 5px 0 0 0;
    padding: 10px;
    background-color: #F0F8FF;
    color: #4c8cc2;
    text-align: center;
    border: 1px solid #4c8cc2;
}

.plurk-widget .plurk-empty strong {
    font-weight: bold;
    color: black;
    display: block;
}

.plurk-widget .call-action{
    font-size: 0.9em;
    color: #fadfd0;
    padding: 10px 0 5px 0;
}

.plurk-widget .call-action a{
    color: #5a3f30 !important;    /* 最底下的廣告鏈結文字顏色 */
    text-decoration: underline;
    font-weight: normal;
}
</style>
<script id="PlurkWidgetScript" type="text/javascript">
/**
* Plurk Widget
* ------------
*
* Supported parameters:
*
*    - PWARG_USERID    the user_id whose plurks we want to display
*    - PWARG_HEIGHT    the height in pixels (has no effect if CSS is disabled)
*    - PWARG_NOCSS        set to "true" if you don't want any CSS applied
*    - PWARG_INTERVAL    the reloading interval in seconds
*            (30 per default, 0 for no reloading at all.)
*    - PWARG_CHUNK    the number of plurks in the widget
*    - PWARG_WIDGETLOGO    the logo we want to display
*
* If you are an advanced user and want to style the widget with custom CSS,
* here are the classes the widget uses:
*
* plurk-widget        the overall div that wraps the widget
* plurk-logo        a div that is usually used to display the logo
* plurk-timeline    the div containing the messages on the timeline
* plurk-loading-pane    a div that is displayed while data is retrieved
* plurk-message    a div that contains the message of a user
* plurk-nickname    a link to the users's profile
* plurk-qualifier    the span for the qualifier
* plurk-qualifier-x    the span for the qualifier x (x == says, thinks etc)
*
* For further information, you can reference the css file at
*    http://www.plurk.com/static/widget/widget.css
*
* (c) Copyright 2008 by Galt Networks.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* ------------
* Original version of this script:
*    http://www.plurk.com/static/widget/plurkwidget.js
*
* Modified by Sorry (http://www.plurk.com/user/sorry)
* Version 0.3
* 2008/6/25
*/

;(function() {
    
    // globals and constants
    var
        PWARG_SERVERURL = 'http://www.plurk.com',
        PWARG_USERID = 1418700,    /* from_uid=xxxxxxx */
        PWARG_HEIGHT = 300,    /* Widget 高度 */
        PWARG_NOCSS = 'true',
        PWARG_INTERVAL = 0,
        PWARG_CHUNK = 10,
        PWARG_WIDGETLOGO = 'http://www.plurk.com/static/creatures/small/1.png',    /* 上方的 logo */

        QUALIFIER_COLORS = {
            is:    '#e57c43', says:    '#E2560B', feels:    '#2D83BE',
            thinks:    '#689CC1', wants:    '#8DB241', wishes:    '#5BB017',
            has:    '#7A9A37', loves:    '#B20C0C', hates:    '#111111',
            asks:    '#8361bc', will:    '#B46DB9', was:        '#525252',
            had:    '#8C8C8C', likes:    '#CB2728', shares:    '#A74949',
            gives:    '#620E0E'
        },
        TIME_UNITS = [
            [60 * 60 * 24 * 30, 'month'],
            [60 * 60 * 24 * 7, 'week'],
            [60 * 60 * 24, 'day'],
            [60 * 60, 'hour'],
            [60, 'minute']
        ],
        widget, lastRequest, fish, timeline, options;

    // general purpose helpers
    function each(s, f) {
        if (!s) return; var r;
        if (s.length !== undefined)
            for (var x = 0, y = s.length; x < y; ++x) {
                if (typeof (r = f.call(s[x], x)) !== 'undefined')
                    return r;
            }
        else for (var k in s)
            if (typeof (r = f.call(this, k, s[k])) !== 'undefined')
                return r;
    }

    // formatting
    function timedelta(d) {
        var delta = ((new Date()).getTime() - d.getTime()) / 1000, value;
        return each(TIME_UNITS, function() {
            if ((value = Math.floor(delta / this[0])) > 0)
                return value + ' ' + this[1] + (value != 1 ? 's' : '');
        }) || '0 minutes';
    }

    // DOM
    function $fnGetElmt(n) { return document.getElementsByTagName(n)[0]; }
    function $fnCreateElmt(n, a) {
        var rv = document.createElement(n);
        each(a, function(k, v) { rv.setAttribute(k, v); });
        return rv;
    }
    function $$fnAppendElmt(p, n, a) { return p.appendChild($fnCreateElmt(n, a)); }
    function $fnAppendText(n, t) { n.appendChild(document.createTextNode(t)); return n; }
    function $fnAppendHtml(n, h) { var t = $$fnAppendElmt(n, 'span'); t.innerHTML = h; return n; }

    // CSS
    function css(n, a, c) {
        each(a, function(k, v) { n.style[k] = v; });
        if (c) n.className = c; return n;
    }
    function hide(e) { e.style.display = 'none'; return e; }
    function show(e) { e.style.display = ''; return e; }

    // communication system
    if (!this.$__plurkWidgetReceiveData) {
        $__plurkWidgetReceivers = [];
        $__plurkWidgetReceiveData = function(data) {
            $__plurkWidgetReceivers[data.receiverID](data);
        }
    }
    var receiverID = $__plurkWidgetReceivers.length;
    $__plurkWidgetReceivers.push(function(data) {
        lastRequest.parentNode.removeChild(lastRequest);
        if (data.plurks) updatePlurks(data.plurks, data.private);
        lastRequest = null;
    });

    // widget code
    function initializeWidget() {
        if (PWARG_NOCSS !== 'true')
            $$fnAppendElmt($fnGetElmt('head'), 'link', {
                href:    PWARG_SERVERURL + '/static/widget/widget.css',
                rel:    'stylesheet',
                type:    'text/css'
            });
        css(widget, {visibility: 'hidden'});
        var logo = css($$fnAppendElmt(widget, 'a', {href: PWARG_SERVERURL + '/redeemByWidget?from_uid=' + PWARG_USERID}), {
            background: 'url(' + PWARG_WIDGETLOGO + ') no-repeat 0 0'
        }, 'plurk-logo');
        timeline = css($$fnAppendElmt(widget, 'div'), {
            height:         (PWARG_HEIGHT || 300) + 'px'
        }, 'plurk-timeline');
        fish = css($$fnAppendElmt(timeline, 'div'), {
            background:    'url(' + PWARG_SERVERURL + '/static/loading.gif) no-repeat center center',
            height:        '90px'
        }, 'plurk-loading-pane'),
        callLink = $$fnAppendElmt(widget, 'div'),
        callLink.innerHTML = '<a href="'+ PWARG_SERVERURL + '/redeemByWidget?from_uid=' + PWARG_USERID
            + '">一起來體驗 Plurk 吧</a>',
        callTo = css(callLink, null, 'call-action');
        requestPlurks();
        if (PWARG_INTERVAL && PWARG_INTERVAL !== '0')
            setInterval(requestPlurks, (parseInt(PWARG_INTERVAL, 10) || 30) * 1000);
    }

    function requestPlurks() {
        if (lastRequest) return;
        hide(fish);
        lastRequest = $$fnAppendElmt($fnGetElmt('head'), 'script', {
            type:    'text/javascript',
            src:    PWARG_SERVERURL + '/API/Widget.getUserPlurks?'
                    + 'user_id=' + PWARG_USERID
                    + '&receiver_id=' + receiverID
                    + '&per_chunk=' + PWARG_CHUNK
        });
    }

    function updatePlurks(plurks, is_private) {
        timeline.innerHTML = '';
        timeline.appendChild(show(fish));
        if (plurks.length) each(plurks, function(idx) {
            var plurk = css($$fnAppendElmt(timeline, 'div'), null, 'plurk-message');            
            $fnAppendText(css($$fnAppendElmt(plurk, 'a', {href: PWARG_SERVERURL + '/user/' + this.username}),
                 null, 'plurk-nickname'), this.username);
            if (this.qualifier != ':') {
                $fnAppendHtml(plurk, ' ');
                var shade = QUALIFIER_COLORS[this.en_qualifier];
                $fnAppendText(css($$fnAppendElmt(plurk, 'span'), {
                    backgroundColor:    shade,
                    padding:        '0 3px 0 3px',
                    color:            'white'
                }, 'plurk-qualifier plurk-qualifier-' + this.en_qualifier),
                     this.qualifier);
            }
            $fnAppendHtml(plurk, ' ' + this.content);
            $fnAppendText($$fnAppendElmt($fnAppendText(css($$fnAppendElmt(plurk, 'div'), null, 'plurk-meta'),
                 timedelta(this.pub_date) + ' ago | '), 'a', {
                href:    PWARG_SERVERURL + '/p/' + (this.id).toString(36),
                target:    '_blank'
            }), this.responses == 1 ? '1 response' : this.responses + ' responses');
        });
        else {
            var empty = css($$fnAppendElmt(timeline, 'div'), null, 'plurk-empty');
            $fnAppendText($$fnAppendElmt(empty, 'strong'), 'Ouch. ');
            $fnAppendText(empty, is_private
                ? '抱歉, 你設定了 private 屬性, 別人不能讀取你的 plurk. 這個 widget 沒有東西可以顯示.'
                : '你沒有 plurk 可以顯示. 先去寫個幾句吧 :).'
            );
        }
        hide(fish);
        css(widget, {visibility: 'visible'});
    }

    // widget setup
    widget = css($fnCreateElmt('div'), null, 'plurk-widget');
    var options = document.getElementById('PlurkWidgetScript');
    options.parentNode.replaceChild(widget, options);
    initializeWidget();

})();
</script>

  最後,不管你改爛了什麼,都不要來找我。我一概不負責任。

2008/06/24

Plurk - Hints

這篇寫一點跟 Plurk 的用法和注意事項。

◆ 修改 Plurk 介面語系:在畫面左下角的下拉式選擇即可。

◆ 修改 Plurk 發言語系:「Private plurks, languages & options」→「Plurk language」→「Chinese (Traditional)」。在回應的時候,只能使用對方的語系。手機環境下只能使用英語語系。

◆ 貼圖片的方法:直接把圖片網址貼進去。YouTube 的影片也可以這樣貼。

◆ 文字格式:
  • **粗體** : **粗體**
  • *斜體* : *斜體*
  • __底線__ : __底線__
  • `address` : `address`
  • http://aigrette.blogspot.com/ : http://aigrette.blogspot.com/
  • http://aigrette.blogspot.com/ (My Blog) : My Blog
  • @sorry : Sorry
範例:「@stewwu 請上http://www.cpa.gov.tw/ (人事行政局網站)看**停班停課消息**」
效果:「stewwu 請上人事行政局網站**停班停課消息**

嫌麻煩的話,Firefox 使用者也可以安裝 maxchu 所開發的 Greasemonkey Script:Plurk Rich Edit

◆ 外掛

◆ 鍵盤快速鍵:
  • ←:時間軸左捲
  • →:時間軸右捲
  • &:捲到目前時間
  • V:切換到新留言模式/全部模式
  • M:全部標示為已讀
  • U:更新以取得新留言

◆ 滑鼠停在 Profile 的照片上稍候,照片會放大並變得清晰。

◆ 滑鼠停在回應上稍候,右邊會出現該筆回應的時間;停在回應者的暱稱上稍候,右邊會出現淺藍色的倒三角形,按下叫出小選單可顯示該使用者的相關資訊。該選單中的「Block」可以將該使用者的所有發言消音。

◆ 展開單則 Plurk 後,有一個「mute」按鈕,可以讓系統不再對你通知該則 Plurk 的更新。

◆ 安裝 Firefox add-on Stylish,搭配 dogg 介紹的方法可以將 Karma 數字從畫面中隱藏起來。

◆ 廣告:如果按這裡註冊 Plurk 帳號,就相當於被我推薦,會自動變成我的 follower 而看到我的訊息。

◆ 噗海無邊,回頭是岸。

2008/06/23

Plurk



  這幾天試玩 Plurk,慢慢體會到它如何黏住使用者的道理。除了特殊的時間軸界面,Karma System 也是蠻重要的一個項目。Karma 的作用類似積分,使用者在系統中待得越久、有越多有價值發言、介紹越多人註冊,都會提升 Karma;反過來說,很久不上線、類似 spam 行為的灌水、申請將別人納入好友被拒等等(表示不受歡迎),則會減少 Karma。越高的 Karma 可以 unlock 越多個人化功能,所以也有人對此斤斤計較,甚至有 Karma Trend 之類的邊際系統出現。

  每一則發言稱為一則「Plurk」,而每則 Plurk 之前的 be 動詞/助動詞稱為「Qualifier」。Qualifier 具有多種顏色,方便快速地區分不同屬性的發言。由於 be 動詞助動詞都是英語系的東西,對中文使用者很難派上用場,前幾天我索性翻譯了一份繁中版寄給 Plurk 開發團隊。很快地對方有了回應,並且也將成品上線。這裡要給他們的高效率拍拍手,現在 Plurk 玩起來更親切了。

  有個特殊的翻譯順便在這裡解釋一下。對 Qualifier 中的「Share」,我翻譯了「分享」和「推」兩個版本,告訴他們說「分享」是精準的翻法,而「推」有「推薦」之意,但是會好玩得多,請他們自行決定。結果他們選擇了「推」,這表示他們挺有玩心的。

(後來如預料之中,「推」這個字引起鄉民的不小反應,哈。)

後記:我看到有人問說,「hate」何不翻成「恨」?呃,其實我本來想翻成台灣通用發語詞、鏗鏘有力的經典國罵「※」。但是這樣一翻,Plurk 可能會被列入十八禁網站,掙扎再三之下,最後選擇了「討厭」,走俏皮路線安全些。

廣告:如果按這裡註冊 Plurk 帳號,就相當於被我推薦,會自動變成我的 follower 而看到我的訊息。

2008/06/20

好書推薦



  這三本書的作者要出新書了,預定 6/25 上架。有興趣的人動作請快

2008/06/18

修螢幕


  買來整整四年的 BenQ FP737s 突然故障。開機電源燈會亮,但畫面一片黑。上網查到這是 BenQ LCD 螢幕的通病,過保維修至少要 $1500 起跳,光檢查不修也要 $300。

  由於之前 BenQ 到府收送維修的評價起伏很大,實在不放心,最後在露天拍賣找到一位專修 LCD 螢幕的賣家,檢測不用錢,維修收費也相當合理,就託他修了。結果一個晚上搞定,讓人十分滿意。同事有一台 FP756ms 有同樣毛病,見到我維修順利,今晚也抱去請那賣家幫忙啦。

2008/06/12

AVI 影片檔導致檔案總管 crash

  這問題困擾我很久了,但是原本只發生在公司的電腦。每當我用檔案總管開啟某個含有一堆 .avi 檔案的資料夾,什麼都不做,靜候約數秒鐘之後就會出現「explorer.exe…該記憶體不能為 "written"…」的錯誤訊息,然後檔案總管與開始工作列一起掛掉。本來我以為是當中某個 .avi 檔案造成的,而這個狀況只會發生在我用「詳細資料」檢視,或者有開啟狀態列的時候,所以只要在看影片之前改一下選項,撐過去也就算了。直到前天為了將影片轉檔而更新部份 codec,同樣的問題也開始發生在家裡的電腦,這下子就無法忍受了。

  到網路上搜尋,很快就發現一堆人遇過同樣的情形(…)。根本的解法是把有問題的 codec 處理掉,不過 K-Lite 跟其他影音剪輯軟體裝進電腦裡的檔案盤根錯結,要整理談何容易。所以用偷懶的解法,直接把檔案總管相關的預覽功能全部關掉最快。底下是步驟摘要:

一、下載 ShellExView,把「Avi Property Handler」disable 掉。
二、用登錄編輯程式打開「HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\」,如果底下有包含「DivX」的 key,把它刪掉。
三、打開「HKEY_CLASSES_ROOT\SystemFileAssociations\.avi\shellex\」,如果底下有一個「PropertyHandler」,把它內容清空。
四、重新開機。

  這樣修改之後,前述的問題就不會再發生了,同時檔案總管在詳細資料模式下也不會再去讀每個檔案的「時間長度」跟「維度」兩個垃圾資訊,一舉兩得。

2008/06/11

神秘的 Yahoo 站長工具

  我有一個開了五年的網站A,原先寄放於一位老大的 Sun Sparc Server 上。三年前為了測試 Webs-TV(後來跟 Yam 整併)的 blog 功能,在上面申請了一個帳號並把部份文章複製過去,後來就擱著當備份用,姑且稱為網站B。過去到各搜尋引擎輸入我的網站全名,第一筆結果都是A,第二筆是B。去年八月那台 Sparc 壽終正寢,我把整個站 ftp 到另一部主機東山再起。因為網址變了,我在搬遷後幾個月內分別到幾個搜尋引擎試過,剛開始A會指到舊主機,一陣子之後A消失剩下B,又一段時間後A擠下B重回第一位,並且已指向新的主機。各家搜尋引擎的結果差不多只是更新快慢不同而已。

  上個月,台灣 Yahoo 的「站長工具」問世。我收到一場發表會的邀請函,參加回來後也在網站A安裝其追蹤元件開始試用。就這麼擱了一週出頭左右,我留意到幾篇網路上的文章(這裡這裡這裡,雖然第三篇的作者是…呃…)。半信半疑到 Yahoo 一搜尋,網站A果然消失了,只剩下B。同一時間 GoogleMSN/Live Search 等網站查出來結果仍舊好好的,A第一B第二。所以說,目前 Yahoo 可能是唯一查不到網站A的搜尋引擎。(當然 powered by Yahoo 的 Hinet 跟 powered by Google 的 PChome 各依其結果)

  Yahoo 出了什麼問題呢?我不知道。不過我也不知道還有多少個網站因為這個神秘的 bug(?)而消失於 Yahoo 的搜尋結果之中,或是排名變到後面去。所以個人建議是:

一、還沒安裝 Yahoo 站長工具的人,最好再觀望看看,先不要急著裝。
二、在確定這個狀況是不是 bug 之前,Yahoo 的搜尋結果不知道還漏了多少東西,所以想要完整的結果,可以先嘗試別家搜尋引擎。

2008/06/10

日本旅館老闆娘的一席話

※我自己拍的糊掉了,所以借用同事費卡拍的照片:


其實剛回來台灣就想紀錄這件事,可是拖著拖著就一年過去了。

晚飯時,旅館老闆娘到現場跟大家打招呼。首先是感謝大家光臨,希望大家喜歡他們的服務,玩得愉快等等,接著她說了這段話:

「在日本,一群朋友或同事外出旅遊,大家一起吃飯,喝個酒、聊天、唱歌,晚上泡個湯,就覺得是很大的快樂。」

「應該要放鬆心情,好好地享受這段時光。」

「如果只是拚命地吃飯吃飯吃飯,吃飽匆匆忙忙地趕去這裡去那裡,這樣不是享受。」