jQuery 1.4 Solved The OnChange Event Issue of IE AutoComplete

IE's AutoComplete feature doesn't trigger onchange event is a well-known "by-design" behaviour:

Using AutoComplete in HTML Forms
To determine when a user updates the content of a field from the AutoComplete dialog box, use the onpropertychange event, rather than the onchange event, because the onchange event does not fire.

I have ever found some fixes (example 1, 2) developed by community before. Today, when I managed to put them into my project, I found something interesting.

As using jQuery 1.4+, if I set change() event on a <input type="text" />, it will be triggered as well as the event declared by onchange="..." when IE AutoComplete.  In the other word, jQuery 1.4 solved the "onchange not triggered" issue of IE AutoComplete.

After tracing the code roughly, I found jQuery uses some new mechanism: beforeactive event to store the original value in .data("_chage_data"), compares it with current value in focusout event, if not equal, then trigger the onchange event...  I guess that's why it can detect the value change when using IE AutoComplete.

So, using jQuery 1.4+, you can solved the well-known "No OnChange Event When IE AutoComplete" issue easily!

Here is my simple test case.  When jQuery 1.3.2 is included, no change event will be fired when using IE AutoComplete to fill t1, t2 value.   After changing the link to jQuery 1.4.2, you will find AutoComplete on t1 can trigger both "T1 JS change" and "T1 jQuery change" now.

<%@ Page Language="C#" AutoEventWireup="true" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type='text/javascript' 
src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js'></script>
<!--  <script type='text/javascript' 
src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js'></script> -->
    <script type="text/javascript">
        function output(msg) {
            var disp = document.getElementById("disp");
            disp.innerHTML += "<li>" + msg;
        }
        $(function() {
            //if you set change event on #t1 with jQuery 1.4.2,
            //the onchange event will be triggered when using AutoComplete
            $("#t1").change(function() {
                output("T1 jQuery change");
            });
        });
    </script>
</head>
<body>
<form method="post">
T1: <input type="text" id="t1" name="t1" onchange="output('T1 JS change');"/><br />
T2: <input type="text" id="t2" name="t2" onchange="output('T2 JS change');" /><br />
<input type="submit" value="Submit" />
</form>
<div id="disp"></div>
</body>
</html>

【中文摘要】

上回試著要用autocomplete="off"法解決IE自動完成時onchange事件不會被觸發的老問題,不過如同91在回饋裡所言--要關閉使用者慣用的自動完成功能,就得向User解釋這個難以啟齒的技術鳥問題。萬一不幸遇上"角頭級"的使用者,哪可能耐著性子聽你說什麼IE自動完成、onchange這些鬼東西,只怕會換來一句--"onchange是三小,我只聽過回家吃自己啦!"... 想到這個畫面,我忽然不寒而慄,決定花時間研究一下怎麼讓AutoComplete也觸發change事件。

開了個小專案寫了jQuery做測試,卻發現一件有趣的事。AutoComplete竟會觸發onchange事件~~~ 是我眼花了還是腦筋不清楚? 明明上回驗證過,像鐵一般的事實這回怎麼來了個急轉彎? 在做了一堆測試比對後,總算釐清一件事實:

使用jQuery 1.4設定<input type="text">的change()事件後,使用IE AutoComplete也會觸發onchange事件!!

我做了一個簡單的測試案例(程式碼在上方)來驗證這點: 首先使用jQuery-1.3.2,用IE自動完成填入t1, t2的資料,並不會觸發onchange事件;改用jQuery-1.4.2後,再以自動完成為t1, t2填入資料, 這回有加掛jQuery chnage()事件的t1會觸發用jQuery及用onchange="..."宣告的事件,而t2則不會。

粗略地追了一下程式碼,發現jQuery-1.4在change事件觸發機制上做了改良: 在beforeactive事件時會將原值存在.data("_chage_data")、在focusout事件中會用它跟現值比對,不一致時則觸發change事件。這跟我原本想出的解法差不多,而jQuery在核心中已實踐了類似的邏輯,應該就是無形中修好"AutoComplete不觸發OnChange事件"問題的原因。

總之,不費吹灰之力就修好這個惱人的問題,有賺到的感覺,哈!!

利用Windows Mobile模擬器測試網頁

上回介紹過獨立安裝Windows Mobile 6模擬器的方法,最終目標是想利用模擬器測試網站在行動裝置上的呈現與操作效果,今天繼續補完後續步驟。

首先,我們來讓模擬器可以"上網"。要讓模擬器連上Internet,透過VPC 2007的虛擬網卡驅動程式是一種方法,不過必須安裝VPC 2007,會有平台上的限制,所以像Windows 7、Windows 2007 R2就被發了好人卡;我個人偏愛的是另一種做法---透過Mobile Device Center轉接(就是ActiveSync在Vista之後進化版)。

[2010-03-08補充] 網友guest提供在Windows 7上獨立安裝模擬器所需Virtual Machine Network Services的密技,可以克服Win7上無法安裝VPC2007的問題,特此感謝!

為了讓模擬器連上Mobile Device Center,我們要先開放DMA連線(如下圖)。另外,網路選項要設成Automatic或The Internet,以允許模擬器連到Internet。有一個小撇步: 當發現模擬器無法連上Internet時(完全連不到或變成連上本機IIS),在Automatic/The Internet間做一次切換常能解決問題,我不知背後原理為何,大家就姑且當成"電視沒訊號時用力搥一下就會變好"那種密技吧!

設定好Mobile Device Center,接著在Device Emulator Manager啟動模擬器的"Cradle”功能,模擬實體手機/PDA用USB連上電腦的樣子。

接著Mobile Device Center就會偵測到模擬器連上線了(Connected),由於我們不需要同步模擬器的行事曆、連絡人,其他功能可以忽略不理。

