JQuery Datatable

I use to play my faceboook app Chinese poker on my Iphone on my way home from work. My connection is not fast enough and it made the game less interest. . I felt also a litle bit frustrated. I need to do something about it and out comes JQuery Datatable.

JQuery Datatable generates a table based on a set of json data. You can provide callback for formatting the content in each td cell, parse attributes to the table, th and td element. You can also add listeners to do more thing on the rendered table. The good thing of this is that I just save 40% bandwidth. The bad thing is that I am still using table.

JQuery Datatable is available at http://facebook.chinesepoker.isgoodness.com/js/jdatatable.js

JQuery history

Was curious on how window.history.pushState works and since I have been used jQuery history plugin on my Facebook application for years (Thanks to Taku Sano, Takayuki Miwa, Lincoln Cooper!) and still ran on an old version I decided to add support for new feature in HTML5 and rewrite it a litle bit. How does pushState work? shortly it replaces the entire url with a new url in the url bar without causing the browser to load the actual page. It seems to work great with my application since there is a context switch, no change needs to be done. However, I wonder how much job need to be done (to handle url-sharing or when user push on refresh-button) if the content corresponding to the hash does not have a full page version.

Anyway, here it is. Feel free to use and the usage is simple. There are 2 public methods that you can make call

void init( callback [, options] )
   initiate history object
@param callback <Function>
  This function vill be called with the hash as argument when the hash is updated
@param options <Object> 
   Possible properties 
   {noHash: Boolean, dynamic: Boolean, initReload: Boolean, shorten: Boolean}
   If noHash is true pushState will be used if it is available.
   If dynamic is true a callback is always made even if it is the same hash
   if initReload is true the full page will be reload if the user 
   enters the page with a hash 
   If shorten is true scheme and host will be removed from the hash.
   This is useful when using load directly on href
   Default is true for all.
 
void load ( hash [, title] )
@param hash <String>  -  New hash to update
@param title <String> -  Title to set to the document

If your application needs the hash state to complete the page you must set noHash and initReload to false. The iframe solution is not well tested since I don’t have plattform to test but I think it should function as the original one. I have only tested it on win7, IE8 running in mode IE7. If you have a chance please test it!

