Internet Explorer 8 background disappeared (or becomes white) bug with jQuery 1.6 (or 1.6.1)

Latest Update: This bug has been fixed by jQuery 1.6.2 released on 30/06/2011.

If your project has upgraded to jQuery version 1.6 or jQuery 1.6.1, then you should make sure it works fine with Internet Explorer 8 (IE8) before you hit the publish button. This is my two cents.

What’s wrong with jQuery 1.6+ and IE8?

If your website has used background image or color other than “white”, and if it is viewed by IE8, the website will be rendered just right…but only at the first sight…and then the background will goes plain WHITE soon after that, automatically. Only IE8 will show you this “magic”.

There is a bug in jQuery 1.6 and 1.6.1 and is reported here. Unfortunately we have suffered this strange behavior for several days before we realize that this “magic” is associated with jQuery.

Solution

There is also a solution suggested in the above mentioned bug report. To save your time, I quote it here with credit goes to the original author:

Same problem on version 1.6.1

The bug can be resolved on ligne 1288 from jquery-1.6.1.js.

Replace :

“documentElement.insertBefore( body, documentElement.firstChild );”

by

“documentElement.appendChild( body );”

Hope this post could save you a day or several hours. Happy coding!

Really simple CSS compression function in PHP

Thanks to the simple syntax of CSS, we can easily compress the CSS code with a PHP function of only 3 lines of codes.

Since the compression will remove all the code indention and comments so it should be apply to production codes only.

Here is the CSS compression that I used in my projects.


function CSS_Compress(&$css){
    //new lines, multiple spaces/tabs/newlines
    $css = preg_replace('/[\r\n\t\s]+/s', ' ', $css);
    //remove comments
    $css = preg_replace('#/\*.*?\*/#', '', $css);
    //remove extra single spaces
    $css = preg_replace('/[\s]*([\{\},;:])[\s]*/', '\1', $css);
}

Fixing the drive letter for RAMDisk without saving and loading drive image – A solution for Dataram RAMDisk

[Note: 我們另外為此文章撰寫了中文版本,請看這篇。]

Using RAMDisk

One of the most popular RAMDisk solution is Dataram RAMDisk. It is FREE to setup a RAMDisk of max. 4GB size, running in both 32-bit & 64bit Windows systems (Usually other RAMDisk solutions would charge quite much for a 64bit version.) with promising performance. If you are using a 32bit Windows, Dataram RAMDisk  can access the RAM outside the address range(the so-called 4GB RAM limit in 32bit Windows) so your RAM wouldn’t be wasted. I have been using Dataram RAMDisk since I upgraded to 64bit Windows 7, and I’m totally satisfied with its performance and stability.

The problem

My only complain about Dataram RAMDisk is that the drive letter of the RAMDisk is dynamically assigned on each time it runs. So today I have “K:” as my RAMDisk and have my browser cache folder configured to “K:”. On the next morning if I have plugged in an USB drive (or assigned another network drive) before the RAMDisk being created, my USB / network drive will occupy the drive letter “K:” so the RAMDisk will become “L:”. This will be a big disaster because all the cache will be written into the USB / network drive instead of the RAMDisk. So I want the RAMDisk keeps a consistent drive letter all the time.

Official solution

In the user manual, Dataram provides a solution through saving the RAMDrive into a drive image file once and loading it back on every time the computer starts. I personally don’t like it because :

  1. It creates a large disk image file (same size as the RAMDisk) on the harddisk, and
  2. It takes quite much overhead to load the disk image file on every booting.

My solution : A DOS Batch script

Instead, I write my own solution using just a 3-line (without comments) DOS batch script. Thanks to the feature that Dataram RAMDisk allows users to specify a Drive Label and keep it consistent all the time. So my trick is: When the system starts, the script will find the dynamic RAMDisk drive letter (e.g. “K:”) according to the disk label, then create a consistent drive letter (e.g. “R:”) that mounts to the dynamic drive letter (“K:”).

Step-by-step walk-through

To make it simple, I post the walk-through here:

1. Create the batch file.

Create a new .bat file (e.g. C:\StartUp.bat) with the following content:

