Loading blog entries.. loading


Blogs which referenced tag: [Coding]

 



Resolving mouseover/mouseout multi-trigger problem

 
Written by Wayne Ye  Friday, December 23, 2011

Background

While I was developing my blog's comments function, I hope reader can write down their Gravatar email address, and my preferable UI was an "hint icon" dispolayed desides the Email textbox, so that while reader mouse overs it an overlay box will show the reason why Email is required, in additional, I want to add a little bit fancier animation:

  • When user's mouse hovers on the icon, show the div box from small to large, once reaches predefined size, fades in the hint sentente.
  • When mouse out the icon, fades out the hint sentence and then hides the div from large to small.

My requirement seems easy, however, I did spend two whole days in making it perfect, so I write down my investigation path here. 

The problems I faced

My first thoughts was definitely simple, subscribe the onmouseover/onmouseout event for the hint icon, and setinterval the size animation, once finished, fades in the texts.

That's fine! But I soon encountered one problem which is easy to trigger: mouse keeps on overing/outing the icon quickly (as long as the interval is shorter than the sizing animation), it will cause the two event handlers (mouseover/mouseout) multi-triggered so that a infinite loop could be easily caused.

The multi-trigger problem I faced is not the one related with DOM event bubbling, which is well described in jQuery mouseover API.

Majority functions on my simple blog didn't use any third party JS library including jQuery, instead, I was trying to be a "rambo", just for improving my coding skills - by implementing various kinds of features and resolving various kinds of problems.

Overcoming this issue cost me one whole day:) I tried strategies listed below:

  1. Put global variables indicating the showing/hiding process - isShowing & isHiding.
  2. Clearing oppositeinterval handle as soon as over/out event was triggered.
  3. Inspired by #2, unsubscribing oppotite event handler as soon as mouseover/mouseout was captured.

#1 does not solve the problem, I tried while multi-triggered, the interval cannot be cleared (not very sure the reason), #2 absolutely not work because it will stop unfinished animation. Finally I resolved the problem using #3, which I think it is the best one. 

Implementation

The HTML

 <div id="hintContainer">
    <div id="hintIcon">
        <img src="../Images/Hint.gif" alt="Email Hint">
    </div>
    <div id="CommentHint">
        <div id="hintText">
            Required (not shown), used only for displaying <strong>Gravatar</strong> and receiving
            future notification when new comment(s) posted on this blog.
        </div>
    </div>
</div>

The CSS

 <style type="text/css">
    #hintContainer
    {
        width: 184px;
        height: 80px;
    }
    #hintIcon
    {
        width: 16px;
        height: 16px;
        float: left;
        cursor: pointer;
    }
    #CommentHint
    {
        position:absolute;
        color: #fff;
        width: 0;
        height: 0;
        background-color: black; /*#adc7ed*/
        border: 1px dotted #666;
        padding: 2px;
        display: none;
    }
    #hintText
    {
        opacity: 0;
        filter: Alpha(opacity=0);
    }
</style>

The JavaScript

Foundation methods in my "rambo" library:

 var $id = function (id) { return document.getElementById(id); }
function bindEvent(domObj, eventName, evtHandler, useCapture) {
    if (domObj.addEventListener) {
        domObj.addEventListener(eventName, evtHandler, useCapture ? useCapture : false);
    }
    else if (domObj.attachEvent) { // IE 8 and below
        evtHandler.wrapper = function () {
            evtHandler.call(domObj)
        };

        domObj.attachEvent("on" + eventName, evtHandler.wrapper);
    }
}
function setSize(ele, width, height) {
    ele.style.width = width + 'px';
    ele.style.height = height + 'px';
}
function setPosition(ele, left, top) {
    ele.style.left = left + 'px';
    ele.style.top = top + 'px';
}

Animation methods:

 var hintIcon = $id('hintIcon'), commentHint = $id('CommentHint'), hintText = $id('hintText');
bindEvent(hintIcon, "mouseover", showHint);

var totalFrames = 20, curFrame = 0;
var sliceWidth = 180 / totalFrames, sliceHeight = 80 / totalFrames;
var showHinthandler = hideHinthandler = undefined;
var offset = 10;

function showHint(evt) {
    if (!evt)
        evt = window.event;

    // Unbind mouseover immediately to prevent multi-trigger
    unbindEvent(this, "mouseover", showHint);

    setPosition(commentHint, evt.clientX + offset, evt.clientY);
           
    showElement(commentHint);
    showElement(hintText);

    showHinthandler = setInterval(function () {
        //console.log("show: " + curFrame);

        if (++curFrame <= totalFrames)
            setSize(commentHint, sliceWidth * curFrame, sliceHeight * curFrame);
        else {
            clearInterval(showHinthandler);
            curFrame = totalFrames;

            // Fade in hint text
            divFadeIn(hintText, 2, 10);

            // Bind mouseout event back to the icon UNTIL finished showing
            bindEvent(hintIcon, "mouseout", hideHint);
        }
    }, 10);
}
function hideHint(evt) {
    if (!evt)
        evt = window.event;

    // Unbind mouseover immediately to prevent multi-trigger
    unbindEvent(this, "mouseout", hideHint);

    divFadeOut(hintText, 2, 10);

    setTimeout(function () {
        hideHinthandler = setInterval(function () {

            hideElement(hintText);
            console.log("hide: " + curFrame);


            if (--curFrame >= 0) {
                setSize(commentHint, sliceWidth * curFrame, sliceHeight * curFrame);
            }
            else {
                clearInterval(hideHinthandler);
                curFrame = 0;

                hideElement(commentHint);

                // Bind mouseover event back to the icon UNTIL finished hiding
                bindEvent(hintIcon, "mouseover", showHint);
            }
        }, 10);
    }, 800);
}

Conclusion

I guess this is a pretty good problem for interview, I am fool so I cannot resolve it in short period, however, I think good fronteers should solve it greatly:)


View Post»



Permalink:http://wayneye.com/Blog/Resolving-mouseover-mouseout-Multi-Trigger-Problem
Tag:

Modify proxy settings using Powershell

 
Written by Wayne Ye  Thursday, December 8, 2011

To use Live Messenger in the office, I always need to firstly disable my proxy settings, after Live Messenger launched, I need turn it back. Thus everytime I need to change that from "Internet Options -> Connections -> Lan Settings ->", check/uncheck the checkboxes, well, this task I need to do at least one time a day, I as a developer should achieve this in a more efficient and convenient way.

By overcoming one problem I wrote a snippet of Powershell cmdlet which does the job perfectly, the steps are:

  1. Update proxy settings to "Automatically detect settings".
  2. Launch Live Messenger.
  3. Update proxy settings to use proxy.

 The "one problem" I encountered and mentioned above is:

Where are the registry key(s) to store "Automatical configuration" section in Lan Settings.

I firstly tried to use Process Monitor by applying filter to only monitor my IE's process PID, while I did see it updated a number of places (see result outputed by procmon below), some of them are easy to be recognized such as "ProxyEnable", "ProxyOverride", etc, while I didn't see a obvious palce to store "Automatically detect settings".

 4:54:31.9881707 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\DefaultConnectionSettings    SUCCESS    Type: REG_BINARY, Length: 385, Data: 46 00 00 00 3D 00 00 00 09 00 00 00 15 00 00 00
