        //Feature control objects for top most layer v1.0 Updated: 20080826 by Isaac Yong
        /*
            myObj.layer = layer;
            myObj.addFeature=addFeature;
            myObj.addFeatures=addFeatures;            
            myObj.enableFeature=enableFeature;                        
            myObj.disableFeatures=disableFeatures;
            myObj.disableFeature=disableFeature;
            myObj.disableAll=disableAll;
            myObj.refresh=refresh;            
            myObj.refreshAll=refreshAll;            
            myObj.getFeature=getFeature;
            myObj.containsFeatureKey=containsFeatureKey;
            myObj.containsFeatureCacheKey=containsFeatureCacheKey;
            myObj.containsFeatureActKey=containsFeatureActKey;
            myObj.retainFeatures=retainFeatures;
                    
        sample code:
            objMyKMLs = new objKMLs(map);
            objMyKMLs.addLayer("seleter_route", "kml/test_bus_route_morning.kml", true, true);
            objMyKMLs.enableLayer("seleter_route");        
        */        
        
        function objFeatures(objLayer){
            var myObj=new Object();
            var htbFeatures = new Hashtable();
            var htbFeaturesAct = new Hashtable();
            var htbFeaturesCache = new Hashtable();
            var htbKmlReader = new Hashtable();
            var featureLayer = objLayer;
            
            function addFeature(strId, strURL, boolRefresh, intSeq){
                htbFeatures.put(strId,new objFeature(strId, strURL, boolRefresh, intSeq));
            }
            
            function addFeatures(arrInput){
                for(var i = 0; i<arrInput.length;i++){
                    htbFeatures.put(arrInput[i][0], objKML(arrInput[i][0], arrInput[i][1], arrInput[i][2], arrInput[i][3]));                    
                }    
            }
            
            function enableFeature(strId, boolForce){
                if (boolForce == "" || boolForce == null)
                    boolForce = false;
                    
                if(!htbFeaturesCache.containsKey(strId) || boolForce){
                    //alert(!htbFeaturesAct.containsKey(strId) + " " + boolForce);                    
                    var objFeature = htbFeatures.get(strId);
                    htbKmlReader.put(strId, new kmlReader(strId));
                    OpenLayers.loadURL(objFeature.url, "", htbKmlReader.get(strId), (htbKmlReader.get(strId)).parseData, (htbKmlReader.get(strId)).onFailed);
                } else{
                    var cloneFeatures = new Array();
                    for(var i=0;i<htbFeaturesCache.get(strId).length;i++){
                        cloneFeatures[i] =htbFeaturesCache.get(strId)[i].clone(); 
                    }
                    htbFeaturesAct.put(strId,cloneFeatures);
                    featureLayer.addFeatures(cloneFeatures);
                }

                return htbFeaturesCache.get(strId);
            }                        
            
            function enableFeatures(arrId){
                var features = new Array();
                for(var i = 0; i<arrId.length;i++){
                    var feature = enableFeature(arrId[i]);
                    features[i] = feature; 
                }
                
                return features;
            }
            
            function kmlReader(strId){
                this.id = strId;                
                this.parseData = function (req){
                    var g =  new OpenLayers.Format.KML({extractStyles: true});              
                    var features = g.read(req.responseText);
                    //disable the feature here in case user keep refreshing and cause concurrency issue.
                    if(htbFeaturesAct.containsKey(strId)){
                        disableFeature(strId);
                        htbFeaturesAct.remove(strId);
                        htbFeaturesCache.remove(strId);
                        }
                    /*
                    //TODO: load features based on current boundaries
                    var currBounds = (map.getExtent()).toArray();
                    var arrTempFeatures = new Array();
                    for(var i=0,cnt=0;i<features.length;i++,cnt++){
                        if ( currBounds[0]<=features[i].geometry.x && currBounds[1]<=features[i].geometry.y 
                        && currBounds[2]>=features[i].geometry.x && currBounds[3]>=features[i].geometry.y ){
                            arrTempFeatures[cnt] = features[i];
                        }
                    }*/
                    
                    featureLayer.addFeatures(features);
                    htbFeaturesAct.put(this.id,features);
                    var cloneFeatures = new Array();
                    for(var i=0;i<features.length;i++){
                        cloneFeatures[i] =features[i].clone(); 
                    }
                    htbFeaturesCache.put(this.id,cloneFeatures);
                }
                /* For reference
                    > var kml = new OpenLayers.Format.KML();
                    > var kmlOut = kml.write(map.layers[1].features);
                    > 
                    > map.layers[1].eraseFeatures(map.layers[1].features);
                    > map.layers[1].features = [];
                    > 
                    > var features = kml.read(kmlOut);
                    > 
                    > features;
                    > 
                    > map.layers[1].addFeatures(features);                
                */                                         
                this.onFailed = function(req){
                    //should we reload? how many times should we reload?
                }
            }
            
            function disableFeature(strId){
                if(htbFeaturesAct.containsKey(strId)){
                    //featureLayer.eraseFeatures(htbFeaturesAct.get(strId));
                    featureLayer.destroyFeatures(htbFeaturesAct.get(strId));
                    htbFeaturesAct.remove(strId);
                }
            }
            
            function disableFeatures(arrId){
                for(var i = 0; i<arrId.length;i++){
                    disableFeature(arrId[i]);
                }
            }
            
            function disableAll(arrActKeys){
                if (arrActKeys == null || arrActKeys == "")
                    arrActKeys = htbFeaturesAct.keys();
                disableFeatures(arrActKeys);
            }
            
            function refresh(strId){                                
                //disableFeature(strId);
                enableFeature(strId,true);
            }
            
            function refreshAll(){
                var arrActKeys = htbFeaturesAct.keys();
                for(var i = 0; i<arrActKeys.length;i++){                    
                    var objFeature = htbFeatures.get(arrActKeys[i]);
                    if(objFeature.refresh)
                        refresh(arrActKeys[i]);
                }
            }

            function getFeature(strId){
                return htbFeaturesAct.get(strId);
            }                        

            function containsFeatureKey(strId){
                return htbFeatures.containsKey(strId);
            }
            
            function containsFeatureCacheKey(strId){
                return htbFeaturesCache.containsKey(strId);
            }
            
            function containsFeatureActKey(strId){
                return htbFeaturesAct.containsKey(strId);
            }

            function retainFeatures(arrStrId){
                var arrActKeys = htbFeaturesAct.keys();
                var boolSkip;
                for(var i = 0; i<arrActKeys.length;i++){
                    boolSkip = false;
                    for(var j = 0; j<arrStrId.length;j++){
                        if(arrStrId[j] == arrActKeys[i]){
                            boolSkip = true;
                            break;
                        }
                    }
                    if(!boolSkip){
                        disableFeature(arrActKeys[i]);
                    }
                }
                
            }
            //-------------------------------------------//
            
            myObj.featureLayer = featureLayer;
            myObj.addFeature=addFeature;
            myObj.addFeatures=addFeatures;            
            myObj.enableFeature=enableFeature;
            myObj.enableFeatures=enableFeatures;
            myObj.disableFeatures=disableFeatures;
            myObj.disableFeature=disableFeature;
            myObj.disableAll=disableAll;
            myObj.refresh=refresh;            
            myObj.refreshAll=refreshAll;            
            myObj.getFeature=getFeature;
            myObj.containsFeatureKey=containsFeatureKey;
            myObj.containsFeatureCacheKey=containsFeatureCacheKey;
            myObj.containsFeatureActKey=containsFeatureActKey;
            myObj.retainFeatures=retainFeatures;
            
            return myObj;
        }      
        
        //object for storing raw feature/kml data/setting
        function objFeature(strId, strURL, boolRefresh, intSeq){
              var myObj=new Object();
              myObj.id    = strId;
              myObj.url   = strURL;              
              myObj.refresh = boolRefresh;              
              myObj.sequence  = intSeq;
              return myObj;
        }
        //end of object for storing raw feature/kml data/setting
                
        
        //End of features control objects by Isaac Yong