(Update: Please note that batch script takes line-break as “end of command”, so if you find the script doesn’t work at your PC, try removing extra line-breaks. See the comments for more details.)

@rem assign the key variables.
SET _label=RAMDISK
SET _ramdrive=R:

@rem Below is the magic happens : Find the drive letter which has volume name set as _label, and mount it to _ramdrive
FOR /F "skip=1 tokens=1 delims=: " %%a IN ('wmic logicaldisk where "VOLUMENAME='%_label%'" get caption') DO ( subst %_ramdrive% %%a:\ )

@rem if you want to copy some files or mount your cache folders, do it after this line.

If you are interested in how this magic script works, read below. Otherwise you can safely skip it and go on step 2.

The command :

wmic logicaldisk where "VOLUMENAME='RAMDISK'" get caption

will run once, and gives such result :

Caption
K:

This result is returned as a string. Then a for-loop will go through the result string line-by-line. For each line, the string is split by “:” into parts and the first part will be stored into variable %%a.

The for-loop options explained:

“skip=1” : Skip the first line (i.e. “Caption”). The first line is the header, we are not interested in it.

“tokens=1” : Only get the first part after the split. The first part is the drive letter.

“delims=:” : Use “:” as the delimiter to split the string.

So at the end the variable  %%a would be assigned with value “K”.  Then the subst command becomes

subst R: K:\

which will mount the R: to the K:

2. Specify the Drive Label for RAMDisk

Open the Dataram RAMDisk Configuration Utility, check the “Disk Label” box and enter a disk label “RAMDISK” in the text box below.

You may assign name other than “RAMDisk”, but if you do, remember to update the DOS batch script with

SET _label=TheNewDriveLabel

3. Assign the startup script

So that the computer will run the StartUp.bat each time when the computer boots. There are a few steps to do this.

3.1 Press Win + R (or find “Run” on start menu), a “Run” dialog will appear. Enter “gpedit.msc” and select the “gpedit” in Programs.

Note: For Win7 Home version where the gpedit.msc is not bundled, you may consider using the Task Scheduler to launch the script after Windows starts. Please visit this page for the details: http://windows.microsoft.com/en-US/windows7/schedule-a-task

3.2 In the “Group Policy Editor”, click “Local Computer Policy” -> “Windows Settings” -> “Scripts(Startup/Shutdown)”  then on the right side, click “Startup”

Startup script

3.3 On the “Scripts” tab, click “Add…” and add the “C:\StartUp.bat” to the list. Click “OK” to finish.

4. That’s it!

Now restart the computer and there will be a “R:” drive that is mounted to the RAMDisk, no matter what drive letter the RAMDisk actually is.

Conclusion

The advantage of this solution over the Dataram’s solution is: this solution doesn’t require a large disk image file, thus the overhead of reading and loading the disk image is none. This reaults in a faster system booting and less disk space occupied.

A final note about this solution: There will be two drive letters assigned to the same drive. My solution creates a new virtual drive with consistent drive letter (“R:”), and mount it to the actual RAMDisk (“K:”). Although the RAMDisk can always be referred as “R:”,  the “K:” is still there. Currently I have no idea how to remove or hide the “K:” in the batch script. So you will end up with both “K:” and “R:” accessing to the same drive.

Comments are welcomed!

Updated-19/06/2011: Merijn (see comments below) suggested a very good approach using the “diskpart” command instead of “subst”. It is a better solution because it alters the drive letter of the RAMDisk directly, so at the end you won’t have two drives on “My Computer” accessing the same drive.

Check if an array is associative or sequential(indexed) in PHP

In PHP, depending on the type of the keys in an array, the array can be identified as associative (if the key is a string like $arr[‘mykey’]) or sequential (some may call it indexed, i.e. if the key is an integer like $arr[1], $arr[2], etc.).

Although the internal representation of both associative and sequential arrays are the same in the PHP’s core (Both are ordered map), in some situations we still need to differentiate one from another. There is no built-in function to tell if an array is associative or sequential, so we have to write our own.

The most popular method to check this is to compare the keys of an array with the result of range(), like this, this and this:


function isAssoc($arr){
	return array_keys($arr) !== range(0, count($arr) - 1);
}