4:54:31.9883681 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:31.9884340 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyServer    SUCCESS    Type: REG_SZ, Length: 44, Data: proxy.aus.hp.com:8080
4:54:31.9884568 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyOverride    SUCCESS    Type: REG_SZ, Length: 60, Data: dummyA.com;dummyB.com;<local>
4:54:31.9886357 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\SavedLegacySettings    SUCCESS    Type: REG_BINARY, Length: 385, Data: 46 00 00 00 B5 02 00 00 09 00 00 00 15 00 00 00
4:54:31.9899406 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:31.9899612 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyServer    SUCCESS    Type: REG_SZ, Length: 44, Data: proxy.aus.hp.com:8080
4:54:31.9899892 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyOverride    SUCCESS    Type: REG_SZ, Length: 60, Data: dummyA.com;dummyB.com;<local>
4:54:31.9901306 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\SavedLegacySettings    SUCCESS    Type: REG_BINARY, Length: 385, Data: 46 00 00 00 B6 02 00 00 09 00 00 00 15 00 00 00
4:54:32.0357094 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad\{65249C59-E027-4F0B-83FF-27DBB0B4E19E}_{B9E1413C-BF8C-444E-8FD0-105D9DD10F49}\WpadDecisionReason    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:32.0358754 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad\{65249C59-E027-4F0B-83FF-27DBB0B4E19E}_{B9E1413C-BF8C-444E-8FD0-105D9DD10F49}\WpadDecisionTime    SUCCESS    Type: REG_BINARY, Length: 8, Data: 60 20 7E 00 87 B5 CC 01
4:54:32.0360316 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad\{65249C59-E027-4F0B-83FF-27DBB0B4E19E}_{B9E1413C-BF8C-444E-8FD0-105D9DD10F49}\WpadDecision    SUCCESS    Type: REG_DWORD, Length: 4, Data: 1
4:54:32.0362007 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad\{65249C59-E027-4F0B-83FF-27DBB0B4E19E}_{B9E1413C-BF8C-444E-8FD0-105D9DD10F49}\WpadNetworkName    SUCCESS    Type: REG_SZ, Length: 24, Data: cpqcorp.net
4:54:32.5961530 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EnableAutodial    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:32.5961684 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\NoNetAutodial    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:32.5969457 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:32.5969722 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyServer    SUCCESS    Type: REG_SZ, Length: 44, Data: proxy.aus.hp.com:8080
4:54:32.5969907 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyOverride    SUCCESS    Type: REG_SZ, Length: 60, Data: dummyA.com;dummyB.com;<local>
4:54:32.5971154 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\SavedLegacySettings    SUCCESS    Type: REG_BINARY, Length: 385, Data: 46 00 00 00 B7 02 00 00 09 00 00 00 15 00 00 00
4:54:32.9694553 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\DefaultConnectionSettings    SUCCESS    Type: REG_BINARY, Length: 641, Data: 46 00 00 00 3E 00 00 00 09 00 00 00 15 00 00 00
4:54:32.9696389 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad\WpadLastNetwork    SUCCESS    Type: REG_SZ, Length: 156, Data: {65249C59-E027-4F0B-83FF-27DBB0B4E19E}_{B9E1413C-BF8C-444E-8FD0-105D9DD10F49}
4:54:39.7267907 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable    SUCCESS    Type: REG_DWORD, Length: 4, Data: 0
4:54:39.7268135 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyServer    SUCCESS    Type: REG_SZ, Length: 44, Data: proxy.aus.hp.com:8080
4:54:39.7268366 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyOverride    SUCCESS    Type: REG_SZ, Length: 60, Data: dummyA.com;dummyB.com;<local>
4:54:39.7270232 PM    iexplore.exe    2992    RegSetValue    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\SavedLegacySettings    SUCCESS    Type: REG_BINARY, Length: 641, Data: 46 00 00 00 BE 02 00 00 09 00 00 00 15 00 00 00

By a short time googling I found the answer on the awesome StackOverFlow, the "Automatical configuration" setting is stored in:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\DefaultConnectionSettings

And the tricky part is, it is a Reg_Binary value, and the 9th binary number indicates the value of the two checkboxes, that is:

01 - neither of those 2 auto config boxes are checked

05 - just the Use automatic configuration script is checked

09 - just the Automatically detect settings is checked

0d - both of them are checked

 By knowing this I am ready to write my Cmdlet code, I pasted below:

 # 1.Modify proxy settings to "Automatically detect settings". i.e. Disable proxy
CD HKCU:\"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
Set-Itemproperty . DefaultConnectionSettings -Value ([byte[]](0x46,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x70,0x72,0x6F,0x78,0x79,0x2E,0x61,0x75,0x73,0x2E,0x68,0x70,0x2E,0x63,0x6F,0x6D,0x3A,0x38,0x30,0x38,0x30,0x1D,0x00,0x00,0x00,0x64,0x75,0x6D,0x6D,0x79,0x41,0x2E,0x63,0x6F,0x6D,0x3B,0x64,0x75,0x6D,0x6D,0x79,0x42,0x2E,0x63,0x6F,0x6D,0x3B,0x3C,0x6C,0x6F,0x63,0x61,0x6C,0x3E,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x61,0x75,0x74,0x6F,0x63,0x61,0x63,0x68,0x65,0x2E,0x68,0x70,0x2E,0x63,0x6F,0x6D,0xA0,0x9D,0xC6,0x4C,0x6B,0xB5,0xCC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x02,0x10,0xBB,0x56,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0xBB,0x56,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0xBB,0x56,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00))

CD HKCU:\"Software\Microsoft\Windows\CurrentVersion\Internet Settings"
Set-ItemProperty . ProxyEnable 0

# 2.Launch Live Messenger
Start-Process "C:\Program Files (x86)\Windows Live\Messenger\msnmsgr.exe"

# 3.Sleep 10 seconds to allow live messenger signed in
Start-Sleep -s 10

# 4.Restore proxy back
CD HKCU:\"Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
Set-Itemproperty . DefaultConnectionSettings -Value ([byte[]](0x46,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x70,0x72,0x6F,0x78,0x79,0x2E,0x61,0x75,0x73,0x2E,0x68,0x70,0x2E,0x63,0x6F,0x6D,0x3A,0x38,0x30,0x38,0x30,0x1D,0x00,0x00,0x00,0x64,0x75,0x6D,0x6D,0x79,0x41,0x2E,0x63,0x6F,0x6D,0x3B,0x64,0x75,0x6D,0x6D,0x79,0x42,0x2E,0x63,0x6F,0x6D,0x3B,0x3C,0x6C,0x6F,0x63,0x61,0x6C,0x3E,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x61,0x75,0x74,0x6F,0x63,0x61,0x63,0x68,0x65,0x2E,0x68,0x70,0x2E,0x63,0x6F,0x6D,0xA0,0x9D,0xC6,0x4C,0x6B,0xB5,0xCC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x02,0x10,0xBB,0x56,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0xBB,0x56,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0xBB,0x56,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00))

CD HKCU:\"Software\Microsoft\Windows\CurrentVersion\Internet Settings"
Set-ItemProperty . ProxyEnable 1

C:
Echo Finished
Exit

Well, I put the sccript file on my desktop and named "Launch Live Messenger.ps1", every time I can simplt right click on it and select "Run with Powershell".


View Post»



Permalink:http://wayneye.com/Blog/Modify-Proxy-Settings-Using-Powershell
Tag:

Windows 8 - Far from perfect

 
Written by Wayne Ye  Thursday, December 1, 2011

Recently I spent a few days in investigating Metro App (AKA immersive UI) development, I downloaded & installed VS 2011 Ultimate, watched couple of videos talking about WinRT, Metro App life cycle, Living tiles & Notifications, etc. So far, I've encountered many many issues... I had to say: Windows 8 - Long Way to goSmile.

Below are few examples.

Metro App failed to got suspended

This happened when Ben Srour was doing the demo at Build conference, I encountered, too. His walking arround was killing explorer process and relaunch it manually, I copied that and my app successfully got suspended (applauding...).

Suspending issue

From a blackbox point of view, I guess this might be a bug: explorer process failed to suspend metro apps that deployed after process.exe itself.

Obvious library code defect

Code snippet below is copied from WinJS.IOHelper.existsFar from - MSDN link

 exists: function (fileName) {
    return this.folder.getFileAsync(fileName).
        then(
            function () { return true; },
            function () { return false; }
        );
},

What happend? When I call var fileExisted = IOHelper.exists("FileName.extention"); and the given file does not exists, exception will be thrown inside exists method's inside -> getFileAsync, in which essentially grabs a thread from ThreadPool and does an async IO, FileNotFound exception was thrown at that thread and can be caught by VS debugger, however Metro app will not got affected. This drove me crazy and so far I didn't find a walk arround, I think this is a bug in wwaapp.js version 0.5.

