Just Another Blog

Are you thinking what I'm thinking?

Monday, February 28, 2005

The Dying IrfanView

While the official site is gone, most of its mirrors are still here! Perhaps it is not completely dead (yet).

It would be nice if Irfan Skiljan would put his codes to public domain/open source so that the product can stay forever.

Sunday, February 27, 2005

Good bye, IrfanView!

The official homepage of IrfanView is now gone. R.I.P., IrfanView. :-(

Saturday, February 26, 2005

GeoURL is back!

GeoURL 2.0 beta launches

Welcome to GeoURL 2.0, thanks for visiting!

What in tarnation is this thing!?

GeoURL maps geographic locations to websites letting you lookup websites near a location or near another website. It's great fun. If you haven't tried already, then go to the front page and click on a location on the map.

This looks a lot like something I've seen before ...

Yes, indeed! This is a relaunch (and re-implementation) of the original GeoURL site that was invented and built by Joshua Schachter (of del.icio.us fame). All the old URLs should work. If not, please send me a mail.

See the sites near me!

不應該... 不應該是這樣的...

T... T... TVB!! 可惡!怎麼可以允許這種事發生!不應該... 不應該是這樣的... 畜生... OP被換成中文............!!

Thursday, February 24, 2005

Mouse gestures considered harmful

Due to heavy use of mouse gestures, the right button of my mouse stopped to function properly (a single right-click sometimes resulted in multiple right-click). And yesterday, it even failed to move the cursor for at least 4 times! I had to reconnect the mouse cord in order to "reset" the mouse.

Probably I should use more keyboard shortcuts instead of mouse gestures. :-P

Wednesday, February 23, 2005

BiDi and CSS

Currently I'm developing a web application that supports localization (via custom DTD). While substituting text is simple and easy, creating a BiDi-friendly CSS-based layout seems to be impossible.

In CSS, there are handy properties like margin, padding and bording, but they have to be specified as top, right, bottom and left. These properties do not have BiDi in mind. Let's take a look of the following example:

什麽是Unicode(統一碼/標準萬國碼)?
ما هي الشفرة الموحدة "يونِكود" ؟

Ideally, there should be something like border-start, so that the red margin will always appear next to the first character. However, we don't have any choice since we can only specify the border as either left or right.

The use of start and end can be seem in XUL, like the attribute crop. Note that the value left and right were deprecated in flavor of start and end.

I wonder if there is any workaround. (or maybe I didn't understand CSS completely? :-P)

Friday, February 18, 2005

The world on fire

Who said Firefox is a second grade browser? The number says it all ;-):

Firefox 1.0: 25,241,830 Downloads

There is no sign of slowing down:

Firefox cumulative downloads

Tuesday, February 15, 2005

Gmail Invitations Refilled!

I found that my invitations are refilled by Gmail! It was 46, but now I got 50! Hey... how about making it public and removing the ugly "beta" from the name?

Tuesday, February 08, 2005

Parallel Universe of Amercia

Google thinks that the World consists of the North America only:

Google Maps

My feedback to Google: why don't you remove countries like Canada and Mexico? You don't have those data anyway... Otherwise, many people will think it is yet another result of nationalism (大美國主義). We had suffered a lot of these stuffs in those Hollywood movies already. :-/

P.S. Note that Google never said it is a World map. It is simply referred as the "Google Maps".

Saturday, February 05, 2005

Synergy

Synergy, aka software KVM switch?

synergy: [noun] a mutually advantageous conjunction of distinct elements

Synergy lets you easily share a single mouse and keyboard between multiple computers with different operating systems, each with its own display, without special hardware. It's intended for users with multiple computers on their desk since each system uses its own monitor(s).

Via Forever Geek.

Friday, February 04, 2005

風紀特備隊

風紀特備隊,愛與正義的化身!

風紀特備隊

我敗了... Orz

Thursday, February 03, 2005

Gmail Giveaway

Gmail is giving invitations like crazy again:

Give Gmail to: __________ 50 left

Drop your contact here if you would like to have a Gmail account. ;-)

Mozilla Beta Release of XForms 1.0 is out!