The problem of using range() is that it only works when the keys of the array are continuous integer numbers. That is, the keys of an array has to be {0,1,2,3,4,…..}. If the array’s smallest key is not 0 but 1, like {1,2,3….}, or there is a “hole” in between, like {1,2,4,5,8,…}. They will be treated as associative array.

Whether this method makes sense depends on how you define the term ”sequential array” and “associative array”. Unfortunately I didn’t find any “official” definition about them in PHP’s manual (as they doesn’t matter to PHP). My personal definition is that when there exist a non-integer key in an array, the array is not sequential. So for the arrays with keys like  {1,2,100, 1000, 1500}, it is still sequential. With this logic, my method to check associative / sequential arrays is this:


function is_asso($a) {
	foreach(array_keys($a) as $key)
		if (!is_int($key)) return TRUE;
	return FALSE;
}


This function will check every keys in the array until it find one that is not an integer, then it quit the loop and tell it is associative array. This function can handle sequential arrays with any starting index, and any hole in between.

Link exchange? Do research before you put a link for someone.

What is Link Exchange

In case you are not familiar with SEO, let me elaborate what “link exchange” is. Usually as a website owner, I would like to have more other websites (of good quality) link to my website so as to increase the exposure and page-rank of my website. If I think the websites linking to mine is good, I would put links to them in my website as a reward, so that both of us will be benefited. This is so call “link exchange” and it is a win-win game, as I always believe.

The story

So let’s start talking about the story: Yesterday when our project website received an invitation about advertising on us and propose link exchange. The sender is a medium-size, international job hunting website. The advertising campaign sounds quite good but unfortunately it’s technically not feasible for our website (I’m not going to cover that in this post.). What we wanna tell today is their link exchange proposal. They propose that we add a link to their website while they do the same for us as well. They provided us an URL to a page (Let’s call it “Secret Page”, and the website FoolingYouWebsite.com. You will know why soon.) showing how the links would look like in their website. The “Secret Page” has a quite simple and direct URL structure: www.FoolingYouWebsite.com/SecretPage.html.

We were almost agree to the proposal, right before we do further investigation: We just can’t find a way to go to the “Secret Page” from the website’s entrance, unless we type in its URL directly. This means that, the “Secret Page” actually is isolated from the website’s main pages. Since it’s isolated, the search engine may be able to find and index the “Secret Page”. If search engine cannot find and index the page, anything on the page is meaningless. This is one doubt.

Secondly we checks the robots.txt of the website (www.FoolingYouWebsite.com/robots.txt). Surprisingly we find that the “Secret Page” is “Disallowed” from ALL robots! What does it mean? It means that ALL search engines will NEVER read that page even when they found that page. What a big cheating!

Conclusion

Not mentioning we refused the proposal, we are writing here to let people learn about this. We suggest every website owner have to do a research on the website as well as the robots.txt for every link exchange request.

How to know a blog comment is actually a spam?

A surprise

Zome off is a new and little popular blog that is even not showing on any search result by any keywords (except the word ‘zomeoff’ which nobody would be interested to look for it) in any search engine. So it’s really a big surprise that many people are trying to leave comment on my blog, not once but several times. (But up to now you still don’t see any comments on the blog, huh?)

The latest comment is this:

trying to find you on facebook, wats ur profile

— frostwire

I almost reply, but…

Thanks to WordPress that I can approve all the comments before it’s gone published. As this is a so personal request, I wouldn’t give out my facebook profile before knowing who “frostwire” actually is. There is quite complete contact information (website, email) provided by “frostwire”. So I start with checking the website. It’s about a P2P software and gives not much clue about the person “frostwire”. Then I look into the email address which is an AOL email address. Then I check the IP of the comment (using IP 2 location services such as hostip.info). It shows that the IP is from “Richardson, TX, UNITED STATES”. Still, not much clue is found. Finally I try to google with the whole comment, that is, search the whole sentence “trying to find you on facebook, wats ur profile” in google. Surprisingly there are more than 2000 results, most out of them are WordPress blogs. At this step I realize that I’m fooled.

Why?