Issues

  • Chrome seems to break script-tag from serverfbml-tag (script-tag contains fbml-tag which is required in serverfbml-tag). This hapens when listening on popstate-event. Possible workaround is to initiate the history object when first used if there is fbml code.
  • /*
     * jQuery history plugin
     * 
     * sample page: http://www.serpere.info/jquery-history-plugin/samples/
     *
     * Copyright (c) 2006-2009 Taku Sano (Mikage Sawatari)
     * Copyright (c) 2010 Takayuki Miwa
     * Licensed under the MIT License:
     *   http://www.opensource.org/licenses/mit-license.php
     *
     * Modified by Lincoln Cooper to add Safari support 
     * and only call the callback once during initialization
     * for msie when no initial hash supplied.
     * 
     * Modified by Van Nhu Nguyen (2011);
     * redesign
     * use hash token #!
     * delay of iframe creation
     * support popstate and hashchange (html5)
     * always load page if dynamic content
     * shorten the hash if it is a full url
     * reload full page using the hash if enter the page with a hash
     * reload the page when user uses back-button 
     * and navigate to the "entered" page if this does not contain a hash 
     */
     
    (function($) {
    	$.history = (function() {
    		var appState = undefined;
    		var appCallback = undefined;
    		var usePushState = true;
    		var dynamic = true;
    		var hashHandler = {};
    		var interval = 100;
    		var support = {};
    		var reloadIfInitHash = true;
    		var shortenHashPattern = new RegExp("^" + document.location.protocol
    				+ "\/\/" + document.location.host, "i");
    		var shortenHash = true;
    		var encodeHash = false;
    		support.needIframe = ($.browser.msie && ($.browser.version < 8 || document.documentMode < 8));
    		support.pushState = (window.history && window.history.pushState);
    		support.hashChange = ("onhashchange" in window);
    		var hashHandlers = {};
     
    		hashHandlers.pushState = {
    			init : function() {
    				appState = this.get();
    				if (reloadIfInitHash) {
    					var hash = getHash(window);
    					if (hash) {
    						window.location = hash;
    						return;
    					}
    				}
    				$(window).bind("popstate", detectHashChange);
    			},
    			detect : function() {
    				appState = this.get();
    				appCallback(appState);
    			},
    			set : function(hash, title) {
    				window.history.pushState(null, title ? title : "", hash);
    			},
    			get : function() {
    				return window.location.href;
    			}
    		};
     
    		hashHandlers.hashChange = {
    			init : function() {
    				if ((appState = getHash(window))) {
    					if (reloadIfInitHash) {
    						window.location = appState;
    						return;
    					}
    					appCallback(appState);
    				}
    				if (support.hashChange) {
    					$(window).bind("hashchange", detectHashChange);
    				} else {
    					setInterval(detectHashChange, interval);
    				}
    			},
    			detect : function() {
    				var currentHash = this.get();
    				if (currentHash) {
    					if (currentHash != appState) {
    						appState = currentHash;
    						appCallback(currentHash);
    					}
    				} else if (appState) {
    					currentHash = cleanHash(window.location.href);
    					appState = currentHash;
    					appCallback(currentHash);
    				}
    			},
    			set : function(hash, title) {
    				setHash(hash, window);
    				if (title) {
    					document.title = title;
    				}
    			},
    			get : function() {
    				return getHash(window);
    			}
    		};
     
    		hashHandlers.iframe = (function() {
    			var id = "__jQuery_history";
    			var getIframeDocument = function(write) {
    				var doc = $("#" + id)[0].contentWindow.document;
    				if (write) {
    					doc.open();
    					doc.close();
    				}
    				return doc;
    			};
     
    			return {
    				init : function() {
    					var self = null;
    					if ((appState = getHash(window))) {
    						if (reloadIfInitHash) {
    							window.location = appState;
    							return;
    						}
    						self = this;
    					}
    					/* delay call in case this method called before dom ready */
    					$(document).ready(function() {
    						var html = '<iframe id="'
    							+ id
    							+ '" style="display:none" src="javascript:false;" />';
    						$("body").prepend(html);
    						setInterval(detectHashChange, interval);
    						if (appState) {
    							appCallback(appState);
    							self.set(appState);
    						}
    					});
    				},
    				detect : function() {
    					var currentHash = this.get();
    					var windowHash = getHash(window);
    					if (currentHash != appState) {
    						appState = currentHash;
    						setHash(appState, window);
    						appCallback(appState);
    					} else if (currentHash != windowHash) {
    						appState = windowHash;
    						setHash(appState, getIframeDocument(true));
    						appCallback(appState);
    					}
    				},
    				set : function(hash, title) {
    					setHash(hash, getIframeDocument(true));
    					setHash(hash, window);
    					if (title) {
    						document.title = title;
    					}
    				},
    				get : function() {
    					return getHash(getIframeDocument());
    				}
    			};
    		})();
     
    		var setHash = function(hash, win) {
    			win.location.hash = "!"
    					+ (encodeHash ? encodeURIComponent(hash) : hash);
    		};
     
    		var getHash = function(win) {
    			var hash = (win.location.hash).replace(/^#!/, '');
    			return $.browser.mozilla ? hash
    					: (encodeHash ? decodeURIComponent(hash) : hash);
    		};
     
    		var cleanHash = function(hash) {
    			if (shortenHash) {
    				hash = hash.replace(shortenHashPattern, "");
    			}
    			return hash;
    		};
     
    		var getHashHandler = function() {
    			if (support.needIframe) {
    				return hashHandlers.iframe;
    			}
    			if (support.pushState && usePushState) {
    				return hashHandlers.pushState;
    			}
    			return hashHandlers.hashChange;
    		};
     
    		var detectHashChange = function() {
    			hashHandler.detect();
    		};
     
    		return {
    			init : function(callback, options) {
    				if (!$.isFunction(callback)) {
    					throw new Error("callback must be a function");
    				}
    				appCallback = callback;
    				if (options) {
    					if (typeof options.noHash != 'undefined') {
    						usePushState = new Boolean(options.noHash);
    					}
    					if (typeof options.dynamic != 'undefined') {
    						dynamic = new Boolean(options.dynamic);
    					}
    					if (typeof options.initReload != 'undefined') {
    						reloadIfInitHash = new Boolean(options.initReload);
    					}
    					if (typeof options.shorten != 'undefined') {
    						shortenHash = new Boolean(options.shorten);
    					}
    				}
    				hashHandler = getHashHandler();
    				hashHandler.init();
    			},
    			load : function(hash, title) {
    				hash = cleanHash(hash);
    				if (dynamic || hash != appState) {
    					hashHandler.set(hash, title);
    					appState = hash;
    					appCallback(hash);
    				}
    			}
    		};
    	})();
     
    })(jQuery);

    Emacs selected command, config and practice

    This is a collection of things I found usefull regarding emacs. It will grow …

    .emacs
    Add those bellow in the file ~/.emacs
    ;; do not make backup files
    (setq make-backup-files nil)

    ;; php syntax color
    (load “php-mode”)
    (add-to-list ‘auto-mode-alist
    ‘(“\\.php[34]?\\’\\|\\.phtml\\’” . php-mode))

    X forwarding with ssh
    ssh -X your.ssh.domain.com
    ~$emacs somefile
    will start an gui emacs on your local computer. You can also set ForwardX11 to yes for enable X forwarding. This property can be found at /etc/ssh/ssh_config

    ReferenceError #1065: can not create property 0 on … (and #1069)

    Last week, I was trying to create a shuffle function on my array class which extends the native Array class. I got the ReferenceError when running the code bellow. The reason is that Array is a dynamic class and my is not. Since Array is dynamic, it will try to create new property when you try to assign some value to a nonexistent instance variable but MyArray prevents this. If you try this you will see that reference error #1065 is occurred when the array is constructed. If you remove the constructor you will get reference error #1069 instead and it hapens when you try to swap the elements in shuffle method (trying to access a nonexistent instance variable). So make sure that you declare dynamic if you extend a dynamic class or better if editor or compiler gives a warning if you forget …

     
    package vnmedia.common.lang
    {
    	import vnmedia.common.lang.math.Random;
     
    	public class MyArray extends Array
    	{
    		public function MyArray(...parameters)
    		{
    			super(parameters);
    		}
     
    		public function shuffle():void
    		{
    			var a:*, k:int, i:int, r:Random = new Random();
    			for(i = this.length; i > 1; i--){
    				// 0 <= k <= i - 1
    				k = r.nextInt(i);
    				a = this[k];
    				this[k] = this[i-1];
    				this[i-1] = a;
    			}
    		}
    	}
    }

    However, I think the best way to provide a shuffle function for arrays is to define a static ArrayUtil.shuffle as bellow

    package vnmedia.common.lang
    {
    	import vnmedia.common.lang.math.Random;
     
     
    	public class ArrayUtil
    	{
    		public function ArrayUtil()
    		{
    		}
     
    		public static function shuffle(arr:Array):void
    		{
    			var a:*, k:int, i:int, r:Random = new Random();
    			for(i = arr.length; i > 1; i--){
    				k = r.nextInt(i);
    				a = arr[k];
    				arr[k] = arr[i-1];
    				arr[i-1] = a;
    			}
    		}
     
    	}
    }

    Limit access with firewall ufw

    Here is some links on how you can reject requests using firewall ufw.

    http://manpages.ubuntu.com/manpages/jaunty/en/man8/ufw.8.html

    http://www.ubuntu-unleashed.com/2008/05/howto-take-use-setup-and-advantage-of.html

    Limit http methods

    I you have an apache server which you only use GET and POST methods I think it is better that you limit the access. Since there are many methods it is more convinient to use the opposite functions, limitExcept. Bellow is an example how it would look like. The limitExcept directive allows only GET and POST. All other request methods will be rejected.

    NameVirtualHost xxx.xxx.xxx:80
    <VirtualHost xxx.xxx.xxx:80>
            ServerName example.com
     
            DocumentRoot /path/to/doc/root
            <Directory /path/to/doc/root/>
                    <LimitExcept POST GET>
                             Require valid-user
                    </LimitExcept> 
                    Options Indexes MultiViews FollowSymLinks
                    Order Allow,Deny
                    Allow from all
            </Directory>
    ...
    </VirtualHost>

    If you want to deny from the access you can use “Deny from all” instead of “Require valid-user”

    Deny requests from specific ips, configured in virtual host

    Bad thing has hapened to me this holliday. The server has been attacked from serveral ips or more precis they used my server as proxy for attacking other targets. Lucky for me that my server is not configured for this kind of attacks. However, they kept request my server and I wanted that apache would deny all requests from those ips. This one is really tricky. I have configured for denying all except some ips, such as local, but never configured for denying some ips and allow the rest. I started reading apache docs and tried to understand. The result is as following:

    NameVirtualHost xxx.xxx.xxx:80
    <VirtualHost xxx.xxx.xxx:80>
            ServerName example.com
     
            DocumentRoot /path/to/doc/root
            <Directory /path/to/doc/root/>
                    Options Indexes MultiViews FollowSymLinks
                    Order Deny,Allow
                    Deny from xx.xxx.xxx.xxx
                    Deny from xxx.xxx.xxx.xxx
                    Deny from ...
                    # ips that not matched deny list will be permitted
            </Directory>
    ...
    </VirtualHost>

    Be aware that you can not use “Allow from all” in the end since apache deny a request only if it does not match an allow condition. So we have a deny list to match against. Ips that are not matched ips in this list will be permitted.

    It says that there are no good way to protect against this kind of attacks. I hope that I can get in touch with those ip-owners and hopefully they can help me.

    Some good pages

    http://wiki.apache.org/httpd/ProxyAbuse

    http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html

    How to get/kill process in ubuntu

    For example if process name is php, you can get a list of jobs by using the command bellow

    ps aux | grep php

    Result

    root      7749  0.0  0.1   3236   796 pts/2    S+   16:09   0:00 grep php
    root     19292  1.8  5.6  90624 28528 ?        S    01:36  16:09 php test.php

    To kill a process you can use kill with a specific pid, for instance 19292 above

    kill 19292

    And kill all php process

    killall -9 php

    Howto enable ssl and create self-signed ssl certificate

    Enable ssl
    In my case it was simple. I just ran this line

    a2enmod ssl

    Generate self-signed ssl certificate
    Solution: http://www.akadia.com/services/ssh_test_certificate.html

    Just follow instruction carefully. Make sure that i step 2 you need to enter a correct “Common Name”, ie your domain. Step 5 and 6 also different for different distributions and installation …

    Summary in case link above is not available anymore:
    Step 1: Generate a Private Key

    openssl genrsa -des3 -out server.key 1024

    Step 2: Generate a CSR (Certificate Signing Request)

    openssl req -new -key server.key -out server.csr

    Step 3: Remove Passphrase from Key

    cp server.key server.key.org
    openssl rsa -in server.key.org -out server.key

    Step 4: Generating a Self-Signed Certificate

    openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

    Step 5: Installing the Private Key and Certificate
    (or the location you want to store)

    cp server.crt /usr/local/apache/conf/ssl.crt
    cp server.key /usr/local/apache/conf/ssl.key

    Step 6: Configuring SSL Enabled Virtual Hosts

    SSLEngine on
    SSLCertificateFile /usr/local/apache/conf/ssl.crt
    SSLCertificateKeyFile /usr/local/apache/conf/ssl.key
    SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

    Step 7: Restart Apache and Test

    New problem:
    ssl_error_ssl2_disabled
    Solution in ssl.conf:
    # enable only secure protocols: SSLv3 and TLSv1, but not SSLv2
    ## disabled this one
    #SSLProtocol all -SSLv2
    ## use this instead
    SSLProtocol all

    See also http://httpd.apache.org/docs/2.0/ssl/ssl_howto.html

    New problem:
    sec_error_untrusted_issuer due to self-signed SSL Certificate
    But it is no problem for me since this is for my own usage and test…

    How to set default OS in boot.ini

    This is a boot setup for Microsoft Windows XP Professional and Ubuntu (wubi) where we have Windows XP as default and will be preselected on start of computer. This content can be found in c:\boot.ini (be aware this is a hidden system file).

    [boot loader]
    timeout=15
    default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
     
    [operating systems]
    multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn
    c:\wubildr.mbr="Ubuntu"

    If we wish to start with Ubuntu instead we change default operative system as following

    [boot loader]
    timeout=15
    default=c:\wubildr.mbr
     
    [operating systems]
    multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn
    c:\wubildr.mbr="Ubuntu"