Install the extension now! Be sure that you are using a nightly dated after January 28 2005 or it will fail.

Tuesday, February 01, 2005

XMLHTTPRequest in PHP 5

In PHP 5, while there is domDocument, there is no XMLHTTPRequest. I knew there is HTTP_Request, but since I don't want to have unnecessary dependency, I've decided to create my own one. Fortunately there is fsockopen. So in a several hours of quick learning and googling, I've created this wonderful wrapper class.

<?php

/**
 * A wrapper class for handling HTTP request.
 * Only HTTP is supported. So don't try anything like HTTPS or FTP.
 * @author Ming Hong Ng <minghong@gmail.com>
 * @version 0.1, 2005/02/01
 * @since 0.1
 */
class XmlHttpRequest
{
    /**
     * Constructor.
     * @version 0.1, 2005/02/01
     * @since 0.1
     */
    function __construct()
    {
        $this->responseText = NULL;
        $this->responseXml = NULL;
        $this->status = NULL;
        $this->statusText = NULL;
        $this->error = NULL;
        $this->errorText = NULL;

        $this->components = array();
        $this->requestHeaders = array(
            "User-Agent" => $_SERVER["SERVER_SOFTWARE"],
            "Accept" => $_SERVER["HTTP_ACCEPT"],
            "Accept-Language" => $_SERVER["HTTP_ACCEPT_LANGUAGE"],
            "Accept-Charset" => $_SERVER["HTTP_ACCEPT_CHARSET"],
            "Connection" => "Close"
        );
        $this->responseHeaders = array();
    }

    /**
     * Destructor.
     * @version 0.1, 2005/02/01
     * @since 0.1
     */
    function __destruct()
    {
        if ( !is_null( $this->responseXml ) )
        {
            unset( $this->responseXml );
        }
    }

    /**
     * Assign method and URL to the pending request.
     * Upcoming transaction is always handled synchronously.
     * Please urlencode the URL before parsing into this method.
     * Please set method to "POST" if you want to post data.
     * @version 0.1, 2005/02/01
     * @param method The request method
     * @param url The URL
     * @since 0.1
     * @todo Supports method other than "GET" and "POST"
     */
    public function open( $method, $url )
    {
        $this->method = strtoupper( $method );
        $this->components = parse_url( $url );

        // Fill in the missing components, if any
        if ( is_null( $this->components["scheme"] ) )
        {
            $this->components["scheme"] = "http";
        }
        if ( is_null( $this->components["port"] ) )
        {
            $this->components["port"] =
                ( $this->components["scheme"] == "http" ) ? 80 : 443;
        }
        if ( is_null( $this->components["path"] ) )
        {
            $this->components["path"] = "/";
        }
    }