Inconsistent between code and documentaiton

In the Dev Center documentation it clearly claims how to specify your own rules inside ApplicationContentUriRules node.

 <?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">

  <!-- Code has been omitted. -->

  <Applications>
    <Application Id="MyApp" StartPage="default.html">
      <VisualElements>
      </VisualElements>

      <ApplicationContentUriRules>
          <Rule Match="http://*.bing.com/*" Type="include"/>
      </ApplicationContentUriRules>

    </Application>
  </Applications>

</Package>

Whereas when I tried that I saw below (click to enlarge):
URIRules 

Living tiles does not got updated

This happened in Kip Olson's demo at build conference, it was embarrassing. for me, I haven't found a way to get it work so far.

wwahost.exe hang/refused to work

This happened many timesSmile... Every time it refused to work, many metro apps cannot runany more.

To be continued

I want to say, those are not my complaints, if I don't like it, I won't wast my time on it. The truth is, Windows 8 will be the most revolutionary OS from Microsoft after Windows 95, it will change everything. I hope so and I believe in it! However, again, there is long way to goSmile.


View Post»



Permalink:http://wayneye.com/Blog/Windows-8-Far-From-Perfect
Tag:

Honored to Win CodeProject HTML5 competition

 
Written by Wayne Ye  Monday, November 14, 2011

Three months ago, my article <HTML5 WebSocket In Essence> was honored to be named first prize in CodeProject HTML5 & CSS3 competion, the award was an Ipad2. To be very honest I was expecting to win a T-Shirt or a CP Mug, if I could get either on I would be really excited, so when I received the email congratulating me I was the winner, you can image how excited and happy I was at that timeSmile.

However, the editor from CodeProject told me they were unable to ship the Ipad2 to China, they could give me equivalent money - $499.99, however after a very short consideration I decided to take the Ipad, absolutely!

So eventually, today, thanks to my co-workers in Cupertino, I got it, below are photos taken from my Sentation:

First look

First look

The front

The front

The back

The back

The memorable words

The memorable words

There is another "award" in fact, now if I google with keywords: “html5 websocket”, my article appears second resultSmile.

 


View Post»



Permalink:http://wayneye.com/Blog/Honored-To-Win-CodeProject-HTML5-Competition
Tag:

Make your HTML5 Video support all browsers

 
Written by Wayne Ye  Monday, November 7, 2011

Introduction

HTML5 video tag gave us the following advantages:

  1. No plugin required, directly play.
  2. Visible & controllable for Browser and search engine, not a "black box" such as Flash or SilverLight.
  3. Cross-platform, in theory all PC/mobile browsers as well as Ipad/Windows 8/Android tablet PC. 
  4. Downward compatibility. Which is very important, but W3C had made it simple and works, we can smoothly fall back to flash if the user agent does not recognize "<video>" tag.

So it is simple and pretty cool! However, there is still issues within a not short period: the browser support - each concrete browser and the company (ies) behind it supports different formant of videos, to be more specific, refer the table below stole from about.com:

 AndroidChromeFirefoxInternet ExploreriOSOperaSafari
Codec WinMacWinMacWin WinMacWinMac
MP4 or H.264 3.0 9 7 X X 9 3 X X 5 5
ogg/Theora 2.3 9 7 3.6 3.6 X X 10.63 10.63 X X
WebM 2.3 9 7 3.6 3.6 9 (withcomponents) X 10.63 10.63 X X

So to support all browsers: IE 6.0+, Firefox, Chrome, Safari, Opera, etc, what should we do?

Cross All Browser Implementation

By searching on the google and half a day investigation, I found the solution, sample code pasted below:

 <video controls="controls" preload="auto" poster="napshot.png">
    <source src="My Video.mp4" type="video/mp4" />
    <source src="My Video.webm" type="video/webm" />
    <source src="My Video.ogv" type="video/ogg" />
    <!--
        Flash
    -->
</video>

The code above results in IE 9.0+/Safari will load mp4, Chrome/Firefox and Opera will load webm, and IE 6.0 ~ 8.0 will load flash.

Note: as browsers may change its attitude on the video format support, so my code & comments might not right in the future.  

Few tips


View Post»



Permalink:http://wayneye.com/Blog/Make-Your-HTML5-Video-Support-ALL-Browsers
Tag:

Atomic Operation in C#.Net

 
Written by Wayne Ye  Tuesday, October 25, 2011

Introduction

Atomic Operation is named academic to Linearizability, Atomicity is a guarantee of isolation from concurrent processes, it can be enfored by hardware level build on Cache Coherence protocol, or software level exclusive lock. In this blog post, I am going to explore a few number of mechanisms to achieve atomic operation in .Net.

What are Atomic operations and what are not?

In C# Specification, the stamement about atomic operation is:

“Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.” Also: “…there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.”.

Threading In C# by Joseph Albahari described:

"Read/write on a field of 32-bit or less is always atomic, operations on 64-bit are guaranteed to be atomic only in 64-bit OS, statements that combine more than one read/write operation are never atomic."

For example, following operations are guaranteed to be atomic operations:

 int i = 3; // Always atomic
long l = Int64.MaxValue; // Atomic in 64-bit enviroment, non-atomic on 32-bit environment

Code like below are never atomic:

 int i = 0;
int j += i// Non-atomic, read and write operation
i++;         // Non-atomic, read and write operation

And I am trying to document atomic operation in a lower level in the section below.

Essence

Comsidering two threads (or two processes) are running simultaneously: T1 and T2, there is a field stored in memory, T1 reads its value and do some calculation on the value and finally write the new value back to memory, during the period T2 was actually doing exact same task - i.e. read/calculate/write value back, so possibly one operation on this field overrides another - in other word: the later executed thread (T2) might override the earlier executed one (T1) because when it read the field's value another thread was just manipulating on it, and after T1 finished writing the new value back to memory, T2 writes back.

So a simple example is the increament/decrement operation, as I showed above, it is NOT an atomic operation, it requires both read and write, if many thread simultaneously doing increment on one field, it will very possible cause race condition (the more threads, the more increment operation going to do, the more possibility race condition happen).

Atomicity example

I wrote a Winform application, it do simple work:

Create 10 threads at runtime and simultaneously operates on a private interger, there is a volatile counter with initial value 0, every thread finishes its work will:

  1. Print information on UI.
  2. Increment the counter
  3. Check whether the counter reachs 10, if yes, print CalculatingFinished method which will print the final result.

And I wish UI won't block during the calculation, otherwise I can simply join every created thread. My code skeleton is showing below:

 private const int MaxThraedCount = 10;
private Thread[] m_Workers = new Thread[MaxThraedCount];
private volatile int m_Counter = 0;
private Int32 x = 0;

protected void btn_DoWork_Click(object sender, EventArgs e)
{
    ShowStatus("Starting...");

    for (int i = 0; i < MaxThraedCount; i++)
    {
        m_Workers[i] = new Thread(IncreaseNumber) { Name = "Thread " + (i + 1) };
        m_Workers[i].Start();
    }
}

void IncreaseNumber()
{
    try
    {
        for (int i = 0; i < 10000; i++)
        {
            // Different strategy to increment x
        }

        ShowStatus(String.Format("{0} finished at {1}", Thread.CurrentThread.Name, m_Watcher.Elapsed.TotalMilliseconds));
       
        // Increases Counter and decides whether or not sets the finish signal
        m_Counter++;
        if (m_Counter == MaxThraedCount)
        {
            // Print finish information on UI
            CalculatingFinished();
            m_Counter = 0;
        }
    }
    catch (Exception ex)
    {
        throw;
    }
}

public void ShowStatus(string info)
{
    this.InvokeAction(() => listInfo.Items.Add(info));
}

private void CalculatingFinished()
{
    ShowStatus("\r\nAll Done at: " + m_Watcher.Elapsed.TotalMilliseconds);
    ShowStatus("The result: " + x.ToString());
}