So why does “forstwire” leave such a comment on the blog? Actually the comment contains no selling messages so leaving such comment actually gives no help to anyone. However, I think of 2 reasons that, no matter the comment gets published or not, the “forstwire” still win.

  1. Increasing the traffic of the website:“forstwire” provides a website URL when submitting the comment, so bloggers like me who wonder the background of the commenter will definitely visit the submitted website. So even before we ban the comment, we have been cheated to pay a visit to that website.
  2. Collecting personal infomation:Since the message itself is quite personal, and “forstwire” provides an email address. Blogger may reply to the email instead of responding publicly on the blog (Unfortunately there are some bloggers who do give out their personal information as a reply to this message on their blogs.) . I noticed that the “trying to find you on facebook, wats ur profile” message appears mostly on WordPress-empowered blogs, so I suspect that actually “forstwire” actually is using some kind of robot software (targeted on WordPress) to make the comment in large batch scale. If this is true, then there is no challenge for “forstwire” to collect the blogger’s response and extract personal information, such as email address from it. No matter how the blogger replies (by private email or public reply), “forstwire” have your information anyway.

Conclusion: Bloggers must be aware of spammers!

This is so far my first time come across spammers who is targetting on bloggers instead of the blog readers. So blogger must be aware of it. When a blogger has doubt about a comment, GOOGLE it with the whole message. Generally spammers do spamming in ways that are automatic and in large scale, so if the message is a spam, you will see a lot of victims in the google result. Do not visit any website before you can trust the commenter.

PHP – isset() vs array_key_exists() : a better way to determine array element’s existence

The story

In the CourseYou project, we’re asked to check if an element is set in an array. That is,  we’re asked to determine whether $Arr[‘MyElement’] exists.

So we use the following code as a start.

<?php 
if (isset($Arr['MyElement'])) { 
     ... do my stuff ... 
} ?> 

This code works fine, but, it works fine for most of cases only. In some other cases (and it’s quite often actually), using this code  to check the existence of an array element can be very DANGEROUS.

What’s wrong with isset()?

Perhaps isset() is one of the most frequently used function that do a very frequent task: determine if a variable has been set. It is simple, and more importantly is FAST, is very FAST. However, the returned result of isset() can be misleading sometimes.

According to the PHP’s manual: isset() — Determine if a variable is set AND is not NULL

So the case that the isset() cause you danger is: the element does exist in the array but it is set NULL. i.e. $Arr[‘MyElemenet’] =NULL; In this case, isset() always return FALSE.  Professional programmers should be aware of this.

The right solution: array_key_exists()

The right way to check  if an element exists in an array is to use array_key_exists(). The array_key_exists() will tell if the given key or index has been “created” in the array regardless the value of the element. So to tell if elements ‘MyElement’ exists in the array $Arr, we should use this:

<?php if (array_key_exists('MyElement', $Arr)) { ... do my stuff ... } ?> 

Why array_key_exists() still sucks?

However, array_key_exits() still sucks. Yes, it’s more reliable than isset(), but it’s SLOW.  We benchmarked the array_key_exists() and isset() methods as shown below and find that array_key_exists() is almost 5 times slower than isset().

To take the speed advantage of isset() while keeping the reliable result from array_key_exists(), we combined the both: Usually an element being set NULL is a rare case, so in most of the time, isset() is still reliable. When isset() fails, we should do an additional checking by array_key_exists() to double confirm that the key really doesn’t exist. It turns out that the below code works the best:

<?php 
if (isset($Arr['MyElement']) || array_key_exists('MyElement', $Arr)) { 
      ... do my stuff ... 
} ?>


The beauty of PHP (also many other modem languages) is that it doesn’t require the whole conditional statement being fully parsed. So the PHP engine actually only evaluate the result of isset(). if isset() returns FALSE, it then evaluate array_key_exists(). If isset() returns TRUE, array_key_exists() is never evaluated. That’s saying the sequence of the two conditions cannot be reversed.

Benchmarking

We did a simple benchmarking base on the isset(), array_key_exists() and the combined method, and the result of the combined method is very promising.

<?php 
$a = array('a'=>1,'b'=>2,'c'=>3,'d'=>4, 'e'=>null); 
$s = microtime(true); 
for($i=0; $i<=100000; $i++) { 
     $t= array_key_exists('a', $a); //true 
     $t= array_key_exists('f', $a); //false
     $t= array_key_exists('e', $a); //true 
} 