    /**
     * Transmits the request with postable string/DOM content.
     * Content is ignored unless the method is "POST".
     * Please urlencode the key/value pairs unless you are sending XML data.
     * Set headerOnly to TRUE if you just need to get the headers (save time).
     * @version 0.1, 2005/02/01
     * @param content An optional string/DOM content
     * @since 0.1
     */
    public function send( $content = NULL )
    {
        // Prepare the request headers
        $path = $this->components["path"];
        if ( !is_null( $this->components["query"] ) )
        {
            $path .= "?" . $this->components["query"];
        }
        if ( !is_null( $this->components["fragment"] ) )
        {
            $path .= "#" . $this->components["fragment"];
        }
        $request = sprintf(
            "%s %s " . $_SERVER["SERVER_PROTOCOL"] . "\r\nHost: %s\r\n",
            $this->method, $path, $this->components["host"] );
        foreach( $this->requestHeaders as $key => $value )
        {
            if ( $key != "Content-Type" && $key != "Content-Length" )
            {
                $request .= "$key: $value\r\n";
            }
        }
        if ( !is_null( $content ) )
        {
            if ( $this->method == "POST" )
            {
                $body = $content;
                $type = "application/x-www-form-urlencoded";
                if ( get_class( $body ) == "DOMDocument" )
                {
                    $body = $body->saveXML();
                    $type = "application/xml";
                }

                $lines = array(
                    "Content-Type: $type",
                    "Content-Length: " . strlen( $body ),
                    "",
                    $body
                );

                $request .= implode( "\r\n", $lines );
            }
        }
        $request .= "\r\n\r\n";

        // Open socket connection to host
        $handle = @fsockopen( $this->components["host"],
            $this->components["port"], $this->error, $this->errorText );

        // Cannot open socket connection to host
        if ( !$handle )
        {
            // Abort
            return;
        }

        // Send the request
        fwrite( $handle, $request );

        // Receive the response headers
        $line = "";
        while ( $line != "\r\n" )
        {
            $line = fgets( $handle, 1024 );

            // Extract status code/text
            preg_match( "/^HTTP\/\d.\d (\d+) (.+)\r\n/", $line, $matches );
            if ( count( $matches ) > 0 )
            {
                $this->status = $matches[1];
                $this->statusText = $matches[2];
            }
            unset( $matches );

            // Extract response name/value pairs
            preg_match( "/^([\w|\-]+): (.+)\r\n/", $line, $matches );
            if ( count( $matches ) > 0 )
            {
                $this->responseHeaders[ $matches[1] ] = $matches[2];
            }
            unset( $matches );
        }
        // Receive the response body, if needed
        if ( $this->method != "HEAD" )
        {
            while ( !feof( $handle ) )
            {
                $this->responseText .= fgets( $handle, 1024 );
            }
            $this->responseXml = new domDocument();
            if ( @!$this->responseXml->loadXML( $this->responseText ) )
            {
                @$this->responseXml->loadHTML( $this->responseText );
            }
        }
    }

    /**
     * Get the text of the specific response header.
     * @version 0.1, 2005/02/01
     * @param header The name of the response header
     * @return The response header value
     * @since 0.1
     */
    public function getResponseHeader( $header )
    {
        return $this->responseHeaders[$header];
    }

    /**
     * Get the response headers as a string for HTTP request.
     * @version 0.1, 2005/02/01
     * @return The response headers
     * @since 0.1
     */
    public function getAllResponseHeaders()
    {
        $headers = "";
        foreach( $this->responseHeaders as $key => $value )
        {
            $headers .= "$key: $value\r\n";
        }
        return $headers;
    }

    /**
     * Set a HTTP request header for HTTP request.
     * @version 0.1, 2005/02/01
     * @param header The name of the request header
     * @param The request header value
     * @since 0.1
     */
    public function setRequestHeader( $header, $value )
    {
        $this->requestHeaders[$header] = $value;
    }

    public $responseText;           // Response text
    public $responseXml;            // Response XML
    public $status;                 // Status code
    public $statusText;             // Status text
    public $error;                  // Error code
    public $errorText;              // Error text

    private $method;                // Request method
    private $components;            // URL components
    private $requestHeaders;        // Request headers
    private $responseHeaders;       // Response headers
}

// An example of using this class
header( "Content-Type: text/plain" );
$request = new XmlHttpRequest();
$request->open( "get", "http://www.example.com" );
$request->send();
echo "---------- Status ----------\r\n";
echo "$request->status $request->statusText\r\n";
echo "\r\n---------- Headers ----------\r\n";
echo $request->getAllResponseHeaders();
echo "\r\n---------- Body ----------\r\n";
echo $request->responseText;

?>

This should give you something like this:

---------- Status ----------
200 OK

---------- Headers ----------
Date: Tue, 01 Feb 2005 16:21:14 GMT
Server: Apache/1.3.27 (Unix)  (Red-Hat/Linux)
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
ETag: "3f80f-1b6-3e1cb03b"
Accept-Ranges: bytes
Content-Length: 438
Connection: close
Content-Type: text/html

---------- Body ----------
<HTML>
<HEAD>
  <TITLE>Example Web Page</TITLE>
</HEAD> 
<body>  
<p>You have reached this web page by typing "example.com",
"example.net",
  or "example.org" into your web browser.</p>
<p>These domain names are reserved for use in documentation and are not available 
  for registration. See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC 
  2606</a>, Section 3.</p>
</BODY>
</HTML>

Feel free to use it! You just need to give me credit by keeping the javadoc-like comment.