I highlighted "// Different strategy to increment x", and will try a few number of ways to achieve atomicity using FCL libriries.

Let's first see the non-atomic routine - simple do x++ in each thread: 

 for (int i = 0; i < 10000; i++)
{
    x++;
}

Since x++ is NOT atomicity, in additional, I made a large loop - 10 thousand times, in my experience, I NEVER get the correct result, the screenshot is below:

Non-Atomic Operation

Analysis and solutions

To solve the issue and ensure atomicity, by more than 2 weeks brokenly study I found the following feasible ways (in theory, not considering performance):

  1. Interlocked.Increment
  2. Apply exclusive lock (or Moniter.Enter) outside the for loop.
  3. AutoResetEvent to ensure threads doing task one by one. 
  4. Create a temp integer in each thread and once finish add temp onto x under an exclusive lock.
  5. ReaderWriterLockSlim.
  6. Parallel.For coordinate with Interlocked.Increment. 

All of above can achieve atomic operation on incresing x's value, and got expected result:

Atomic operation

In fact I did try other ways such as using MemoryBarrier, Thread.VolatileRead/VolatileWrite - StackOverFlow question link, but failed, if dear readers know there is way to use them to achieve the goal please kindly guide me.  

Demonstration code

In this section I will listed key code for implementing the 5 solutions above.

Solution #1: Interlocked.Increment

 for (int i = 0; i < 10000; i++)
    Interlocked.Increment(ref x);

Solution #2: Apply exclusive lock (Moniter) outside the for loop.

 private readonly string m_Locker = "THREAD_LOCKER";

Monitor.Enter(m_Locker);
for (int i = 0; i < 10000; i++)
    x++;
Monitor.Exit(m_Locker);

Solution #3: AutoResetEvent to ensure threads doing task one by one.

 private static AutoResetEvent m_AutoReset = new AutoResetEvent(false);

protected void btn_DoWork_Click(object sender, EventArgs e)
{
    ShowStatus("Starting...");

    for (int i = 0; i < MaxThraedCount; i++)
    {
        m_Workers[i] = new Thread(IncreaseNumber) { Name = "Thread " + (i + 1) };
        m_Workers[i].Start();
    }
   
    m_AutoReset.Set();
}

void IncreaseNumber()
{
    m_AutoReset.WaitOne();
    for (int i = 0; i < 10000; i++)
        x++;
    m_AutoReset.Set();
}

One notable point is in this case (UI non-blocking) it is not easy to use Monitor.Enter/Monitor.Pulse pair to replace AutoResetEvent and implement "one by one" logic becasue Monitor.Pulse won't maitain state, below is the description in MSDN:

Important

The Monitor class does not maintain state indicating that the Pulse method has been called. Thus, if you call Pulse when no threads are waiting, the next thread that calls Wait blocks as if Pulse had never been called. If two threads are using Pulse and Wait to interact, this could result in a deadlock. Contrast this with the behavior of the AutoResetEvent class: If you signal an AutoResetEvent by calling its Set method, and there are no threads waiting, the AutoResetEvent remains in a signaled state until a thread calls WaitOneWaitAny, or WaitAll. The AutoResetEvent releases that thread and returns to the unsignaled state.


In my Winform application, if I call Monitor.Pulse() in button click event, many threads will not receive the signal (whereas AutoResetEvent will remain signaled state)! I wrote a simple routine to demonstrate this:
 private static readonly string _locker = "THREAD_LOCKER";
public static void Main()
{
    for(int i = 0;i<5;i++)
    {
        Thread t = new Thread(DoWork);
        t.Name = "T" + (i + 1);
        t.IsBackground = true;
        t.Start();
    }
   
    //Thread.Sleep(500);
   
    Monitor.Enter(_locker);
    Console.WriteLine("Main thread");
    Monitor.Pulse(_locker);
    Monitor.Exit(_locker);
}

private static void DoWork()
{
    Monitor.Enter(_locker);
    Monitor.Wait(_locker);
    Monitor.Pulse(_locker);
    Monitor.Exit(_locker);
    Console.WriteLine(Thread.CurrentThread.Name + " finished and exist");
}
Removing "Thread.Sleep(500)" will *VERY POSSIBLY* lead less than 5 thread working, because creating 5 threads requires not short time (kenel object, TEB, kenel/user stack), during the period one just created thread (T2) might get the signal or might not (much more possible), because when the previously created thread (T1) calling "Monitor.Pulse(_locker)" T2 had NOT been setup, T2 and the thread created later will have no chance to get signal! They will be waiting... So the 0.5 seconds is used to give time to create 5 thread, otherwise main thread will quit immediately and background thread will be collected.

Solution #4: Create a temp integer in each thread and once finish add temp onto x under a exclusive lock.

 private readonly string m_Locker = "THREAD_LOCKER";

void IncreaseNumber(object objThreadName)
{
    int tmp = 0;
    for (int i = 0; i < 10000; i++)
        tmp++;

    lock (m_Locker)
        x += tmp;
}

Solution #5: ReaderWriterLockSlim.

 void IncreaseNumber(object objThreadName)
{
    // Or we can use ReaderWriterLock.AcquireWriterLock(500) but it has more performance overhead and is not recommended
    m_ReaderWriterLocker.EnterWriteLock();
    for (int i = 0; i < 10000; i++)
        x++;
    m_ReaderWriterLocker.ExitWriteLock();  // Or ReaderWriterLock.ReleaseWriterLock();
}

Please note that ReaderWriterLock class is not recommended, it "take about five times longer to execute than a call to Monitor's Enter method." please refer: Reader/Writer Locks and the ResourceLock Library by Jeffery Richter.

Solution #6: Parallel.Forcoordinate with Interlocked.Increment.

 Parallel.For(0, 100000, (i) => Interlocked.Increment(ref x));

Conclusion

In this post I took a simple & straight-forward example: 10 threads simultaneously operating on on field, to experiment atomicity operation in C#.Net, using synchronization technology including exclusive locking, signaling, non-blocking synchronization, I guess this is a very good example to master basic FCL thread libraries/concepts such as Interlocked, Monitor, MemoryBarrier, volatile, AutoResetEvent, ReaderWriterLockSlim, etc.

Multi-threading programming is indeed very complex, during my investigation I happened to saw even Jon Skeet admitted he had “his eyes opened to the precise meaning of volatile which isn't "always read from main memory, always write directly to main memory" (link), so as a rookie in this field I should invest more effort on it:)

Further Reading

Threading In C# (Strongly recommended!!!)

Linearizability Wikipedia

Introducing the new ReaderWriterLockSlim in Orcas

Asynchronous Code Blocks

Atomic Operations

Boosting Performance with Atomic Operations in .NET 4


View Post»



Permalink:http://wayneye.com/Blog/Atomic-Operation-In-Csharp
Tag:

HTML5 WebMessaging In Essence

 
Written by Wayne Ye  Tuesday, August 30, 2011

Download HTML5_Cross-Domain_WebMessaging_Demo.zip - 2.97 KB

As a web developer, sometimes we are easy to encounter one problem: Cross-Domain communication, conforming Same-Origin-Policy, JavaScript code cannot access code stay in different domain(or sub-domain) or protocol (HTTP/HTTPs) or port, so there was no direct (or I can say: simple) way to achieve Cross-Domain Communication. However, those kinds of requirements does happen: page A and page B are in different domain,  B is "embedded" in A, i.e., there is an "iframe" in page A whose "src" is page B's URL, now page A wants to control page B and vise-versa. 

By limiting the solution to be done by 100% client JavaScript, beforehand HTML5, there are a number of tricky "hacks", such as:

  1. URL long polling: container page A changes the iframe page B's URL hash, and B periodically checks the hash, once the hash changed, it takes action according to the contracted hash value. [BTW, the pattern can be revised to be non-polling by HTML5 onchashchange event]
  2. CrossFrame, a Safe Communication Mechanism Across Documents and Across Domains.
  3. Window Size Monitoring: update the iframe's window size once, and the containning window subscribes its "onresize" event then takes corresponding action(s). Google Mapplets adopted this pattern.

