Tuesday, July 25, 2006
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…