$e = microtime(true); 
echo 'array_key_exists : ', ($e-$s); 

$s = microtime(true); 
for($i=0; $i<=100000; $i++) { 
     $t = isset($a['a']); //true 
     $t = isset($a['f']); //false
     $t = isset($a['e']); //false 
} 

$e = microtime(true); 
echo 'is_set : ' , ($e-$s); 

$s = microtime(true); 
for($i=0; $i<=100000; $i++) { 
     $t= (isset($a['a']) || array_key_exists('a', $a)); //true 
     $t= (isset($a['f']) || array_key_exists('f', $a)); //false
     $t= (isset($a['e']) || array_key_exists('e', $a)); //true 
} 

$e = microtime(true); 
echo 'isset() + array_key_exists : ', ($e-$s); 
?> 

The benchmarking result (average):

  • array_key_exists() : 308 ms
  • is_set() : 4.7ms
  • isset() + array_key_exists() :217ms

Latest Update: I have packaged this method to a single function, and added the checking of element existence in multiple-dimension arrays. Please check my another post: A complete element existence checking function for PHP.

Equal (==), identical (===) and array comparison in PHP

Equal (==)

If you use equal (==), you are allowing type conversion which means PHP will try to convert the two sides into the same type and then do the comparison. So even if the two sides are NOT the same thing, they MAY still be treat as the SAME.

Consider this code:

<?php 
$left = "C"; 
$right = 0; 
var_dump($left == $right); 
?> 

Output:

bool(true)

"C" equals to 0 ?? The logic behind is : $left is a String of "C", since it is compared to $right which is a number, PHP will first convert the String "C" to a number by parsing "C" as a numeric value which is unfortunately 0, then this 0 is compares to $right which is 0, so although strange the comparison result is logically "true".

Identical (===)

On the contrary, when identical (===) is used in the comparison, PHP will not do any type conversion. PHP firstly check if the both side is of the same type. If not, then just return false. If they are of the same type, it then compare the values to see if they are the same. So it should be no wonder that the output of the below codes is "false":

<?php 
$left = "5"; 
$right = 5; 
var_dump($left === $right); 
?> 

Output:

bool(false)

What if they are Arrays?

Consider this code:

<?php 
$a = array('a'=>1, 'b'=>2, 'c'=>3);                 //reference array 
$b = array('a'=>1, 'b'=>2, 'c'=>3);                //equal and identical 
$c = array('a'=>1, 'b'=>2);                                //one element less 
$d = array('a'=>1, 'b'=>100, 'c'=>3);          //one element has different value 
$e = array('a'=>1, 'c'=>3, 'b'=>2);               //same key-value pairs but different sequence 
echo '$a == $b is ', var_dump($a ==$b); 
echo '$a === $b is ', var_dump($a === $b); 
echo '$a == $c is ', var_dump($a ==$c); 
echo '$a === $c is ', var_dump($a === $c); 
echo '$a == $d is ', var_dump($a ==$d); 
echo '$a === $d is ', var_dump($a === $d); 
echo '$a == $e is', var_dump($a ==$e); 
echo '$a === $e is', var_dump($a === $e); 
?> 

Output:

$a == $b is bool(true) 
$a === $b is bool(true) 
$a == $c is bool(false) 
$a === $c is bool(false) 
$a == $d is bool(false) 
$a === $d is bool(false) 
$a == $e is bool(true) 
$a === $e is bool(false) 

So we conclude that:

  • When two arrays are same in each key/value pair, and they have the same amount of elements, and the elements are in the same sequence, they are equal (==) and identical (===),
  • If one array has less elements than another one, they are neither equal (==) nor identical (===).
  • If one of the elements in an array has different value, the two arrays are neither equal (==) nor identical (===)
  • If two arrays have the same element, but different sequence, they are equal (==) but NOT identical (===).

Reference:

  1. Type conversion during comparison in PHP (they call it type juggling): http://php.net/manual/en/language.types.type-juggling.php
  2. Type comparisons in PHP: http://php.net/manual/en/types.comparisons.php