Just Another Blog

Are you thinking what I'm thinking?

Tuesday, July 25, 2006

Keroro軍曹網上預售開始

再貼一次,以方便我自己:AMC又一城百老匯電影中心 :-P

Monday, July 24, 2006

CSV Parser for PHP

fgetcsv() sucks because it doesn't handle quoted newlines. So I wrote a simple CSV parser class for PHP myself, based on the JavaScript version that I wrote earlier (it doesn't handle quoted newlines either). Not prefect (e.g. newlines are standardized to \n, and string matching works only for ASCII or UTF-8), but does what I want it to do.

/**
 * CSV file parser
 * Currently the string matching doesn't work
 * if the output encoding is not ASCII or UTF-8
 */
class CsvFileParser
{
    var $delimiter;         // Field delimiter
    var $enclosure;         // Field enclosure character
    var $inputEncoding;     // Input character encoding
    var $outputEncoding;    // Output character encoding
    var $data;              // CSV data as 2D array

    /**
     * Constructor
     */
    function CsvFileParser()
    {
        $this->delimiter = ",";
        $this->enclosure = '"';
        $this->inputEncoding = "ISO-8859-1";
        $this->outputEncoding = "ISO-8859-1";
        $this->data = array();
    }

    /**
     * Parse CSV from file
     * @param   content     The CSV filename
     * @param   hasBOM      Using BOM or not
     * @return Success or not
     */
    function ParseFromFile( $filename, $hasBOM = false )
    {
        if ( !is_readable($filename) )
        {
            return false;
        }
        return $this->ParseFromString( file_get_contents($filename), $hasBOM );
    }

    /**
     * Parse CSV from string
     * @param   content     The CSV string
     * @param   hasBOM      Using BOM or not
     * @return Success or not
     */
    function ParseFromString( $content, $hasBOM = false )
    {
        $content = iconv($this->inputEncoding, $this->outputEncoding, $content );
        $content = str_replace( "\r\n", "\n", $content );
        $content = str_replace( "\r", "\n", $content );
        if ( $hasBOM )                                // Remove the BOM (first 3 bytes)
        {
            $content = substr( $content, 3 );
        }
        if ( $content[strlen($content)-1] != "\n" )   // Make sure it always end with a newline
        {
            $content .= "\n";
        }

        // Parse the content character by character
        $row = array( "" );
        $idx = 0;
        $quoted = false;
        for ( $i = 0; $i < strlen($content); $i++ )
        {
            $ch = $content[$i];
            if ( $ch == $this->enclosure )
            {
                $quoted = !$quoted;
            }

            // End of line
            if ( $ch == "\n" && !$quoted )
            {
                // Remove enclosure delimiters
                for ( $k = 0; $k < count($row); $k++ )
                {
                    if ( $row[$k] != "" && $row[$k][0] == $this->enclosure )
                    {
                        $row[$k] = substr( $row[$k], 1, strlen($row[$k]) - 2 );
                    }
                    $row[$k] = str_replace( str_repeat($this->enclosure, 2), $this->enclosure, $row[$k] );
                }

                // Append row into table
                $this->data[] = $row;
                $row = array( "" );
                $idx = 0;
            }

            // End of field
            else if ( $ch == $this->delimiter && !$quoted )
            {
                $row[++$idx] = "";
            }

            // Inside the field
            else
            {
                $row[$idx] .= $ch;
            }
        }

        return true;
    }
}

Friday, July 21, 2006

Flash disabled? No more! ;-)

Currently I'm working on a website that uses Flash on one of its pages. As you should already know, ActiveX interaction is disabled in Internet Explorer because of the Eolas issues. You can read more about this in the Activating ActiveX Controls article published in MSDN.

Since I want to write in valid HTML only, the HTML code should be written as something like this:

<object type="application/x-shockwave-flash" data="example.swf">
<img src="example.png" alt="Alternate content">
</object>

This works perfectly on anything but IE. In order to make IE happy, we have to add an extra param element:

<object type="application/x-shockwave-flash" data="example.swf">
<param name="movie" value="example.swf">
<img src="example.png" alt="Alternate content">
</object>

Now IE knows where the movie is and loads it properly. But the ActiveX control is disabled from user interaction. So a little JScript is needed here.

By googling, I came up with this informative blog post: Flash interaction disabled in Internet Explorer. The solution by David Merrilees is almost perfect. The idea is to remove the data attribute not recognized by IE, recreate the whole object element using outerHTML, and using CSS to prevent flickering.

But he is missing one important thing: what about user with JScript disabled, CSS enabled and Flash plugin installed and enabled? They will see nothing because the visibility is set to hidden initially.

So, instead of hiding the objects for all IE users, what about hiding the objects for all IE users with JScript enabled? While ideally we should use DOM method to modify the HTML content, we can't do so here as it will be already too later if we hide the Flash movie until window.onload. Fortunately, we can use document.write to output stylesheet that hide the objects before the document is loaded:

document.write( '<style type="text/css">' );
document.write( 'object { display: none }' );
document.write( '</style>' );

While I personally hate using document.write, it is OK here as IE only supports HTML (specifically (X)HTML served as text/html).

So the rest is almost identical to David Merrilees's solution, except that I've used display: none instead of visibility: hidden as I found it not working for me.

HTML

Conditional comment is used so that only IE will run this JScript (activex_fix.js). Be sure that it is included inside the head element, not the body element.

<!--[if IE]>
<script type="text/javascript" src="activex_fix.js"></script>
<![endif]-->

JScript (activex_fix.js)

window.attachEvent is used, so that it won't mess up other cross-browser onload handlers like window.onload (or better, DOMContentLoaded).

// Hide objects to prevent flickering when recreating the objects
document.write( '<style type="text/css" id="activex_fix">' );
document.write( 'object { display: none }' );
document.write( '</style>' );

// Recreate and reveal the hidden objects
window.attachEvent( "onload", function()
{
    // Recreate the objects
    var objects = document.getElementsByTagName( "object" );
    for ( var i = 0; i < objects.length; i++ )
    {
        var object = objects.item(i);

        // Fix flash object
        if ( object.type == "application/x-shockwave-flash" )
        {
            if ( object.getAttribute("data") )
            {
                object.removeAttribute( "data" );
            }
            object.outerHTML = object.outerHTML;
        }
    }

    // Reveal the objects by removing the stylesheet created by document.write
    var stylesheet = document.getElementById("activex_fix");
    stylesheet.parentNode.removeChild( stylesheet );
} );

This idea should also work for Opera 9, which follows IE's decision to disable user interaction with plugin content. I didn't bother to deal with it as it is not required by the client. :-P

Monday, July 17, 2006

距離藍星侵略還有……十天

Keroro超劇場版將於7月27日在香港上映。不知香港的代理怎麼搞的,在上映Keroro的廿多間電影院中,只有AMC又一城百老匯電影中心提供原裝日文版播放,其餘大部份的都是「嘻哈哈」惡搞廣東話配音,天哦!

我看為安全計,需要於三日前在網上預訂日文版的戲票了……

Tuesday, July 04, 2006

Firefox passed Acid2 test

Well, it is passed on the reflow branch, which will become Firefox 3. So there are still months (or 1 to 2 years) to wait…