如果沒有意外,此時在模擬器裡開啟IE,就可以開連上平日常逛網站,看看它們在手機上變成什麼鬼樣子德行囉! (密技提示: 發現無法連線時,記得用力拍一下電視機 XD)

另外,在智慧手機界,使用Opera Mobile或Opera Mini的使用者很多,所以理論上也該裝一套來玩玩。安裝Opera最快的方法是用直接用模擬器開IE連上http://m.opera.com/next,下載Cab檔案回來安裝就搞定了。

  

Mini C# Lab 1.4 Release

The primary new feature of Ver 1.4 is batch mode!

Now you can run Mini C# Lab program as a scheduled task, no UI interactivity is needed. Here are some batch argument examples:

  • Load and execute x:\temp\test.cs, use the output to overwrie x:\temp\log.txt
    MiniCSharpLab /batch /cs:x:\temp\test.cs /out:x:\temp\log.txt /overwrite
  • Load and execute "c:\some path\test.cs", append the output to LogyyyyMMdd.txt (the file name will be change according to System.DateTime.Now)
    MiniCSharpLab /batch /cs:""c:\some path\test.cs"" /out:Log{yyyyMMdd}.txt
  • Load and execute x:\aa.cs, append the output to x:\aa.txt, if execution time is longer than 1200 seconds, program will be terminated.
    MiniCSharpLab /batch /cs:x:\aa.cs /out:x:\aa.txt /timeout:1200
  • Load and execute x:\aa.vb as .NET 3.5 program, append the output to x:\aa.txt
    MiniCSharpLab /batch /vb:x:\aa.vb /out:x:\aa.txt /v35
There are also minor bug fix and improvement:
  1. Fix: Thread.Sleep for 0.5" after execution to avoid output loss (I hope it works)
  2. Add: Add execution duration display

【中文說明】

其實1.4版已經寫出來蠻久了,上回的黑暗盃程式魔人賽裡就已經偷跑過了。今天剛好跟同事提到要寫簡單的Script跑排程,索性一不做二不休,把它Release到CodePlex網站上,想參考程式的人也可以在CodePlex找到原始碼。

1.4版最大的特色是支援批次無UI的方式執行,這個之前網友Ammon也有提過,剛好也是我自己不少排程作業上的需求。執行參數如下:

  • 載入並執行x:\temp\test.cs, 輸出結果覆寫到x:\temp\log.txt
    MiniCSharpLab /batch /cs:x:\temp\test.cs /out:x:\temp\log.txt /overwrite
  • 載入並執行"c:\some path\test.cs", 輸入結果附加到LogyyyyMMdd.txt檔案後端(檔名會隨執行時間變化)
    MiniCSharpLab /batch /cs:""c:\some path\test.cs"" /out:Log{yyyyMMdd}.txt
  • 載入並執行x:\aa.cs, 輸出結果附加到x:\aa.txt。若執行時間超過1200秒,會被強制中止
    MiniCSharpLab /batch /cs:x:\aa.cs /out:x:\aa.txt /timeout:1200
  • 載入x:\aa.vb,視為.NET 3.5程式執行,輸出結果附加到x:\aa.txt
    MiniCSharpLab /batch /vb:x:\aa.vb /out:x:\aa.txt /v35