Well, personally I really don't like all of them... either inelegant, or violates the original functionalities of DOM elements, or too complecated. I believe many people don't like them too even the pattern inventors I bet... That why WHATWG created Cross-Domain communication in HTML5: Web Messaging.

As an HTML5 crazy advocator I like it very much, complete client communication, no server impact, efficient, secure (at least in theory).

How to

"Child" can be an iframe or an popup window by invocking window.open, "parent page" A contains source code is like below:

 <iframe id="ifr" src="http://domainB.com/B.htm" onload="sendCommand();">
  No frame!
</iframe>

<script type="text/javascript">  
  function sendCommand() {
    var ifr = document.getElementById("ifr");
    ifr.contentWindow.postMessage("Hello", "http://domainB.com");
  }
</script>

Notice, make sure post message only when the iframe is loaded, otherwise the contentWindow will be still in same domain with the container page.

Child page B contains code like below:

 <input type="button" value="Cross domain call" onclick="sendMsg();" />

<script>
window.addEventListener("message", receiveMessage, false);

function receiveMessage(evt) {
    console.log("Page B received message from origin: %s.", evt.origin);
    console.log("Event data: %s", evt.data);
    //evt.source will be a window who sent the message, it can be used to post message to it
   
    // Take action(s)
}
</script>

The demo code above is one direction: parent sends message to child (iframe), actually bi-directional message trasfer can also be done, similar with "Parent control child", child page posts message to container window, only different is call "parent.postMessage".

 function receiveMessage(evt) {
    evt.source.postmessage("Hello caller");
    // or parent.postmessage("Hello parent");
}

Web Messaging Essential

In a nutshell, HTML5 Web Messaging is a suite of JavaScript API exposed by web browser, to communicate between different browsing context, when JavaScript code in one browser tab/window trys to deliver a message to another tab/window, web browser locates the target tab/window under the specified domain, and file a MessageEvent (which inherits from DOMEvent) to the target tab/window, so if the target tab/window already subscribed the message event, it will gets notified, eventually the message got delivered through MessageEvent.data.

Live Demo

I've done a demo in my dev machine, I override my local hosts file to let  Container.com, DomainA.com, DomainB.com and DomainC.com all point to 127.0.0.1:

127.0.1.1 Container.com
127.0.0.1 DomainA.com

127.0.0.1 DomainB.com
127.0.0.1 DomainC.com

I prepared a Container page which contains code below:

 <h3>HTML5 Cross-Domain post message demo</h3>
<p id="infoBar"></p>
<div id="wrapperA">
    <input type="text" id="txtA" /><br />
    <input type="button" value="Post Message" onclick="postMsgToIfr('A');" />
    <br />
    <iframe id="ifrA" src="http://DomainA.com/A.htm"></iframe>
</div>
<div id="wrapperB">
    <input type="text" id="txtB" /><br />
    <input type="button" value="Post Message" onclick="postMsgToIfr('B');" />
    <br />
    <iframe id="ifrB" src="http://DomainB.com/B.htm"></iframe>
</div>
<div id="wrapperC">
    <input type="text" id="txtC" /><br />
    <input type="button" value="Post Message" onclick="postMsgToIfr('C');" />
    <br />
    <iframe id="ifrC" src="http://DomainC.com/C.htm"></iframe>
</div>
<div style="clear: both">
</div>
<script type="text/javascript">
    window.addEventListener("message", receiveMessage, false);

    var infoBar = document.getElementById("infoBar");
    function receiveMessage(evt) {
        infoBar.innerHTML += evt.origin + ": " + evt.data + "<br />";
    }

    function postMsgToIfr(domain) {
        switch (domain) {
            case "A":
                var ifr = document.getElementById("ifrA");
                ifr.contentWindow.postMessage(document.getElementById("txtA").value, "http://DomainA.com");
                break;
            case "B":
                var ifr = document.getElementById("ifrB");
                ifr.contentWindow.postMessage(document.getElementById("txtB").value, "http://DomainB.com");
                break;
            case "C":
                var ifr = document.getElementById("ifrC");
                ifr.contentWindow.postMessage(document.getElementById("txtC").value, "http://DomainC.com");
                break;
            default:
                throw new error("No such domain!");
        }
    }       
</script>

And three pages: A.htm located in DomainA.com, B.htm located in DomainB.com, C.htm located in DomainC.com, phisically they all located under C:\inetpub\wwwrooot, while in the browser I manually type DomainA/B/C.com to make the cheatSmile, the code in the A/B/C page are similar, showing below:

 <h4>DomainA/A.htm1</h4>
<input type="button" value="Cross domain call" onclick="doClick();" />
<div id="d">
</div>
<script>
    window.addEventListener("message", receiveMessage, false);

    function doClick() {
        parent.postMessage("Message sent from " + location.host, "http://container.com");
    }

    var d = document.getElementById("d");
    function receiveMessage(evt) {
        d.innerHTML += "Received message \"<span style=\"color:red\">" + evt.data + "</span>\" from domain: " + evt.origin + "<br />";
    }
</script>

I recorded a gif image below to demonstrate the Cross-Domain messaging:

HTML5 Cross-Domain Messaging Demo

See the message passed through different domain in bi-direction? Isn't it cool?

MessageChannel

To support independent communication under different browsing context, HTML5 introduced Message Channel to post message independently, its official definition is showing below:

Communication channels in this mechanisms are implemented as two-ways pipes, with a port at each end. Messages sent in one port are delivered at the other port, and vice-versa. Messages are asynchronous, and delivered as DOM events. 

I spent about half a day in investigating the Message Channel and finally got it worked, my code is showing below:

Container page source code

 <iframe id="ifr" src="http://wayneye.me/WebProjects/HRMS/Opener.html" onload="initMessaging()"></iframe>
<input type="button" value="Post Message" onclick="postMsg();" />
<div id="d"></div>
<script>
var d = document.getElementById("d");
var channel = new MessageChannel();
channel.port1.onmessage = function (evt) {
    d.innerHTML += evt.origin + ": " + evt.data + "<br />";
};

function initMessaging() {
    var child = document.getElementById("ifr");
    child.contentWindow.postMessage('hello', 'http://wayneye.me', [channel.port2]);
}

function postMsg() {
    channel.port1.postMessage('Message sent from ' + location.host);
}
</script>

iframe page source code

 <div id="info"></div>
<input type="button" value="Post Message" onclick="postMsg();" />
<script>
var info = document.getElementById("info");
var port = null;
window.addEventListener("message", function (e) {
console.log(e);
    if(e.ports && e.ports.length > 0) {
        port = e.ports[0];
        port.start();
        port.addEventListener("message", function (evt) {
            info.innerHTML += "Received message \"" + evt.data + "\" from domain: " + evt.origin + "<br />";
        }, false);
    }
}, false);

function postMsg() {
    if(port) {
        port.postMessage("Data sent from " + location.host);
    }
}
</script>

The entire process can be described as:

  1. Container page (A) embedded an iframe whose src is pointing to a page (B) in different domain.
  2. Once the iframe loaded, the container posts a message to page B with a MessagePortArray.  
  3. Page B received the message as well as the array containing a list of MessagePort objects.
  4. Page B registers onmessage event on port instance.
  5. Page B invokes port.postmessage to send a message to page A through this MessageChannel. 
At this timestamp, I seems only Opera correctly supports MessageChannel, Google Chrome and IE10 Platform Preview 2 failed to deliver the ports array, Safari cannot gets the onmessage event fired. Firefox 7.0 beta event doesn't support MessageChannel object.
Note: Ports can also enable communication between HTML5 Web Workers.

One more thing (plagiarize Steve JobsSmile), to use Message Channel one noticable point is, web developer should explicitly close the channel once there is no need, otherwise there will be a strong reference between two pages, as W3 official page emphasized below:

