// vim: sw=4:ts=4:nu:nospell

/**
 * HyperCities mapList Object
 *
 * @author    Chen-Kuei Lee
 * @copyright (c) 2008, by HyperCities Tech Team
 * @date      2008-12-22
 * @version   0.7
 *
 */
 
HyperCities.mapList = function() {
    // do NOT access javascript generated DOM from here; elements don't exist yet
    
    // Private variable goes here
    var _id        = "[HyperCities.mapList] ";
    var _maps      = new Array();
    var _baseMapId = null;
    var _range     = {max: null, min: null};
    var _mapParams = {};
    var _doRender  = true;
    var _syncMap   = false;
	var _originalMode = HyperCities.config.MODE_DEFAULT;
    var _postSyncOperations = [];

    // empty the _maps array and remove overlay on GMap
    var _empty = function() {
        HyperCities.mainMap.removeMapsProxy(_maps); 
        _maps.length = 0; // delete all element in array
//        _maps.splice(0,_maps.length);
        _range.max = null;
        _range.min = null;
        _baseMapId = null;
    };

    var _doPostSyncOperations = function () {
        for (var i in _postSyncOperations) {
            _postSyncOperations[i].operation(_postSyncOperations[i].data)
            delete _postSyncOperations[i];
        }
        _postSyncOperations = [];
    };

    // parse the returned xml and store in _maps
    var _parseMaps = function($data) {
//		HyperCities.debug(_id + "Parse Maps");
        var overlaidMaps = HyperCities.session.get("map");

//		HyperCities.debug("overlaidMaps="+overlaidMaps);

        // Add Blank MapType for clear Overlay Display if some maps are overlaid
        if ( overlaidMaps.length > 0 ) {
            HyperCities.mainMap.addBlankMap();
        } else {
            HyperCities.mainMap.resetMapType();
        }

        // Reset Map Object Array
        if ( _maps.length > 0 )
            _empty();

        // loop for each match on Maps/Map
        $($data).find("Map").each( function () {

                var mapId = $("id", this).text();
				var mapYear = null;

                _maps[mapId] = { id    : mapId,
                                 city  : $("city", this).text(),
                                 title : $("title", this).text(),
                                 neLat : parseFloat($("neLat", this).text()),
                                 neLon : parseFloat($("neLon", this).text()),
                                 swLat : parseFloat($("swLat", this).text()),
                                 swLon : parseFloat($("swLon", this).text()),
                                 proxy : null,
                                 boundary : [],
                                 minZoom  : parseFloat($("minZoom", this).text()),
                                 maxZoom  : parseFloat($("maxZoom", this).text()),
                                 dateFrom : new Date(),
                                 dateTo   : new Date(),
                                 dateStr  : "",
                                 tileType : $("tile_type", this).text(),
                                 tileUrl  : $("tile_url", this).text(),
                                 tileOverlay  : null,
                                 tileOpacity  : 1.0,
                                 copyright    : $("copyright", this).text(),
                                 description  : $("description", this).text(),
                                 thumbnailUrl : $("thumbnail_url", this).text()
                               };

                // Assign Display Information 
				_maps[mapId].dateFrom.setFullYear($("date_from", this).text());
				_maps[mapId].dateTo.setFullYear($("date_to", this).text());
                if ( Date.compare( _maps[mapId].dateFrom, _maps[mapId].dateTo) === 0 ) {
					mapYear = _maps[mapId].dateFrom.getFullYear();
					if ( mapYear < 0 )
	                    _maps[mapId].dateStr = Math.abs(mapYear) + "B.C.";
					else
						_maps[mapId].dateStr = mapYear;

				} else {
					mapYear = _maps[mapId].dateFrom.getFullYear();
					if ( mapYear < 0 )
						_maps[mapId].dateStr = Math.abs(mapYear) + "B.C.";
					else
						_maps[mapId].dateStr = mapYear;

                    _maps[mapId].dateStr += "-" ;

					mapYear = _maps[mapId].dateTo.getFullYear();

					if ( mapYear < 0 )
						_maps[mapId].dateStr += Math.abs(mapYear) + "B.C.";
					else
						_maps[mapId].dateStr += mapYear;
				}

                if ( _maps[mapId].thumbnailUrl === "" || 
                     _maps[mapId].thumbnailUrl == "./images/thumbError.gif") {
                    _maps[mapId].thumbnailUrl = "./images/thumbDummy" + (mapId%5+1) + ".gif"; 
                    //_maps[mapId].thumbnailUrl = "./images/thumbError.gif"; 
                }

                // Generate Map Boundary Array (for GPolygon rendering)
                _maps[mapId].boundary.push(new GLatLng(_maps[mapId].neLat, _maps[mapId].swLon));
                _maps[mapId].boundary.push(new GLatLng(_maps[mapId].neLat, _maps[mapId].neLon));
                _maps[mapId].boundary.push(new GLatLng(_maps[mapId].swLat, _maps[mapId].neLon));
                _maps[mapId].boundary.push(new GLatLng(_maps[mapId].swLat, _maps[mapId].swLon));
                _maps[mapId].boundary.push(new GLatLng(_maps[mapId].neLat, _maps[mapId].swLon));

                // Update the range of mapList
                if ( _range.min === null || Date.compare(_maps[mapId].dateFrom, _range.min) < 0 )
                    _range.min = _maps[mapId].dateFrom;

                if ( _range.max === null || Date.compare(_maps[mapId].dateTo, _range.max ) > 0 )
                    _range.max = _maps[mapId].dateTo;

                $.each(overlaidMaps, function () {
                    if ( (this !== null) && (this.id == mapId) ) {
                        _maps[mapId].tileOverlay = this.tileOverlay; 
                        _maps[mapId].tileOpacity = this.tileOpacity;
                    }
                });

            });

        HyperCities.mainMap.addMapsProxy(_maps);

        if ( _syncMap ) {
            //HyperCities.debug(_range.min.toString('yyyy'));
            if ( _range.min !== null ) {
                HyperCities.mapList.overlayBaseMap(_range.min.toString('yyyy'), true);
                if (HyperCities.session.get("baseMap") !== null)
                	_baseMapId = HyperCities.session.get("baseMap").id;
            }
        }

        //HyperCities.mainMap.setTimespan(_range, false);
        //HyperCities.debug("_maps="+_maps); 
        if ( _doRender )
            HyperCities.mapList.renderList();
        else  // Reset render flag
            _doRender = true;
        _doPostSyncOperations();
    };

    // Render Error Message in intelliList
    var _renderError = function() {

        var itemWrapper = $(document.createElement("div"));
        itemWrapper.attr("id","intelliItem_error");
        itemWrapper.attr("class","intelliItem");

        var itemImg = $(document.createElement("div"));
        itemImg.attr("id","intelliImg_error");
        itemImg.attr("class","intelliImg");
        itemImg.html('<img src="./images/thumbError.gif" ALT="No Map Available" />');

        var itemTitle = $(document.createElement("div"));
        itemTitle.attr("id","intelliTitle_error");
        itemTitle.attr("class","intelliTitle");
        itemTitle.html("<strong>Oops!&nbsp;</strong> No Map Available");

        var itemText = $(document.createElement("div"));
        itemText.attr("id","intelliText_error");
        itemText.attr("class","intelliText");
        itemText.html("We don't have map for this region. Try zooming out for a broader look, or choosing one of our feature city from above mini map.");

        itemWrapper.append(itemImg).append(itemTitle).append(itemText);
        $("#intelliList").append(itemWrapper);

    };

    var _addOpacityControl = function ($mapId, $opacity) {

        // Add Opacity Control
        var opacityControl = $(document.createElement("div"));
        opacityControl.attr("id","intelliOC_" + $mapId);
        opacityControl.attr("class","intelliOC");
        $("#intelliImg_"+$mapId).append(opacityControl);

        // Initial the Slider after dom inserted, otherwise startValue doesn't honer
        opacityControl.slider({
                startValue: $opacity * 100,
                slide: function(e, ui) {
                    HyperCities.mainMap.refreshMap(_maps[$mapId], ui.value / 100);
                }
            });

        HyperCities.mainMap.refreshMap(_maps[$mapId], $opacity);
//        opacityControl.slider("moveTo", $opacity * 100);
    };

    var _addMap = function ($mapId) {
        $("#intelliItem_" + $mapId).addClass('highlight');

        _addOpacityControl($mapId, 1.0);

        _maps[$mapId].proxy.setFillStyle({
            opacity: 0
        });
        HyperCities.mainMap.addMap(_maps[$mapId], 1.0);
        HyperCities.session.set("map", _maps[$mapId]);
        //set current time to the start time of the map
	HyperCities.timebar.setTime(_maps[$mapId].dateFrom.getFullYear(), null, null, null, false);
    };

    var _clickMapItem = function ($event) {

        var item   = $(this);
        var mapId  = item.attr('id').split('_')[1];
        var triger = $($event.target);
        var removeMap = item.hasClass('highlight');

        if ( triger.hasClass('ui-slider') || 
             triger.hasClass('ui-slider-handle') ||
             triger.hasClass('intelliInfo') )
            return;

//      HyperCities.debug(_id + "Click Map Item " + item.attr('id'));
        
        if ( removeMap ) {
            HyperCities.session.removeMap(_maps[mapId]);
        } else  {
            // David, 09-09-09: Moved to separate function for external access
            _addMap(mapId);

            
        }

        $('#intelliList').jScrollPane();
    };

    var _parseUpdateMapMeta = function ($response) {

		var success = $($response).find("Success > Message").text();
		var error   = $($response).find("Error > Message").text();

		if ( error.length > 0 ) {
			alert(error);
		}
		else {
			// TODO: Only update the single map, not the whole map list
			HyperCities.mapList.update(null, null, true, false);	
		}	
        return;
    };

    var _saveMapInfo = function ($mapId) {
		// This will blur all editable filed. update _mapParams to currect state.
        $("titleMapInfoPanel").focus();
        $("#saveBtn").hide();
        $("#resetBtn").hide();

		// Delay the update call, wait the last update of _mapParams.
		setTimeout(function() {	
	        $.post("./updateMapMeta.php", _mapParams, _parseUpdateMapMeta, "html");
		},100);
		return false;
    };

    var _updateMapInfo = function (value, settings) {

        var attr_id = $(this).attr("id");

        if ( attr_id == "map_size") {
           if ( /\s?(\d+)\s?x\s?(\d+)\s?\(cm\)/g.test(value) ) {
               var mapSize = value.match(/\d+/g);
               _mapParams['width'] = mapSize[0];
               _mapParams['height'] = mapSize[1];
           } else {
               _mapParams['width'] = "NULL";
               _mapParams['height'] = "NULL";
               value = "";
           }
        } else if ( $(this).hasClass("editableD") ) {
            if ( Date.parseExact(value, "yyyy-MM-dd") ) {
                _mapParams[attr_id] = value;
            } else {
                _mapParams[attr_id] = "NULL";
                value = "";
            }
        } else {
            _mapParams[attr_id] = $.trim(value);
        }

        if ( value == "" ) {
            value = "N/A";
		}

        return(value);
    };

	var _showUpdateBtn = function() {
        $("#saveBtn").show();
        $("#resetBtn").show();
        $("titleMapInfoPanel").focus();

		return false;
	}

    var _loadMapInfo = function ($mapId) {

        _mapParams = { id: $mapId };

        // Start To Load the Map Meta via AJAX
        $("#loadingMessage").fadeIn("fast");
        $("#blackoutPanel").fadeIn("slow");

        $.ajax( { url: "getMap.php",
                  cache: false,
                  data: _mapParams,
                  success: function(message) {
                          $("#mapInfoWrapper").html(message);
                          $("#loadingMessage").fadeOut("slow");
                          $("#exportBtn").show();
                          $("#saveBtn").hide();
                          $("#resetBtn").hide();

                          if ( HyperCities.user.isLogin() == false ) return;
                          // Make normal input filed editable
                          $(".editableW").editable( _updateMapInfo, {
                                                    indicator : 'Saving...',
                                                    tooltip   : 'Click to edit...',
                                                    cssclass  : 'editHighlight',
                                                    width     : 355,
                                                    height    : 17,
                                                    onblur    : 'submit',
                                                    data      : function (value, settings) {
																	_showUpdateBtn();
                                                                    if ( value == "N/A" )
                                                                        return "";
                                                                    else
                                                                        return value;
                                                                }
                                              });
                          // Make Note TextArea Editable
                          $(".editableA").editable( _updateMapInfo, {
                                                    type      : 'autogrow',
                                                    indicator : 'Saving...',
                                                    tooltip   : 'Click to edit...',
                                                    cssclass  : 'editHighlight',
                                                    width     : 360,
                                                    onblur    : 'submit',
                                                    autogrow  : { lineHeight : 15,
                                                                  minHeight  : 18 },
                                                    data      : function (value, settings) {
																	_showUpdateBtn();
                                                                    if ( value == "N/A" )
                                                                        return "";
                                                                    else
                                                                        return value;
                                                                }
                                              });

                          // Make Date Field editable
                          $(".editableD").editable( _updateMapInfo, {
                                                    type      : 'masked',
                                                    indicator : 'Saving...',
                                                    tooltip   : 'Click to edit...',
                                                    cssclass  : 'editHighlight',
                                                    mask      : "2999-19-39",
                                                    width     : 65,
                                                    onblur    : 'submit',
                                                    data      : function (value, settings) {
																	_showUpdateBtn();
                                                                    if ( value == "N/A" )
                                                                        return "";
                                                                    else
                                                                        return value;
                                                                }
                                              });

                          // Make Map Size Field editable
                          $(".editableS").editable( _updateMapInfo, {
                                                    indicator : 'Saving...',
                                                    tooltip   : 'Click to edit...',
                                                    cssclass  : 'editHighlight',
                                                    width     : 90,
                                                    height    : 17,
                                                    onblur    : 'submit',
                                                    data      : function (value, settings) {
																	_showUpdateBtn();
                                                                    if ( value == "N/A" )
                                                                        return " x (cm)"
                                                                    else
                                                                        return value;
                                                                }
                                              });
                      }
                });

		return false;
    };

    // Show MapInfo Panel
    var _showMapInfo = function ($event) {

        var mapId = $($event.target).attr("id").split('_')[1];
        var mapInfoDiv = $("#mapInfoPanel");
        var closeBox, infoBox, titleBox, exportBtn, saveBtn, resetBtn, offsetTop, offsetLeft ;

		HyperCities.debug(_id + "Click Map Info " + mapId);

        // Prepare the DOM element to hold Map Meta Info
        mapInfoDiv = $(document.createElement("div"));
        mapInfoDiv.attr("id", "mapInfoPanel");

        titleBox = $(document.createElement("div"));
        titleBox.attr("id", "titleMapInfoPanel");
        titleBox.html("Map Metadata");

        closeBox = $(document.createElement("div"));
        closeBox.attr("id", "closeMapInfoPanel");
        closeBox.click( function () {
				$("#blackoutPanel").fadeOut("slow");
                $("#mapInfoPanel").fadeOut("slow", function () {
                        $("#mapInfoPanel").remove();
                    });
				//remove blackout on earth
				if (HyperCities.mainMap.getCurrentMapType() == G_SATELLITE_3D_MAP)
					HyperCities.earth.removeBlackout();	                
            });

        infoBox = $(document.createElement("div"));
        infoBox.attr("id", "mapInfoWrapper");

        exportBtn = $(document.createElement("div"));
        exportBtn.attr("id", "exportBtn");
        exportBtn.attr("class", "button");
        exportBtn.html('<a target="_blank" href="exportMap.php?id='+ mapId+'">Export</a>');
        exportBtn.find("a").click( function () { $(this).blur(); });

        saveBtn = $(document.createElement("div"));
        saveBtn.attr("id", "saveBtn");
        saveBtn.attr("class", "button");
        saveBtn.html('<a href="javascript:void(0);">Save Change</a>');
        saveBtn.find("a").click( function () { 
                _saveMapInfo( mapId );
            });

        resetBtn = $(document.createElement("div"));
        resetBtn.attr("id", "resetBtn");
        resetBtn.attr("class", "button");
        resetBtn.html('<a href="javascript:void(0);">Reset</a>');
        resetBtn.find("a").click( function () { 
                _loadMapInfo( mapId );
            });

        mapInfoDiv.append(titleBox)
                  .append(closeBox)
                  .append(infoBox)
                  .append(resetBtn)
                  .append(saveBtn)
                  .append(exportBtn)
                  .appendTo(document.body);

        $(".button > a").focus( function() { $(this).blur(); });

        offsetLeft = ($('#contentWrapper').width() - mapInfoDiv.width()) / 2 ;
        offsetTop  = Math.max(0, ($('#contentWrapper').height() - mapInfoDiv.height()) / 2);
        mapInfoDiv.css("left", offsetLeft + "px");
        mapInfoDiv.css("top", offsetTop + "px");

        // Start To Load the Map Meta via AJAX
        $("#loadingMessage").fadeIn("fast");

        //info panel cannot show correctly on GEarth
		//in earth mode, use blackoutPanel2 to block background, Jay
		if (HyperCities.mainMap.getCurrentMapType() === G_SATELLITE_3D_MAP)
			HyperCities.earth.blackoutScreen($("#map"), $("#mapInfoPanel"));
		else
	        $("#blackoutPanel").fadeIn("slow");

            
        _loadMapInfo( mapId );

    };

    return {
        
        // Update the map list 
        update: function($mapBounds, $mapZoom, $doRender, $async) {

			HyperCities.debug(_id + "Update Map List");
            var timespan, mapBounds, mapZoom, params;
			var yearFrom, yearTo;

            $("#loadingMessage").fadeIn("fast");

			if ( typeof($doRender) === "boolean" )
                _doRender = $doRender;

			// Setup System Mode
			_originalMode = HyperCities.session.get("mode");
			if ( _doRender ) {
				HyperCities.session.set("mode", HyperCities.config.MODE_MAP_LIST);
			}

            // Setup query mapBound, mapZoom
            if ( $mapBounds !== 'undefined' && $mapBounds !== null )
            	mapBounds = $mapBounds;
            else
	            mapBounds = HyperCities.mainMap.getBounds();

            if ( $mapZoom !== 'undefined' && $mapZoom !== null )
            	mapZoom = $mapZoom;
            else
            	mapZoom = HyperCities.mainMap.getZoom();

			//HyperCities.debug(_id + "mapBounds="+mapBounds);
			//HyperCities.debug(_id + "mapZoom="+mapZoom);
			
            params = { func : "mapList.update",
                       neLat: mapBounds.getNorthEast().lat(),
                       neLon: mapBounds.getNorthEast().lng(),
                       swLat: mapBounds.getSouthWest().lat(),
                       swLon: mapBounds.getSouthWest().lng(),
                       dateFrom_BC: 0,
                       dateTo_BC: 0
                     };

            // Setup query timespan
            timespan = HyperCities.mainMap.getTimespan();
            if ( typeof(timespan) !== 'undefined') {
				yearFrom = timespan.min.getFullYear();
				yearTo   = timespan.max.getFullYear();
				if ( yearFrom < 0 ) {
					params.dateFrom_BC = 1;
					yearFrom = -1 * yearFrom;
				}
				if ( yearTo < 0 ) {
					params.dateTo_BC = 1;
					yearTo = -1 * yearTo;
				}
                params.startTime = yearFrom + timespan.min.toString("-MM-dd");
                params.endTime   = yearTo + timespan.max.toString("-MM-dd");

				while (params.startTime.length < 10)
			        params.startTime = "0" + params.startTime;

				while (params.endTime.length < 10)
					params.endTime = "0" + params.endTime;
            }
                              
            if ( mapZoom < HyperCities.config.ZOOM_THRESHOLD && _doRender ) {
                HyperCities.city.renderList(mapBounds);
            } else {
                // Get map list and call _parseMaps to render maps in maplist area
               	//synchronous update is necessary when sync map to object
               	//$.post("./mapsList.php", params, _parseMaps, "xml"); 
               	$.ajax({
					url: './mapsList.php',
					dataType: 'xml',
					type: 'POST',
					async: $async,
					data: params,
					success: _parseMaps
				});
            }
        },

        // set proxy display object of map
        setMapProxy: function($mapId, $proxy) {
            if( (typeof(_maps[$mapId]) === 'undefined') || (typeof($proxy) !== 'object') )
                return;

            _maps[$mapId].proxy = $proxy;
        },

        removeMapsProxy: function() {
            HyperCities.mainMap.removeMapsProxy(_maps);
        },

        // set tileOverlay object of map
        setMapOverlay: function($mapId, $overlay, $opacity) {
            if( (typeof(_maps[$mapId]) === 'undefined') || (typeof($overlay) !== 'object') )
                return;

            //HyperCities.debug(_id + "Set Overlay of Map " + $mapId);
            _maps[$mapId].tileOverlay = $overlay;
            _maps[$mapId].tileOpacity = $opacity;
            HyperCities.session.set("map", _maps[$mapId]);
        },

        // return the map object by given Id
        getMap: function($mapId) {
            return (typeof(_maps[$mapId]) === 'undefined') ? null : _maps[$mapId];
        },

        overlayBaseMap: function($year, $doRender) {
            var targetMapId    = -1;
            var currentDiff    = 99999 ;
            var targetMapDiff  = 99999 ;
            var overlaidMaps   = HyperCities.session.get("map");
            var currentBaseMap = HyperCities.session.get("baseMap");
            var mapOverlaid    = false;
            var doRender       = true;

            HyperCities.debug(overlaidMaps);
            HyperCities.debug(currentBaseMap);
            
            $year = parseInt($year);

            if ( typeof($doRender) !== 'undefined' && $doRender === false )
                doRender = false;

            // Search For closest map in current map list
            for ( var i in _maps ) {
                //HyperCities.debug($year+ " <--> " + _maps[i].dateStr);
                currentDiff = Math.abs(_maps[i].dateStr - $year);
                if ( currentDiff < targetMapDiff ) {
                    targetMapId = i;
                    targetMapDiff = currentDiff;
                }
            }

            // No Closest map find, reset baseMap
            if ( typeof(_maps[targetMapId]) === 'undefined' ) {
                HyperCities.debug(_id + "Set BaseMap to null");
                HyperCities.session.set("baseMap", null);
                return ;
            }

            // Not render, just return map year
            if ( !doRender )
                return _maps[targetMapId].dateStr;

            // If the baseMap was set and still the same, don't need to do it again
            if ( currentBaseMap !== null && currentBaseMap.id === targetMapId ) {
                HyperCities.debug(_id + "Same BaseMap");
                return;
            } else {
                HyperCities.debug(_id + "Set BaseMap to "+_maps[targetMapId].dateStr);
                // Otherwise, set baseMap
                HyperCities.session.set("baseMap", _maps[targetMapId]);
                // Check if the map is already overlaid
                for ( i in overlaidMaps ) {
                    if ( overlaidMaps[i].id == targetMapId )
                        mapOverlaid = true;
                }
                // If not, overlay the map
                HyperCities.mainMap.addMap(_maps[targetMapId], 1.0);
            } 

			// Zoom to native zoom level of base map
			if ( HyperCities.mainMap.getZoom() < _maps[targetMapId].maxZoom - 2 ) {
				HyperCities.mainMap.setCenter(HyperCities.mainMap.getCenter(), _maps[targetMapId].maxZoom - 2);
			}

            return _maps[targetMapId].dateStr;
        },

        // return the year range (max, min) of current map list
        getMapRange: function() {
            return _range;
        },

        syncWithMap: function($flag) {
            if ( typeof($flag) !== 'boolean')
                return;

            _syncMap = $flag;
            HyperCities.debug(_id + "Sync Map "+_syncMap);
        },

        // unHighlight selected item
        unHighlight: function($mapId) {
            var item = $("#intelliItem_" + $mapId);

            // Item already removed
            if ( item.length == 0 || typeof(_maps[$mapId]) === 'undefined' )
                return;
//          HyperCities.debug(_id + "unHighlight Map " + $mapId);

            item.removeClass('highlight');
            item.children(".intelliImg").children(".intelliOC").remove();
            _maps[$mapId].proxy.setFillStyle({opacity: 0.2});
        },

        // render the map list to intelliList
        renderList: function(){
//          HyperCities.debug(_id + "Render Map List");
            HyperCities.intelliList.reset();

            var i, isHighlight, itemWrapper, itemImg, itemTitle, itemText, itemInfo, itemExpand, itemZoomBtn;

            for ( i in _maps ) {
                isHighlight = (_maps[i].tileOverlay !== null);

                itemWrapper = $(document.createElement("div"));
                itemWrapper.attr("id","intelliItem_"+_maps[i].id);
				itemWrapper.data("yearFrom", _maps[i].dateFrom.getFullYear());
                if ( isHighlight )
                    itemWrapper.attr("class","intelliItem highlight");
                else
                    itemWrapper.attr("class","intelliItem");

                itemImg = $(document.createElement("div"));
                itemImg.attr("id","intelliImg_"+_maps[i].id);
                itemImg.attr("class","intelliImg");
                itemImg.html('<img src="' + _maps[i].thumbnailUrl + '" ALT="' + _maps[i].title + '" />');

                itemTitle = $(document.createElement("div"));
                itemTitle.attr("id","intelliTitle_"+_maps[i].id);
                itemTitle.attr("class","intelliTitle");
                itemTitle.html("<strong>" + _maps[i].dateStr + '</strong> <span title="' + 
                               _maps[i].title + '" >' + _maps[i].title + "</span>");

                itemInfo = $(document.createElement("div"));
                itemInfo.attr("id","intelliInfo_"+_maps[i].id);
                itemInfo.attr("class","intelliInfo");
                itemInfo.attr("title","Click to see the metadata");

                itemText = $(document.createElement("div"));
                itemText.attr("id","intelliText_"+_maps[i].id);
                itemText.attr("class","intelliText expand");
                itemText.html(_maps[i].description);

                // Zoom To Map button
                itemZoomBtn = $(document.createElement("div"));
                itemZoomBtn.attr("id", "intelliZoom_"+_maps[i].id);
                itemZoomBtn.attr("class", "intelliZoom");
                itemZoomBtn.attr("title", "Zoom the window to fit this map.");
                

                itemText.wordWraps();
                itemWrapper.append(itemImg).append(itemTitle).append(itemInfo).append(itemZoomBtn).append(itemText);

                // add event listener on item click
                itemWrapper.click(_clickMapItem);
                itemInfo.click(_showMapInfo);
                itemZoomBtn.click(HyperCities.mapList.zoomToMap);

                // add event listener on item hover
                itemWrapper.hover( 
                		function(event) {
                            var targetId = $(this).attr('id');
                            var mapId = targetId.split('_')[1];
							//HyperCities.debug(_id + "mouseover map " + mapId);

							//Jay
							//HyperCities.debug("event.type="+event.type);
							//HyperCities.debug("event.target.id="+event.target.id);
							//HyperCities.debug("event.target.nodeName="+event.target.nodeName);
							//HyperCities.debug("event.mouesLocation=("+event.pageX+", "+event.pageY+")");
                            //HyperCities.debug("event.currentTarget.id="+event.currentTarget.id);
                            //HyperCities.debug("event.relatedTarget.id="+event.relatedTarget.id);
                            
                            if (HyperCities.mainMap.getCurrentMapType() !== G_SATELLITE_3D_MAP)
                            {
                                if ( !$(this).hasClass("highlight") )
    	                            _maps[mapId].proxy.show();

//                            GEvent.trigger(HyperCities.city.getMarker(cityId), "mouseover");
							}
							else
							{
								//Jay:01/30/2009
								HyperCities.earth.addMapsProxy(mapId, _maps[mapId].proxy);
							}
                        },
						function(event) {
                            var mapId = $(this).attr('id').split('_')[1];
                            //HyperCities.debug(_id + "mouseout map " + mapId);
							if (HyperCities.mainMap.getCurrentMapType() !== G_SATELLITE_3D_MAP)
                            {
	                            _maps[mapId].proxy.hide();

//                            GEvent.trigger(HyperCities.city.getMarker(cityId), "mouseout");
							}
							else
							{
								//Jay:01/31/2009
								HyperCities.earth.removePolygon(mapId);
							}
                        }
                        );

                $("#intelliList").append(itemWrapper);

                // Compute Height after DOM inserted, add expand link if necessary
                // itemText.attr("debug", itemWrapper.height());
                // David, 08/27/09: Increased factor from 64 to 68 to compensate for additional heigh
                // introduced by Zoom-To-Map button
                if ( (!isHighlight && (itemWrapper.height() > 68)) ||
                     ( isHighlight && (itemWrapper.height() > 88)) ) {
                    //HyperCities.debug(_id+"Need Expand link");

                    // Add Link
                    itemExpand = $(document.createElement("div"));
                    itemExpand.attr("id","intelliExpand_"+_maps[i].id);
                    itemExpand.attr("class","intelliExpand");
                    itemExpand.html('<b>...</b> (<a href="#">more info</a>)');
                    itemWrapper.append(itemExpand);

                    // Bind Event Listener
                    itemExpand.click(HyperCities.intelliList.toggleIntelliText);
                }

                // Hide all intelliText beyond 3rd line
                itemText.removeClass("expand");
            }

            // Restore OpacityControl
            var overlaidMap = HyperCities.session.get("map").slice();
            $.each(overlaidMap, function() {
                if ( HyperCities.mapList.getMap(this.id) !== null )
                    _addOpacityControl(this.id, this.tileOpacity);
            });

            var allMaps   = $(".intelliItem");
            var totalMaps = allMaps.length;

            if ( totalMaps == 0 ) {
                _renderError();
                //HyperCities.session.set("city", HyperCities.city.findCity(HyperCities.mainMap.getCenter()));
            }

            // Sort intelliItem Base on Map Year
            var sortMaps = $(".intelliItem").get();
            sortMaps.sort(function (a, b){ 
					var keyA = $(a).data("yearFrom");
					var keyB = $(b).data("yearFrom");
//					HyperCities.debug(keyA + " =?= " + keyB);
                    if (keyA < keyB) return -1
                    if (keyA > keyB) return 1
                    return 0 
                    });
            $.each(sortMaps, function(index, row) {
                    $("#intelliList").append(row);
                    });
            $("#mapTab").html("Map ("+ totalMaps +")");
            HyperCities.intelliList.render($(".intelliItem"));

            // Scroll to Selected Item
            if ( $.isFunction($("#intelliList")[0].scrollTo) )
                $("#intelliList")[0].scrollTo('#' + $("#intelliList .highlight").attr("id"));
            
        },

        removeMap: function ($id) {
            for (var i in _maps) {
                if (_maps[i].id == $id) {
                    HyperCities.session.removeMap(_maps[i]);
                    HyperCities.mainMap.removeMap(_maps[i]);
                    delete _maps[i];
                    return;
                }
            }
            // Debug code
            HyperCities.util.debug (_id + "Attempted to remove map " + $id + ", but map was not found.");
        },

        clearMaps: function () {
			var isFocused = $("#mapTab").parent().hasClass("highlight");
            //_empty();
            for (var i in _maps) {
				HyperCities.session.removeMap(_maps[i]);
				HyperCities.mainMap.removeMap(_maps[i]);
			}
            _maps = [];
            //HyperCities.mapList.renderList();
            //HyperCities.mapList.update(HyperCities.mainMap.getBounds(), HyperCities.mainMap.getZoom(), isFocused);
        },

        addMap: function (mapId) {
            if ($.inArray (_maps[mapId], HyperCities.session.get("map")) == -1) _addMap (mapId);
        },

        getMaps : function () {
            return _maps;
        },

		filterResult: function($keyword) {
			HyperCities.debug(_id + "[A2] Filter map on keyword " + $keyword);
		},

		//find map closest to the given year
		findMap : function($year) {
			var mapId = null;
			var timeDiff = Number.MAX_VALUE;
			
			$.each(_maps, function() {
				var dateFrom = this.dateFrom;
				var dateTo = this.dateTo;
				if (typeof(dateFrom) !== "undefined" && dateFrom !== null && typeof(dateTo) !== "undefined" && dateTo !== null)
				{
					//HyperCities.debug("dateFrom="+this.dateFrom.getFullYear());
					//HyperCities.debug("$year="+$year);
					//HyperCities.debug("timeDiff="+timeDiff);

					var diff1 = Math.abs(dateFrom.getFullYear() - $year); 
					var diff2 = Math.abs(dateTo.getFullYear() - $year);
					if (diff1 < timeDiff || diff2 < timeDiff)
					{
						//HyperCities.debug("id = " + this.id);
						mapId = this.id;
						timeDiff = Math.min(diff1, diff2);
					}
				}
			});

			HyperCities.debug(_id + "Find map " + mapId);
			return mapId;
		},

         zoomToMap: function ($event) {
             //HyperCities.debug (_id + "Map ID: " + $mapId);
             // get map bounds
             var mapId = $($event.target).attr("id").split('_')[1];
             var map = _maps[mapId];
             // build new GLatLngBounds
             var bounds = new GLatLngBounds(new GLatLng(map.swLat, map.swLon), new GLatLng(map.neLat, map.neLon));
             // pass that to mainMap
             var mainMap = HyperCities.mainMap.getMapInstance(bounds);
             if (mainMap.getZoom() == mainMap.getBoundsZoomLevel(bounds)) mainMap.panTo(bounds);
             // TODO: Possible bug here, due to incomplete map tiling: because zoom levels are limited,
             // zooming to some maps may take us out to a further zoom level than we have maps for.
             // It may make sense to add a fudge factor of +1 at some point to make sure users always
             // at least see something.
             //
             else mainMap.setCenter(bounds.getCenter(), mainMap.getBoundsZoomLevel(bounds));
         },

         addPostSyncOperation: function ($operation, $data) {
             _postSyncOperations.push({operation: $operation, data: $data});
         }
    }; // end public methods
}(); // end of Object

// end of file