另外還有兩個小修改:

  • 修正: 加入0.5秒Thread.Sleep延遲,防止輸出結果遺失(希望有效)
  • 加入執行時間顯示功能
  • 如發現Bug或有功能建議,歡迎在此留言回饋。(但不一定能即時修正或擴充,請見諒!)

    CODE-鎖定有掛onchange事件的欄位停用自動完成

    上回提過因使用IE自動完成造成onchange事件不被觸發的問題,著實帶來一些困擾,不少User抱怨系統掛在onchange事件的欄位連動功能失效。雖然User漸漸都已學會在這些欄位使用手工輸入,不要依賴IE自動完成帶入結果就可以避開問題,抱怨日少。不過我想從網頁設定上去停用autocomplete還是比較根本的解決之道。

    當然,我們可以抱著寧可錯殺一百,不可錯放一個的精神來個$("input:text").attr("autocomplete", "off"),把網頁上所有<input type='text'>的自動完成功能全都閹掉,一了百了。不過,我習慣處理得細膩一點,將這個不得已措施的範圍縮小到受影響的欄位上,換句話說,只有掛了onchange事件的<input type='text'>才是要撲殺的對象。

    要怎麼偵錯欄位是否有掛onchange事件? 想起上回剛好玩過列舉元素已掛載的jQuery事件的小把戲,這回把它融合進來,就做出了這個"標靶治療式"的autocomplete屬性停用Plugin。

    以下的程式範例可以讓大家拿去Mini jQuery Lab實地玩一下。

    //針對有掛onchange的<input type="text">設定autocomplete="off"
    //參考: http://tinyurl.com/yzwp3lt
    //限制: 只對執行當下已經掛載onchange事件者有效(不包含.live()掛載者)
    $.fn.disableAutoCompleteForChange = function(force) {
        //過濾只留下<input type='text'>
        return this.filter(":text").each(function() {
            //篩選有掛change事件者, 參考: http://tinyurl.com/yka6x8g
            var evts = $.data(this, "events");
            if (force || evts && evts["change"] || this.onchange != undefined)
                $(this).attr("autocomplete", "off");
                //.val("SET"); //可加上這段測試是否被設定
        });
    }
    //加入三個<input>進行測試, T2用jQuery掛onchange, T3直接宣告onchange
    $("body").append("<input id='T1' /><input id='T2' />")
    .append("<input id='T3' onchange='alert(3);' />");
    $("#T2").change(function() { alert("2"); });
    $("input").disableAutoCompleteForChange();

    注意:

    1. .disableAutoCompleteForChange()的呼叫時機要在onchange掛上去後
    2. 對使用.live()掛載的onchange無效
    3. 另外多加對以<input onchange="..." />方式掛上事件的偵測
    4. .disableAutoCompleteForChange(true)時可不管有無onchange,一律停用
    [2010-03-08補充] 測試發現使用jQuery 1.4+掛載change()事件即可解決此問題
    獨立安裝Windows Mobile 6模擬器

    Visual Studio 2005/2008提供了整合式的Windows Mobile程式開發環境,其中包含PocketPC及Windows Mobile平台模擬器,可以在沒有實體手機/PDA的情況下執行、測試及偵錯程式。不過VS2008內附的模擬器只支援到Windows Mobile 5.0,如果想測試WM6,則需安裝Windows Mobile 6 Professional and Standard Software Development Kits Refresh (Standard / Profession分成兩包,視需要分別下載安裝) ,而要測試中文環境的話,則還要另外下載正體中文版的Windows Mobile 6 ROM Image,安裝完成後Device Emulator Manager就會自動多出WM6平台可供選擇,非常直覺簡單:


    圖1 從VS2008啟動Device Emulator Manager

     
    圖2 安裝WM6 SDK及正體中文WM6 Image後,會多出WM6平台選項

    手上有台跑Windows 7的ThinkPad T43,沒安裝VS2008(只有VS2010 RC),但我想在上面跑WM6模擬器,用WM6的IE瀏覽器測試網頁在行動裝置上的呈現效果。少了VS2008一手包辦自動裝到好的貼心協助,獨立安裝需要較多的手工。以下是我試出來的步驟:

    1. 下載安裝Microsoft Device Emulator 3.0 -- Standalone Release, C:\Program Files\Microsoft Device Emulator\1.0\dvcemumanager.exe就是圖1 Device Emulator Manager的執行程式,建議在桌面做個捷徑方便啟動。 (註: 模擬器使用與Virtual PC相似的核心技術,所以不能裝在VM OS上)
    2. 下載安裝正體中文版的Windows Mobile 6 ROM Image
    3. 此時執行Device Emulator Manager的結果可能會讓你失望,清單上空空如也,剛才新裝的WM6 Image並不會自動列出來(這種時候就會覺得Visual Studio就甘心A)。我們需要設法產生一個.decfg檔案,提供Emulator Manager足夠的資訊,才能順利啟動WM6模擬器。最簡單的方法是找台有裝VS2008的機器,在圖2介面上先選取模擬器,按右鍵用Save As產生.decfg檔再修改(多半只需調整檔案路徑)。
    4. 將上一步驟產生的.decfg檔案Copy到C:\Users\使用者名稱\Documents\My Device Emulators目錄下,重新啟動一次Device Emulator Manager,就可以看見新增的模擬器,按右鍵選Connect…

      薑! 薑! 薑! 薑~~~ WM6模擬器就跑起來囉!

    檢附上述的WM6ProCHT.decfg供參:

    <DeviceEmulator xmlns="http://schemas.microsoft.com/DeviceEmulator/2006/01/DeCfg">
        <Board>
            <CpuCore Reconfigurable="false">ARMv4</CpuCore>
            <CpuOptions Reconfigurable="false">TDI</CpuOptions>
            <VFP Reconfigurable="false">false</VFP>
            <OSImage Reconfigurable="false" Address="0">C:\Program Files\Windows Mobile 6 SDK\PocketPC\DeviceEmulation\0404\PPC_CHT_GSM_VR.BIN</OSImage>
            <MemSize Reconfigurable="false">128</MemSize>
        </Board>
        <Emulator>
            <DefaultSave Reconfigurable="false">true</DefaultSave>
            <FuncKey Reconfigurable="true">193</FuncKey>
            <HostKey Reconfigurable="true">Right-Alt</HostKey>
            <Language Reconfigurable="false">1028</Language>
            <Skin Reconfigurable="true" EnableSkin="true" ToolTips="off">C:\Program Files\Windows Mobile 6 SDK\PocketPC\DeviceEmulation\Pocket_PC_Phone\Pocket_PC_PE.xml</Skin>
            <VMID Reconfigurable="false">{984B347D-C615-4042-856D-EF567D2721C2}</VMID>
        </Emulator>
        <Peripherals>
            <SerialPort Reconfigurable="true" UARTNumber="0">none</SerialPort>
            <SerialPort Reconfigurable="true" UARTNumber="1">none</SerialPort>
            <SerialPort Reconfigurable="true" UARTNumber="2">none</SerialPort>
        </Peripherals>
        <Platform>Windows Mobile 6 Professional SDK</Platform>
    </DeviceEmulator>

    Posted 04 March 2010 08:32 AMJeffrey | no comments
    Filed under:
    開啟RSClientPrint偵錯Log

    Reporting Service線上列印功能可以直接由網頁列印出美美的報表,是很酷的功能,不過實務應用也遇過不少小狀態就是了。

    昨天無意中發現RSClientPrint元件支援寫Log檔的功能,這在射茶包時可是價值連城的情報。故特別PO文做個筆記以防年老失憶:

    1. 開啟Log記錄的做法是用RegEdit加入一則機碼(Key)HKEY_CURRENT_USER\Software\Microsoft\Microsoft SQL Server\80\Reporting Services
      並新增LogRSClientPrintInfo REG_DWORD = 0x00000001(可參考這篇KBMore Inforation一節的說明)
    2. 執行SSRS報表並使用線上列印
    3. DIR %temp%\LOG*.tmp,便可看到剛才列印動作所產生的Log檔案(檔名格式為LOGnnnn.tmp,nnnn為16進位隨機數字)。

    稍微看了一下,Log裡記載的資訊蠻詳細的,開啟報表路徑/名稱、使用語系、選擇的印表機、換頁、發出Request的過程都鉅細靡遺地被保留下來,很有Debug Log的味道,我想對於解決RSClientPrint疑難雜症應該很有幫忙。不過要看到這些Log,RSClientPrint元件至少要被順利載入並啟動才可以,RSClientPrint另一個常見的問題是因版本更新導致無法載入使用,這裡也順便提供一下參考資訊:

    【茶包射手日記】不限條件選項之SQL組裝與資料庫OR運算特性驗證

    在設計查詢介面時,下拉式選單是很常見的條件選取方式,而往往我們都會再加上一個"不限條件"選項,允許使用者不限制條件,列出所有項頁。舉例來說: 在設定查詢地區的下拉選單(例如: ddlArea),除了列出基隆市、台北市、台北縣、桃園縣...等各縣市Option外,常會再多一個"所有地區"選項,將Option Value設定='*',選取時表示不限定地區。在Server端,便要將這些篩選條件轉為SQL指令,如何將'*'視為不限地區? 我過去都用條件式動態組裝:

    if (ddlArea.Text != "*") {
        cmd.CommandText += " AND AreaCol = @pArea";
        cmd.Parameters.Add("@pArea", SqlDbType.NVarChar).Value = ddlArea.Text;
    }

    後來有一天開了竅,想到更簡潔的寫法:

    cmd.CommandText = @"
    SELECT .... FROM ...
    WHER ...其他查詢條件...
    AND (@pArea = '*' OR AreaCol = @pArea)
    ....";
    cmd.Parameters.Add("@pArea", SqlDbType.NVarChar).Value = ddlArea.Text;

    如此,整個查詢條件可以直接寫成靜態字串,便於閱讀修改,不必視狀況決定是否要傳入@pArea,而它一樣可達到傳入"*"就不設地區比對條件的效果。

    今天在噗浪上與噗友討論到類似議題,看到一種新寫法: where p1 = case @p1 when '*' then p1 else @p1 end,噗友提到曾經聽某資料庫老師說過,這種寫法比上述OR法效能更好,理由是在SQL指令中,OR 的兩邊條件都會做運算比較,跟一般程式的認知不同。(不過噗友對此理論也抱持懷疑態度就是了)

    基本上,我無法接受SQL指令中OR兩邊都會執行的主張。當今各家DB在效能優化上不遺餘力,每一個環節錙銖必較,怎麼可能留下這麼個大洞害人摔跤? 不過要怎麼驗證它倒是挺傷腦筋的。

    想到一個好辦法,用Block機制來突顯差別!! 故意在OR後方條件式中SELECT一個被Lock住的Table,接著故意讓OR前方條件為True,若SQL傻不楞登硬要執行OR後方條件式就會被擋下來,直到Lock被釋放為止(ROLLBACK TRAN/COMMIT TRAN);若SQL如我想的冰雪聰明,就會直接忽略OR後方條件立刻送回結果。由結果出現時機,便可判定OR後方的條件式是否被執行。

    實驗結果如下: (SQL 2008 @ Windows 2008 x64)

     

    在最下方的SQL指令區故意INSERT卻不COMMIT讓T2被Lock住,此時OR前方條件為1=1的SQL指令不受影響,OR前方為1=0的SQL指令才會執行到第二條件SELECT COUNT(ID) FROM T2而被Block住。由此可證: SQL Sever並不會像VB6一樣傻傻地檢查全部的AND/OR條件式,後方條件式執行與否會視前面的結果而定,故前述的OR法大家可安心服用。(這裡有另一篇討論也持相同看法: The query engine will take care of this for you. All operators will "short circuit" if they can.)

    MYTH BUSTED!!! [註]

    【黑暗水電工日記】假戲真做!

    樓梯間的天花板燈座年代久遠,塑膠逐漸脆化,前陣子更是開始座盤分離,接著就出現接觸不良的狀態。開燈後常得用雨傘尖輕推燈泡"喬"個幾下才能點亮,上週一,小燈座走到了生命盡頭,再也亮不起來。樓梯少了燈,晚上出入不便,身為黑暗水電工,面對這種險惡情境,當然責無旁貸要扛起拯救世界的重責大任! (遠目)

    平日下班都己晚上,樓梯間昏暗難以施工,於是趁著週末,先去附近的"專業水電材料行"(不是一般水電行,是專做"專業水電工"生意的水電材料批發店家,也是上回買到止水墊的地方)進貨買材料。材料行內成天都有"專業水電工"進出,用行話直接跟老板下單,像"LP4來50個"、"有六分管嗎"之類的,我雖然跟水電工前輩們一樣穿著排汗衫牛仔褲,但只要一開口說找"樓梯間天花板用的那種燈泡座",偽水電工的身份馬上穿幫。不過,水電材料行的所有商品都標了價,老板沒打算坑殺外行人(比起上回想賣我高檔汽車大燈燈泡的店家好多了),讓我買起來很安心。以下這兩個燈座,大家猜猜看多少錢?

    一個10元,很便宜吧!

    搬來木梯,抄起起子卸螺絲、拆舊燈座、裝新燈座、旋緊螺絲,三兩下便換裝完成。看來拆換燈座對我根本是小菜一碟,請叫我【黑暗水電工】!!

    完工後站在梯子上,等黑暗女王開關燈檢測功能是否正常。此時發生了一件震憾我幼小脆弱心靈的大事...

    樓下傳來鐵門開門聲,原來是住樓上的老先生回來了。木梯卡住狹小的樓梯轉角,要由梯子旁的狹小間隙側身穿過有點為難老人家,於是我趕緊將木梯收合讓出路來。老先生跟我點頭道謝繼續向上走,看到正在家門口控制電燈開關的黑暗女王,隨口說了一句:

    妳找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~找水電來換燈哦~~~~~~

    但見女王強忍笑意,努力克制不自主抖動的身體,回答老先生: "沒有啦,我先生看燈壞了,就想換一換..."

    老大爺,我說"叫我【黑暗水電工】"只是玩笑話,您怎麼當真啦!! 沒想到,我這個"偽.水電工"入戲太深,今天有排汗衫+牛仔褲的加持,全身已開始散放出濃濃的"水電工味",竟順利騙倒鄰居。真不知該為練成"二轉水電工"高興,還是該擔心這會對我繼續從事"專業資訊工作"形成隱憂?

    一切都是幻覺,嚇不倒我的。

    平復好受驚的心情,在此向大家介紹前陣子在光華商場挖到的寶--八合一折疊起子! 比起過去買過的換頭起子優秀許多,最大的好處是不需額外空間收納起子頭,而且起子前端一體成形跟標準起子一樣細長,不像抽換式起子前端的突出六角套筒座常會卡住螺絲孔,伸不進去轉螺絲。

    還有,起子前端的7顆LED燈在照明不足的場合超級好用,大推!!

    這麼順手俐落的兵刃,絕對是水電工作人員居家旅遊,不可或缺的良伴,只要100塊大洋,有空要去光華敗家的捧油們可以考慮帶一把回家。

    遲來一週的Acer P224W箱文

    【註: 文中有標明[p]的連結為噗浪訊息】

    故事要從2008年11月說起,家裡有一台LKK的三鳥牌17" LCD,在添購新的22"內建電視盒LCD後,高齡8歲的它在我公司的辦公桌覓得第二春,轉任工作機的第二螢幕。

    時間來到半年後,7月的一天早上,17" LCD忽然出現一條血紅垂直線[p]。當時,全黑桌面背景配上紅線,還真有Canon L鏡的高級fu呢! 讓我心生莫名的虛榮感[p]~~~

    可惜,代誌嘸像憨人想A哈尼甘單~~~ 兩個月後,第二條紅色線[p]冒出來了,噗友預言,線會愈來愈多,然後不久就會掛點。果不其然,第三條[p]、第四條[p]、第五條[p]陸續出現,還開始不定期出現消失加變色,我想這一定是傳說中稀有的隱藏版LCD--轉吧轉吧七彩霓虹線17"變色龍系列!!

    經過一段時間磨合,螢幕線條開始穩定,固定顯示三條。AskaSu提醒我,三條槓意義非凡,這台螢幕肯定是萬中選一、全球僅有,唯一掛上尉階的LCD,從此它博得"上尉LCD[p]"的稱號。

    雖然擁有一台掛階LCD挺酷的,但為了防止它無預警退役,我還是展開新LCD的Survey。一開始想衝1920*1080 FullHD解析度,一方面它是市場目前的主流,選擇多競爭大價格優,而超大的可使用桌面也有利於寫程式,只是有個問題--點距!

    點距指螢幕上點與點之間的距離。在解析度相同的前題下,用個比喻,就像一模一樣兩千字的文章,印成A4大小的點距會比縮小印成B5來得大,字看起來較大,閱讀較不費力。粗略的點距算法是 吋數 / (水平線數^2+垂直線數^2) ^ 0.5 / 2.54。[參考]

    以我原來的工作機螢幕組合來說,19" + 17",兩台都設成1280*1024解析度,而二者的點距就有段距離,以髮蒼蒼視茫茫中年人的標準,19"的點距(0.0294)看起來比17"(0.0263)舒適不少。若同樣是1920*1080 FullHD,22"的點距是0.0253,23"是0.0265,24"是0.0276,26"是0.0299,換句話說,要到我偏愛的1280*1024 19"點距水準,FullHD得買到26"才會符合,再不然至少也要24"。這會有兩個問題,1) 我的辦公桌硬要擠下24" or 26" + 19"的組合會猶如阿婆生子--很拼 2) 有點擔心24"/26"大尺寸LCD對近距離長期使用有點太大(一直從左到右轉頭,讀完長篇大論Word文件時不知會不會扭傷脖子 XD)

    最後我決定退而求其次選1680,尺寸也退到22"。有兩個選擇: 16:9 (1680*945)或16:10 (1680*1050)? 在辦公室裡鮮少會有看16:9寬螢幕電影的需求,而垂直多出105個pixel高度,足足可以放下三條狀態列,對總是擠滿各式工具小視窗的IDE開發環境來說彌足珍貴。

    確定22" 1680*1050的規格後,趁著年假逛了PCHome,發現22" 16:10的選擇並不多,不用在幾十台符合規格中猶豫不決,反而是好事一椿。篩選條件很簡單,一定要有DVI,有HDMI更好,一堆便宜的純D-Sub款式立刻出局,很快地就決定了Acer P224W(剛才發現現在標價比上週調高三百元,頓時又多了賺到的感覺,XD),這台螢幕有24h到貨服務,順便也讓我第一次體驗到"敗家於無形"的奧義--一時意亂情迷,頭腦還來不及清醒,小黑貓就已經叼著東西在門口等您...

    敗了家還沒圖沒真相是不被允許的,所以:

    講完了。

    什麼? 住在開封艾凱香小姐Call In提問,為什麼只有這樣,根本就沒開箱?

    咳,請大家仔細看標題,本來就說是"P224W箱文",並不是"P224W開箱文",到此為止也是合情合理。

    使用心得還是得提一下: 經一週工作使用實測,各方面感覺良好,這是台好螢幕!

    TIPS-Windows 7便利貼(自黏便箋)快速鍵

    Windows 7內建了桌面便利貼軟體,或許基於商標專利或其他考慮,名字有些繞口--自黏便箋

    相較於市場上其他便利貼軟體,它的功能有點陽春,介面也不怎麼順手。例如: 右鍵選單除了Copy/Paste跟換紙條底色外,找不到任何編輯文字內容的操作,也沒有工具列,一度讓我以為它只能當NotePad用。不過貼上去的內容倒是可以保留字型顏色,表示它的確支援RichText。爬了一下文,發現原來有密技! 雖然沒有按鈕工具列,但有一些快速鍵可以進行"簡單"的文字編輯操作。

    1. Ctrl + Shift + > : 字型變大
    2. Ctrl + Shift + < : 字型變小
    3. Ctrl + B : 粗體
    4. Ctrl + I : 斜體
    5. Ctrl + T : 刪字線
    6. Ctrl + U : 底線
    7. Ctrl + Shift + L : 按一次項目符號(Bulleted)
    8. Ctrl + Shift + L : 按兩次自動編號(Numbered)
    9. Ctrl + L : 靠左對齊
    10. Ctrl + C : 靠中對齊
    11. Ctrl + R : 靠右對齊

    很可惜,想改字型跟顏色,還是門都沒有。

    如果不介意它的功能過於陽春、介面過於簡陋,Windows 7 User倒可試試這個不用安裝的內建便利貼。不過,我個人把它定位成免費奉送的小菜,針對這類需求,微軟真正的大餐是--OneNote(OneNote是Office 2003/2007的家族成員之一,不過好像知名度並不高),操作順手、功能華麗,用過的人評價都蠻高的,有興趣的人可以試試。

    【不小心找到的其他技巧】

    POC-以jQuery實作會議室登記狀況顯示

    專案有個需求,要在網頁上呈現多間會議室一天的使用登記狀況。'傳統思維"可能會傾向用Table <td>模擬出時段區塊,然後將預約起迄時間範圍內的<td>用colspan併成一塊。不過如果要做到登記時間以10分鐘為單位,意味著每個小時要切割成6個<td>,即便沒有任何預約也是,讓Table充滿一堆無用又囉嗦的廢物Tag。加上計算哪幾個<td>要併在一起,得找出第一個<td>加colspan,並將後面<td>省略,演算法不怎麼單純。分析起來,這就是該用CSS取代Table做法實作Layout的經典情境。

    寫了一段jQuery程式做出POC(Proof of Concept)供同事參考,示範使用DIV+CSS達成上述效果,並並盡可能簡化為呼叫函數就可以完成。一魚兩吃,PO出來供大家參考,順便蒐集諸位先進達人們的回饋指教。

    //輸入起/迄整點、寬、高及是否顯示時間標題列,傳回顯示全日時段狀態的<div>
    function genTimeline(startHour, endHour, width, height, incHeader) {
        $.Darkthread.tools.addDefaultStyles(".dttl_BookingBlock", {
            ".dttl_BookingBlock": {
                float: "left", position: "absolute",
                margin: "0px", padding: "0px", height: "100%",
                "font-size": "9pt", "background-color": "#dddddd",
                "border-left": "solid #444444 1px",
                "border-right": "solid #444444 1px"
            },
            ".dttl_Timeline": {
                border: "solid black 1px", position: "relative"
            },
            ".dttl_TimelineHeader": {
                border: "solid black 1px",
                backgroundColor: "#dddddd",
                "font-size": "8pt", position: "relative"
            },
            ".dttl_SplitLine": {
                float: "left", position: "absolute",
                "border-left": "solid gray 1px", 
                width: "1px", height: "100%"
            }
        });
     
        var $tl = $("<div class='dttl_Timeline' />");
        $tl.height(height).width(width);
        var hourWidth = width / (endHour - startHour);
        var $h = $("<div />");
        if (incHeader)
            $h.width(width).height(15).addClass("dttl_TimelineHeader");
        for (var i = startHour; i < endHour; i++) {
            var vLine =
                "<div class='dttl_SplitLine' style='left:" +
                hourWidth * (i - startHour) + "px;' />";
            $tl.append(vLine);
            if (incHeader)
                $h.append("<div>" + i + "</div>").append(vLine);
        }
        if (incHeader) {
            $h.find("div").css({
                float: "left", margin: "0px", width: hourWidth + "px", 
                textAlign: "center"
            });
        }
        var $container = $("<div class='dttl_Container' />");
        $container.append($h).append($tl)
        .data("hourWidth", hourWidth)
        .data("startHour", startHour)
        .data("endHour", endHour);
        return $container;
    }
     
    //display會直接顯示在方格中,可支援HTML語法
    //detail以title="..."方式處理,請給純文字
    //cssOption可為css屬性物件,例如: { border:"...", color:"..." }或是className
    //會傳回<div> jQuery物件供後續應用
    function addBooking(timeline, startTime, endTime, display, detail, cssOption) {
     
        if (cssOption == undefined || typeof cssOption == "string") 
            cssOption = { };
     
        var $tl = $(timeline);
        var sh = $tl.data("startHour"), eh = $tl.data("endHour");
        //將時間先轉成小數
        var p = startTime.split(':');
        var st = parseInt(p[0]) + (parseFloat(p[1]) / 60);
        //小於startHour, 以startHour為準
        if (st < sh) st = sh;
        
        p = endTime.split(':');
        var et = parseInt(p[0]) + (parseFloat(p[1]) / 60);
        //大於endHour, 以endHour為準
        if (et > eh) et = eh;
     
        //算出起始位置及寬度
        var hw = $tl.data("hourWidth");
        var w = (et - st) * hw;
        var x = (st - sh) * hw;
     
        //加入顯示元素
        var $container = $tl.children("div:last");
        
        var $dv = $("<div class=\"dttl_BookingBlock\"/>");
        $dv.css(cssOption).css({ 
            left: x + "px", width: w + "px",
            opacity: 0.7
        });
     
        $dv.html("<span style='padding: 2px;'>" + display + "</span>")
        .attr("title", startTime + "-" + endTime + "\n" + detail);
        if (typeof cssOption == "string")
            $dv.addClass(cssOption);
     
        $container.append($dv);
        
        return $dv;
    }

    以下是應用範例:

    <script src="jquery-1.4.2.js" type="text/javascript"></script>
    <script src="jquery.darkthread.tools.js" type="text/javascript"></script>
    <script src="TimelineUtil.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function() {
            var $d = genTimeline(9, 21, 800, 30, true);
            $("body").append($d);
     
            var $booking;
            //truncText可幫忙取前10個字            
            var longText = "超長文字測試,利用truncDetail()取前10個字元";
            $booking = addBooking($d, "14:30", "17:00",
                $.Darkthread.tools.truncText(longText, 10), longText);
            //允許重疊顯示
            $booking = addBooking($d, "16:30", "20:00", "來亂的", "咬我呀!");
      //cssOption可指定CSS
            var cssOption = { backgroundColor: "green", color: "yellow" };
            $booking = addBooking($d, "09:30", "10:00", "Jeffrey", 
                                  "重要會議", cssOption);
     
            //cssOption傳入字串時,視為className
            $booking = addBooking($d, "10:30", "12:00", 
                                    "Meeting", "MouseOver特效", "booking");
            //addBooking會傳回<div>的jQuery物件,可進行加上hover/click事件等操作
            $booking.hover(function() {
                $(this).addClass("inverse");
            }, function() {
                $(this).removeClass("inverse");
            }).css("cursor", "pointer").click(function() {
                alert("Hi!");
            });
     
            //產生100條時間軸各有12筆登錄,測試速度
            var $tbl = $(
            "<table cellspacing='0' cellpadding='0' style='margin-top: 10px;' />");
            for (var i = 0; i < 100; i++) {
                $tr = $("<tr><td width='10%' /><td width='90%' /></tr>");
                $tr.find("td:first").text("Room" + i);
                var $timeline = genTimeline(9, 21, 800, 30, i == 0);
                $tr.find("td:last").append($timeline);
                $tbl.append($tr);
                for (var j = 9; j < 21; j++) {
                    $booking = addBooking($timeline, j + ":00", j + ":30", 
                        "Test-" + i + "-" + j, "Test");
                }
            }
            $("body").append($tbl);
        });
    </script>
    <style type="text/css">
        .booking
        {
            background-color: Green;
            color: Yellow;
        }
        div.inverse 
        {
            background-color: black;
            color: White;
        }
    </style>

    【註】只是POC,防呆、檢核及彈性都很薄弱,不足處請自理!

    黑暗jQuery工具包1-動態加入預設Style設定

    對我來說,Javascript在處理CSS上一直有一個小困擾...

    一般在習慣上,為了方便開發人員修改,多半會將Style設定獨立放在.css檔案裡,應用者可視需要覆寫或修改之。但有蠻多時候,應用者根本不打算更動CSS內容,只想直接沿用預設CSS設定,但部署時還是少不了要Copy相對應的.css檔案,網頁也必須多加上<style src="...">宣告。

    我總覺得,最理想的方式是將預設css與js融和在一個檔案裡。當開發者想自行定義CSS樣式,可將其寫在.css中加以引用,或在HTML中以<style>逕行宣告;若只想用預設內容,則只需參照.js就大功告成。

    今天遇到類似需求,索性寫了幾個小函數,試著實現上述想法。

    我的設計概念是這樣的: 先透過document.styleSheets.cssRules比對某個CSS Selector(例如: div.cCountryPicker),若已被定義過,表示網頁已透過參照.css檔或直接宣告<style type="text/css">方式定義了div.cCounterPicker,此時就不需加入預設的div.cCountryPicker樣式規則;反之,若沒有定義該規則,則透過$("head").append("<style type=\"text/css\">....")的方式加入Style宣告。

    程式碼如下:
    (PS: 我規劃用jQuery.Darkthread.*蒐集我陸續開發出來的工具函數,最前面的$.extend用來將多個.js中宣告的jQuery.Darkthread.*函數彙整在一起)

    /*
    ** Darkthread's Tools for jQuery ver 1.0 **
    by Jeffrey, 2010-02-25, http://blog.darkthread.net
    =======================================================
    checkStyle: to check if specific style selector is defined
    addStyles: add <style type="text/css"> dynamically
    addDefaultStyles: if specific style selector isn't defined, add style setting
     
    */
    jQuery.extend(jQuery, { Darkthread: {} });
    jQuery.extend(jQuery.Darkthread, {
        tools: {
            //ruleSelector sample: a:hover, .myClass, #someId
            checkStyle:
            function(ruleSelector) {
                //REF: http://www.javascriptkit.com/dhtmltutors/externalcss3.shtml
                var cssCol = document.styleSheets;
                ruleSelector = ruleSelector.toLowerCase();
                for (var i = 0; i < cssCol.length; i++) {
                    var rules = cssCol[i].cssRules || cssCol[i].rules;
                    for (j = 0; j < rules.length; j++)
                        if (rules[j].selectorText.toLowerCase() == ruleSelector)
                        return true;
                }
                return false;
            },
            /*styleDictionary sample:
            { 
            "a:hover": { "background-color": "red", "color":"yellow" },
            "#dvTest": { "border", "solid blue 1px" }
            }
            Note: it's for <style> declaration, not for jQuery.css, so don't use 
            backgourndColor instead of "background-color"
            */
            addStyles:
            function(styleDictionary) {
                var sb = [];
                for (var selector in styleDictionary) {
                    sb.push(selector + " {");
                    var props = styleDictionary[selector];
                    for (var p in props) {
                        sb.push(p + ":" + props[p] + ";");
                    }
                    sb.push("}");
                }
                $("head").append("<style type=\"text/css\">\n" +
                    sb.join("\n") + "\n</style>");
            },
            addDefaultStyles:
            function(ruleSelector, styleDictionary) {
                if (!$.Darkthread.tools.checkStyle(ruleSelector))
                    $.Darkthread.tools.addStyles(styleDictionary);
            }
        }
    });

    應用方式挺簡單的,將以下的程式寫進.js,等於將預設的.css一起藏進.js裡,就不必再多搞出一個預設用途的.css檔案囉!

        $.Darkthread.tools.addDefaultStyles(".dttl_BookingBlock", {
            ".dttl_BookingBlock": {
                float: "left", position: "absolute",
                margin: "0px", padding: "0px", height: "100%",
                "font-size": "9pt", "background-color": "#dddddd",
                "border-left": "solid #444444 1px",
                "border-right": "solid #444444 1px"
            },
            ".ddtl_Timeline": { border: "solid red 1px" }
        });

    (以上的程式碼,會在網頁本身沒有宣告.dttl_BookingBlock且所有參照的.css中也沒有該宣告時,加入.dttl_BookingBlock及.dttl_Timeline兩條CSS規則,賦與預設Style設定。)

    Outlook 2007無法預覽PDF附件

    Outlook 2007在x64平台上無法預覽信件的PDF附件檔,會出現:

    無法預覽此檔案,因為下列預覽器發生錯誤:
    PDF Preview Handler for Vista
    若要在本身所屬的程式中開啟此檔案,請按兩下檔案。

    找到這篇文章,才知道這是Adobe PDF Preview Handler的Bug,網友們已經抱怨了兩年多,Adobe還是沒處理,於是該文章的作者就自力救濟寫出修補程式,修改Registry裡錯誤的AppID後,PDF預覽器馬上就可以使用囉~~

    PS: 另外,該Fix還做了一個"32/64-bit thumbnail bridge",一併修正了x64下檔案總管檢視時無法產生PDF內容縮圖的問題(Adobe沒出x64版的縮圖產生器),真神人也!

    升級Win2008R2後Hyper-V VM無法啟動

    年前將作業系統由Windows 2008升級到Windows 2008 R2,已有年後要"打地鼠"的心理準備(每日一詞: 【打地鼠】意指針對疑難雜症或系統問題,採取見一個解一個,遇到再說的鄉愿策略),而今天打了第一隻地鼠...

    啟動Hyper-V VM時出現以Microsoft Emulated IDE Controller: Failed to Power on with Error 'The system cannot find the file specified.'錯誤,導致VM無法開機。

    本以為會很棘手,幸好在檢視VM設定時很快就發現可疑之處: Physical CD/DVD drive:選單出現Drive 'Not found',而下拉選單中另有一筆Drive 'D:'應該才是正確的選項。

    重設為Driver 'D:'後,VM即可順利啟動,而之後再開啟VM Setting,Driver 'Not found'選項也消失了,我的推測是升級R2後,DVD光碟機被賦與了新識別碼,而VM設定仍指向舊識別碼,導致上述問題,理論上重新設過就可解決囉。

    【茶包射手手札】比蝸牛還慢的HD

    前一代工作機XPC SB81P自從退休後,輾轉流落到客廳,擔任起"偽家電"兼娛樂PC的角色。(老讀者們可能己見過,它在這篇文章中曾露過臉,照片左側音箱上方,巴黎鐵塔下方,我想它能二次就業多少跟黑色鏡面長相脫不了關係,沒想到俊男美女較吃香的定律在硬體界也適用)

    前些時候,周遭的機器接連發生某廠牌HD集體暴斃的慘劇,它也在中鏢之列,便換了顆新硬碟WD 6400AAKS,順便把OS由XP換裝成Windows 7。這台機器我裝完機後幾乎未再碰過,平日多由黑暗女王及兩位王室小成員操作使用。在換HD升OS後,陸續接獲一些抱怨: 放YouTube影片聲音會斷斷續續、某些程式執行過程會停頓一下、跑大型遊戲程式時會當機...

    趁著年假時間較有空,便在客廳扮演一下茶包射手,著手修理這台不乖的電腦。

    實測播放YouTube影片,360p順暢,到480p起就偶有斷續雜音(感覺是CPU跟不上),720p更是慘不忍睹。有點狐疑,同一台機器,之前裝XP用得還不錯,即便機器老了點(Pentium-D 935 3.2GHz + 2G RAM) ,Windows 7對硬體要求稍高,但直覺上應該不至於連YouTube影片都跑不動。

    Vista/Windows 7起有所謂的效能分數檢測,是很不錯的初步評量。執行了一次,發現頗意外的結果,這顆新HD居然只有2.7分,是所有分數中最差的。啥? 最新買的裝置居然是整台機器的罩門,太不合理了!

    X光照出異常,接著就該做斷層掃瞄了。找來HD專屬的測速工具--HD Tune,診斷報告一出,我花容失色大吃一驚!!

    嚇! 3MB/s!! 大家評評理,SATA HD都在拼100MB/s的時代,這種速度能看嗎能看嗎?

    經過一番嘗試,發現如果我把BIOS裡的On-Chip Serial ATA Setting/SATA Mode設定由AHCI調回IDE,HD速度就會改善,一舉飆上86MB/s,才是WD 6400AAKS合理的效能表現。

    為何AHCI模式會導致如此不堪的HD效能數字,噗友鐵衛在噗浪提供了一些情資,不過似乎於我的狀況不太相同,由HD Tune資訊顯示,HD狀態為UDMA Mode 5。但回想起來,因為XP要換AHCI工程很大,所以AHCI倒真是安裝Windows 7時才調過來的,跟遇到HD效能問題的時點相當,坐實HD問題與AHCI設定有關的推論。

    雖然未知AHCI模式導致HD效能低落的真實原因,但畢竟這是台高壽五年的老機器,若是追到最後,問題出在BIOS卻又不可能再有Update,將情何以堪? 於是我很果斷地決定"面對它、接受它、處理它(調成IDE不就好了)、放下它",啦啦啦啦~~~

    在福虎開春之際,茶包射手就用馬馬虎虎的結案態度應景一下(喂!),順道給大家拜個晚年囉!

    更多文章 下一頁 »

    搜尋

    Go

    <March 2010>
    SunMonTueWedThuFriSat
    28123456
    78910111213
    14151617181920
    21222324252627
    28293031123
    45678910
     
    RSS
    【工商服務】
    最新回應

    Tags 分類檢視
    關於作者

    一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

    文章典藏
    其他功能

    這個部落格


    BlogLook Score and Rank

    Syndication