Authors are strongly encouraged to explicitly close MessagePort objects to disentangle them, so that their resources can be recollected. Creating many MessagePort objects and discarding them without closing them can lead to high memory usage.

Further reading

HTML5 Web Messaging

window.postMessage - MDN Docs

HTML5 Demo: postMessage (cross domain)

HTML5′s window.postMessage AP

Internet Explorer 10 Platform Preview: HTML5

http://ithelp.ithome.com.tw/question/10057709

 

Also posted on CodeProject: http://www.codeproject.com/KB/HTML/HTML5-WebMessaging.aspx


View Post»



Permalink:http://wayneye.com/Blog/HTML5-WebMessaging-In-Essence
Tag:

nodejs+express+jade+socket.io on Ubuntu 11.04

 
Written by Wayne Ye  Monday, August 29, 2011

Introduction

socket.io is a brilliant project which perfectly supports HTML5 WebSocket, in additional, it can fall back to flash or long polling when the client web browser does not support WebSocket, I explored it today on Ubuntu, I summarized the process in this post.

 

Install nodejs

I used nodejs v0.4.11, the latest stable version, by mannually compile/make/install.

sudo curl http://nodejs.org/dist/node-v0.4.11.tar.gz -o ~/Desktop/WayneDevLab/node.tar

sudo tar -xf node.tar cd node-v0.4.11

./configure

make

sudo make install

In the "./configure" step above if it prompted "libssl-dev" not found, please install libssl-dev (sudo apt-get install libssl-dev) which is required by node for hosting HTTPs server.

Install npm

curl http://npmjs.org/install.sh | sh

Install express

express is an insanely fast and high performance server-side JavaScript web development framework built on node, hosting a real web server based on node will be much harder without it:)

$ npm install -g express
$ express /tmp/foo && cd /tmp/foo
$ npm install -d 

Install socket.io

npm install socket.io

Setting up the Web server!!

I've modified my app.js as below:

 var express = require('express');

var app = module.exports = express.createServer(),
    io = require('socket.io').listen(app);

// Configuration
app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.set('view options', {layout: false});
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){
  res.render('index', {
      title: 'Wayne Express Hello World',
      youAreUsingJade: true,
      domain: '192.168.1.4'
  });
});

app.listen(3000);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

And below is my index.jade:

!!! 5
html(lang="en")
  head
    title= title
    script(src='http://'+ domain + ':3000/socket.io/socket.io.js')
    script(type='text/javascript')
      var socket;
      socket =  io.connect('http://192.168.1.4:3000');

      socket.on('news', function (data) {
       console.log(data);
       socket.emit('my other event', { my: 'data' });
             });
  body
    h1 Wayne WebSocket Demo using Socket.io
    #container
      - if (youAreUsingJade)
        p You are amazing
      - else
        p Get on it!

    p Powered By a(href='http://socket.io') Socket.io

    footer Copyright©2011 http://wayneye.com

Testing everything up

Start the server by:

$ node app.js

It works:)

I tested the combination above by using google Chromoum Dev 12 and Dev 15, they seperately adhere WebSocket protocol draft-hixie-thewebsocketprotocol-76 and draft-ietf-hybi-thewebsocketprotocol-10, the connection details for hybi10 is showing below:

socket.io

socket.io will print out useful information on console when running:

socket.io console

 

Wish that helps:)


View Post»



Permalink:http://wayneye.com/Blog/nodejs-express-jade-socketio-On-Ubuntu
Tag:

写代码写累了,DIY pizza玩

 
Written by Wayne Ye  Sunday, August 28, 2011

周中写Code写累了,偶尔看到微博上有人发自制Pizza的教程,觉得很有兴趣,于是周末自己搞了一下,成果如下:

第一锅 - 什锦pizza (金枪鱼+火腿+香菇+青椒 = Wayne荤食天地):

First pizza

第二锅 - 依然Wayne荤食天地,but double cheese:Smile

Second pizza

从2点钟去金桥家乐福买食材,到晚上8点半,我忙活了6个伴小时,就弄出来这2锅,我负责80%的工作,感谢@可爱的小光头他妈@痒痒兔爪爪的帮忙

Jessie holds Pizza

Have a slice day^_^

Have a Slice day

我整理的做法:

面饼
1.低筋面粉300g
2.酵母粉1小匙
3.盐少许(约2-5g)
4.砂糖15g
5.奶油20g
6.温开水170 ㏄(冬天温水,夏天凉水)
7.半个蛋(蛋打散与温水混合,约180 ㏄~200 ㏄)

1.和面,揉至扩展阶段,发酵至2倍大
2.发酵好的面团,以塑胶袋包覆,松弛15min
3.杆平
4.用叉子均匀地在底部刺小洞,放置温度稍高的地方,发酵20min左右

Pizza酱
1. 准备好西红柿丁,洋葱丁,蒜蓉
2. 热锅,放入黄油块(3-4 块), 橄榄油亦可
3. 黄油融化后放入洋葱丁和蒜蓉, 炒一分钟
4. 洋葱爆香以后,放入西红柿丁,大火翻炒
5. 当看到西红柿炒出汁水以后,放进糖、黑胡椒粉、牛至叶(Oregano),罗勒(Basil),翻炒均匀,然后盖上锅盖,转小火煮20分钟左右。煮的过程中不时打开锅盖翻炒一下,以免糊底。
6. 煮好后,如果此时水份还比较多,转大火收汁成酱,加入盐,翻炒均匀后即可出锅。

Pizza Cheese
马苏里拉奶酪片

食材 (个人喜好,我选择了火腿+青椒+菠萝+碎肉+香菇)

pizza出锅后撒些Cheese粉味道更好。


烘焙:210°,烤箱中层,大约20min

Tips:
1、调酵母的水一定要是温水,用手摸上去稍有点热即可,否则酵母就给汤死了;
2、PIZZA酱一定要炒浓稠,而且不能刷太多,否则烤的时候会淌水;
3、要想烤好的PIZZA能拉出丝来,就一定要选用马苏里拉奶酪;
4、想吃表皮脆脆的饼,可以在第二次进烤箱前在饼皮上刷上一层油。

名词解释:
罗勒(Basil),是大多数人接触到的第一种意大利香草,吃意面、比萨都会看到。罗勒号称“香草之王”,和意大利的另一种代表性蔬菜——番茄,味道非常相配,所以铺天盖地使用开来。由新鲜罗勒、乳酪、松子、大蒜和橄榄油混合而成的罗勒酱,就是有名的意大利pesto酱。罗勒味道清新,有点像丁香,意大利菜中使用的是甜罗勒,芳香味最佳,台湾菜中的九层塔也是罗勒的一种,味道比较冲一点。
牛至(学名:Origanum vulgare)又名滇香薷,是唇形科牛至属中的一种植物。
做烹调用时,常与番茄、乳酪搭配;牛至与罗勒是给予意大利菜独特香味的两大用料。

 


View Post»



Permalink:http://wayneye.com/Blog/DIY-Pizza-When-Tired-Of-Coding
Tag:

JavaScript - Foundation of the Language

 
Written by Wayne Ye  Sunday, July 24, 2011

Introduction

JavaScript "was born under the shadow of Java" - Brenden Eich, it is "the most misunderstood programming language in the world" - Douglas Crockford. JavaScript was used so widely nowadays, nearly everywhere as long as you are using a web browser and surfing the internet, most of the websites have JS inside, and even server side - nodejs, according to http://langpop.com/, JavaScript is the fourth popular programming language in the world

In this post I am trying to use refined language to cover some foundation of JavaScript (many of which confused quite a number of developers) including basic data types, comparison mechanism, functions, Execution context, Variable Scope Chain, essence of Closure and anounymous function (lambda). Hope it can more or less help people have more fun and less frustration in the future JS coding. 

Basic data types/object types 

In JavaScript, there are 5 basic types: Undefined, Null, Boolean, Number and String, integers  Boolean values and strings are accessed by value, that's different with many modern languages like C# (System.String) or Python (PyStringObject), string are object and passed by reference, JS code snipppet below proves that string is stored in the stack and passed by value. 

var str = "a";
                
function strValTest(s) {
    s = "b";  // "s" is a String value: "a".
    alert(s); // Will alert "b".
}
strValTest(str);
alert(str); // Will alert "a", because when called strValTest, String variable's value is passed as argument.

String is JavaScript is also immutable just like many other languages, i.e. any change applied on an existing string will create a new string in memory and destroy the old one (there is still different with C# in which there is a String Intern Pool to store all String values in managed heap). Code snippet below shows the difference between string and String: 

var str1 = "A new String";
console.log("str1");
console.log(str1 instanceof String); // false
console.log(str1 instanceof Object); // false
console.log(typeof (str1)); // string

var str2 = new String("A new String");  // Create a new Object stored on the heap with value "A new String"
console.log("str2");
console.log(str2 instanceof String); // true
console.log(str2 instanceof Object); // true
console.log(typeof (str2)); // object

Then you might have question of: how come string instance has methods now that string is value type? The answer is that In JavaScript there are corresponding Object wrapper for the basic types: Number, Boolean and String, they inherit from Object and have their own properties and methods such as Number.toFixed() and String.indexOf(), simple code snippet below:

string str = "I am a JSer"; // Create a new string variable on the stack with value "I am a JSer".
alert(str.indexOf("JSer"));

Essentially at the back end, JS interpreter will temporarily creates a new String object and invokes its instance method "indexOf", after the method call finished, the temporary String object will be claimed, the process can be demonstrated as below:

string str = "I am a JSer";
var tmp = new String(str);
alert(tmp.indexOf("JSer"));
tmp = null;

 

Comparison

Comparison might be a very confused part in JavaScript, why? See code below:

console.log(null == undefined); // true Per ECMA-262, null and undefined are superficially equal, essentially "the value undefined is a derivative of null"<professional>.
console.log(null === undefined); // false
console.log(NaN == NaN);  // false. A specific NaN is not considered equal to another NaN because they may be different values. Also refer: http://en.wikipedia.org/wiki/NaN
console.log('5' == 5);   // true. 
console.log('5' === 5);   // false. typeof('5') is string and typeof(5) is number
console.log(false == 0);  // true
console.log(true == 1);  // true
console.log(null == 0);  // false

console.log(typeof (null)); // object
console.log(typeof (undefined)); // undefined

Foo.prototype = {
	constructor: Foo,
	valueOf: function () {
		return "Object Foo";
	},
	toString: function () {
		return "Foo";
	}
};

var foo1 = new Foo();
console.log("foo1 == foo2: " + (foo1 == "Object Foo")); // true will call foo1.valueOf() 

var foo2 = new Foo();
console.log("foo1 == foo2: " + (foo1 == foo2)); // false foo1, foo2 point to diffrent instance of Foo

foo2 = foo1;
console.log("foo1 == foo2: " + (foo1 == foo2)); // true no doudt
</professional>

Are you sweating? I did... So I read book and copied Paragraph below from <Professional JavaScript for Web Developers>. 

 

  • If an operand is a Boolean value, convert it into a numeric value before checking for equality. 
  • A value of false converts to 0, whereas a value of true converts to 1. 
  • If one operand is a string and the other is a number, attempt to convert the string into a number before checking for equality. 
  • If either operand is an object, the valueOf() method is called to retrieve a primitive value to compare according to the previous rules. If valueOf() is not available, then toString() is called. 
  • The operators also follow these rules when making comparisons:
  • Values of null and undefined are equal. 
  • Values of null and undefined cannot be converted into any other values for equality checking. 
  • If either operand is NaN, the equal operator returns false and the not - equal operator returns true. Important note: Even if both operands are NaN , the equal operator returns false because, by rule, NaN is not equal to NaN. 
  • If both operands are objects, then they are compared to see if they are the same object. If both operands point to the same object, then the equal operator returns true . Otherwise, the two are not equal.  

Function

In JavaScript, function is not only traditional function but also an object, define a function is actually define a pointer to that function, and function is not only traditional function but also an  Object. I wrote code snippet below for better understanding: 

function dummyFunc() { // Define a function and a pointer to it, the pointer's name is "dummyFunc"
	this.DummyProperty = "Dummy Property";
	console.log("Dummy func");
}

var tempFunc = dummyFunc; // Define a variable tempFunc, let it equal to dummyFunc which is a function pointer pointing to function defined above
dummyFunc = null; // null the dummyFunc
tempFunc(); // tempFunc still points to the function defined above so still can be executed.

var dummy = new tempFunc(); // Will invoke tempFunc's constructor to form a new Object
console.log(dummy.DummyProperty);

Another very important point of functions is the parameters, in JavaScript, function's arguments are ALL passed by value, NOT reference even if the argument is an Object, to prove this please see code snippet below:

var person = new Object();
function setName(obj) {
	obj.Name = "Wayne"; // obj is actually newly created and given the pointer's value, so obj and the reference type outside this function will both point to the Object on the heap, thus operation on obj will affect the Object passed in the function.
	
	obj = new Object(); // By executing this line, temporary variable obj will point to a new Object, has no relationship with the passed-in Object any more.
	obj.Name = "Wendy";
}

setName(person); // Executing this line will pass person's pointer stored in stack to the function setName, 
alert(person.Name); // Will alert "Wayne"

Execution Context and Variable Scope Chain 


View Post»



Permalink:http://wayneye.com/Blog/JavaScript-Foundation-Of-The-Language
Tag:

IP Address to Geolocation

 
Written by Wayne Ye  Thursday, December 30, 2010

Background


Few months ago I found an interesting website: http://ipinfodb.com/, it provided API which could "translate" any IP Address into a geography location including City/Region/Country as well as latitude/longitude and time zone information, to invoke its API, a registered API key is required (which is free). Since beforehand I stored visitor's IP Addresses into my own database, I decided to utilize InfoDB API to store visitor's GEO locations.

Just few days ago, I casually emitted an idea: summarize those GEO location records and display them on Google Map, hum, it is feasible:)

So, the process is: Track visitor's IP addresses -> "Translate" them to Geography location -> Show them on Google Map!

(PS, I've been used Google Analytics for my Geek Place - http://WayneYe.com for more than two years, it is no double extremely powerful, and it already contains a feature "Map Overlay", however, due to privacy policy, Google Analytics does NOT display visitor's IP address, see http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=86214).

Implementation


The first task I need to do is track visitor's IP Address, most of the time, user visits a website in browser submits an HTTP GET request (an HTTP data package) based on Transmission Control Protocol (most of the time) , browser passed the ball to DNS server and DNS server delivered the request to the designation - the web host server, during the process, the original Http request was possibly transferred through a number of routers/proxies and many other stuff, the request's header information might have been updated: Via (Standard HTTP request header) or X-Forwarded-For (non-standard header but widely used), could be the original ISP's information/IP Address OR possibly one of the proxy's IP Address.

So, usually the server received the request and saw Via/X-Forwarded-For header information, it got to know visitor's IP address (NOT all the time, some times ISP's IP address), in ASP.NET, it is simply to call Request.UserHostAddress, however, we can never simply trust this because of two major reasons:

  1. Malicious application can forge HTTP request with modified X-Forwareded-To header (for example: X-Forwarded-To: dangerous code), if you are unlucky to trust it and have it inserted into Database, then SQL Injection hole will be utilized by Malicious application.

  2. Not all the visitors are human-been, part of them could be search engine spiders, I must distinguish human visitors and spiders, otherwise for example, I will be happy to see a lot of "visitors" came from "Mountain View, CA" ^_^.


For #1: I use regular expression to validate the string I got from Request.UserHostAddress:
public static Boolean IsValidIP(string ip)
{
if (System.Text.RegularExpressions.Regex.IsMatch(ip, "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"))
{
string[] ips = ip.Split('.');
if (ips.Length == 4 || ips.Length == 6)
{
if (System.Int32.Parse(ips[0]) < 256 && System.Int32.Parse(ips[1]) < 256
& System.Int32.Parse(ips[2]) < 256 & System.Int32.Parse(ips[3]) < 256)
return true;
else
return false;
}
else
return false;
}
else
return false;
}

If the result is "0.0.0.0", I will


View Post»



Permalink:http://wayneye.com/Blog/IPAddress-To-Geolocation
Tag:

Microsoft Desktop Player

 
Written by Wayne Ye  Thursday, November 18, 2010


MSDN TechNet Flash has announced Microsoft Desktop Player, now it is in Beta, it can be accessed through the official website which is Silverlight based:

http://www.microsoft.com/click/desktopplayer/

I also installed the desktop version, the desktop version is not a Silverlight OOB, instead, it is a traditional Winform, see screenshot below:



View Post»



Permalink:http://wayneye.com/Blog/Microsoft-Desktop-Player
Tag:

Programmatically PIN shortcut onto Taskbar on Win7

 
Written by Wayne Ye  Saturday, July 17, 2010

Background

During my work I got one requirement of pinning a specific shortcut file (*.lnk) onto the Windows 7 Taskbar, after investigating I found programmtically achive this is “not permitted”, refer MSDN article: http://msdn.microsoft.com/en-us/library/dd378460(VS.85).aspx

A small set of applications are pinned by default for new installations. Other than these, only the user can pin further applications; programmatic pinning by an application is not permitted.

However, the exception comes from Windows Script Hosting, a snippet of VBscript can achieve my requirement, I read several articles (refer the end of this post) and wrote two VBS scripts showing below:

Pin to Taskbar

Option Explicit

'Const CSIDL_COMMON_PROGRAMS = &H17
Dim ShellApp, FSO, Desktop
Set ShellApp = CreateObject("Shell.Application")
Set FSO = CreateObject("Scripting.FileSystemObject")

'Set StartMenuFolder = ShellApp.NameSpace(CSIDL_COMMON_PROGRAMS)
Set Desktop =  ShellApp.NameSpace("C:\Users\Wayne\Desktop")

Dim LnkFile
LnkFile = Desktop.Self.Path&"\ScheduleNotifier.lnk"

If(FSO.FileExists(LnkFile)) Then
    Dim tmp, verb
    'For Each verb in Desktop.ParseName("ScheduleNotifier.lnk").Verbs
        'tmp = tmp&verb&chr(13)
    'Next
    'MsgBox(tmp)

    Dim desktopImtes, item
    Set desktopImtes = Desktop.Items()

    For Each item in desktopImtes
        If (item.Name = "ScheduleNotifier") Then
            'MsgBox(item.Name)
            For Each verb in item.Verbs
                If (verb.Name = "Pin to Tas&kbar") Then 'If (verb.Name = "锁定到任务栏(&K)")
                    verb.DoIt
                End If
            Next
        End If
    Next

End If

Set FSO = Nothing
Set ShellApp = Nothing

Unpin from Taskbar

Option Explicit
'Const CSIDL_COMMON_PROGRAMS = &H17
Dim ShellApp, FSO, Desktop
Set ShellApp = CreateObject("Shell.Application")
Set FSO = CreateObject("Scripting.FileSystemObject")

'Set StartMenuFolder = ShellApp.NameSpace(CSIDL_COMMON_PROGRAMS)
Set Desktop =  ShellApp.NameSpace("C:\Users\Wayne\Desktop")

Dim LnkFile
LnkFile = Desktop.Self.Path&"\ScheduleNotifier.lnk"

If(FSO.FileExists(LnkFile)) Then
    Dim tmp, verb
    'For Each verb in Desktop.ParseName("ScheduleNotifier.lnk").Verbs
        'tmp = tmp&verb&chr(13)
    'Next
    'MsgBox(tmp)

    Dim desktopImtes, item
    Set desktopImtes = Desktop.Items()

    For Each item in desktopImtes
        If (item.Name = "ScheduleNotifier") Then
            'MsgBox(item.Name)
            For Each verb in item.Verbs
                If (verb.Name = "Unpin from Tas&kbar") Then 'If (verb.Name = "从任务栏脱离(&K)")
                    verb.DoIt
                End If
            Next
        End If
    Next

End If

Set FSO = Nothing
Set ShellApp = Nothing

Although two snippets of VBS code are simple and stright-forward, they has on serious limitation, it only support one specified language, attention of this line of code:

If (verb.Name = "Pin to Tas&kbar") Then 'If (verb.Name = "锁定到任务栏(&K)")

On Chinese locale, I have to replace “Pin to Tas&kbar” with “锁定到任务栏(&K)”, so in order to support more languages I need to know each exact {Pin sentence} to do the pinning, you know, google translation won’t always work in this case:), so I definitely don’t want to do that…

You probably ask WHY??? Because FolderItemVerb object only has one property: Name, and Name is definitely “language sensitive“, MSDN does mention there are two more properties “Application” and “Parent” however they are “Not implemented”.

Several tips

  • For a specific Windows user, all shortcuts pinned onto taskbar store the shortcuts files under
    %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
  • OEM manufacturers could “pre-pin” some shortcut onto the Taskbar before shipping the PC/laptops to the consumers, simply run batch command below during the DASH (see Desktop and mobile Architecture for System Hardware) process:
    Rem Pining OEM Welcome Center app on task barReg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\TBDEn /v SBOEM0 /t REG_EXPAND_SZ /d “SomeFile.lnk” /f

     

    References

    Pin Items to the Start Menu or Windows 7 Taskbar via Script
    http://blogs.technet.com/b/deploymentguys/archive/2009/04/08/pin-items-to-the-start-menu-or-windows-7-taskbar-via-script.aspx

    Pin and Un-pin items to/from the Windows 7 taskbar
    http://blog.ananthonline.net/?p=37


  • View Post»



    Permalink:http://wayneye.com/Blog/Programmatically-PIN-shortcut-Onto-Taskbar-on-Win7
    Tag:


    A good lesson and experience

     
    Written by Wayne Ye  Thursday, September 3, 2009

    I was extremely busy during the last two weeks, since our project was at bug fixing phase, my team and I worked really hard on dealing with the bugs; We fixed two to three bugs everyday by average, I am very satisfied with the efficiency myself.

    During this process, I encountered several very interesting issues.

    1. There was a crash bug with P1 priority, we all cannot reproduced it until one guy from US team provided a remote server that our product always crashed, after I logged onto that server and 20 minutes investigating, I was really surprised, the reason is one line of code written by myself, it is:

      DateTime.Parse(String.Format("{0}/{1}/{2} 00:00:00 AM", DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Year))

      Does this line of code has any problem, I definitely don’t think so before, but now, I understand, it only works under English format, once non-English format, under some format such as Chinese, the AM makes non sense; and… Under some formats (such as French), the code will throw FormatException and leads program crash…

    2. Another issue is a WPF related multi-threading issue, there is a module in our product used thread pool to invoke WMI APIs to


    View Post»



    Permalink:http://wayneye.com/Blog/A-Good-Lesson-On-DateTime-Localization
    Tag:

    作息时间自动提醒Windows Service

     
    Written by Wayne Ye  Friday, April 24, 2009

    经济危机真真切切的影响到了几乎每一个人,09年过去三分之一多一点了,生活不太规律,确实太忙的说,工作换了,房子也换了,又做回了张江男,觉得自己再不好好努力会Out的。。。都奔三儿而去了。。。于是乎,为了勉励自己,做了这么个东东。

    WSNSVC

    一个Windows Service,里面跑一个Timer,每隔10秒check一下当前时间,一旦发现时间吻合,则调用Microsoft Speech Technology? 发声提醒,超级简单,不过。。。以后要么得一直戴着心爱的森海塞尔,要么只能用音箱了。。。

    服务描述:
    Notify Wayne to follow the schedule established by himself. If this service is disabled, Wayne may waste a lot of time...

    项目层级:
    SolutionHierarchy 

    • WayneScheduleService.cs Windows Service后台代码


    View Post»



    Permalink:http://wayneye.com/Blog/Personal-Schedule-Windows-Service
    Tag: