var MapIconMaker = {};
var tod = 0;  
var asl = 0;
var wc = 0;  //
var mType = 0;
var map = null;
var meetings = new Array();
var geocoder = null;
var meMarker = null;
var meetingsAtPoint = new Array();
var barrieMarker = null;
var oshawaMarker = null;
var hamiltonMarker = null;
var contextmenu = null;
var wcAttr = "<span class='imWC'>mATTR</span>";
var aslAttr = "<span class='imASL'>mATTR</span>";
var gLink = "<a target='_blank' href='GURL' title='Visit the home page for this meeting&#39;s group.'>NAME</a>";
var fetchedDows = [ false, false, false, false, false, false, false, false,
        false ];
var dowLabels = [ 'Any Day', 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
        'Thursday', 'Friday', 'Saturday' ];
var todLabels = [ 'at <b>Any Time</b>', 'in the <b>Morning</b>',
        'in the <b>Afternoon</b>', 'in the <b>Evening</b>' ];
var mTypeLabels = [ 'Any Meeting Type', 'Open Meetings', 'Closed Meetings' ];
var alertDone = false;

function initializeMap()
{
    try
    {
        Ajax.Responders.register(
        {
            onCreate : function()
            {
             
                if (Ajax.activeRequestCount === 1)
                {
                    $('loading').show();
                }
            },
            onComplete : function()
            {
                if (Ajax.activeRequestCount === 0)
                {
                    $('loading').hide();
                }
            }
        });
    }
    catch (ignored)
    {
       ;// alert(ignored);
    }
    
    try
    {
        if (GBrowserIsCompatible())
        {		
		  map = new GMap2(document.getElementById("bigmap_canvas"));	  
		  
		  var topLeft = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(10, 10));

		 // map.addControl(new GLargeMapControl(), topLeft);
		  map.addControl(new GOverviewMapControl());
		  map.addControl(new GLargeMapControl3D());
		  map.setCenter(new GLatLng(43.653, -79.568), 10);
			  
		  // === create the context menu div ===
		  contextmenu = document.createElement("div");
		  contextmenu.style.visibility="hidden";
		  contextmenu.style.background="#ffffff";
		  contextmenu.style.border="1px solid #8888FF";
	
		  contextmenu.innerHTML = '<a href="javascript:zoomIn()"><div class="context">&nbsp;&nbsp;Zoom in&nbsp;&nbsp;<\/div><\/a>'
								+ '<a href="javascript:zoomOut()"><div class="context">&nbsp;&nbsp;Zoom out&nbsp;&nbsp;<\/div><\/a>'
								+ '<a href="javascript:zoomInHere()"><div class="context">&nbsp;&nbsp;Zoom in here&nbsp;&nbsp;<\/div><\/a>'
								+ '<a href="javascript:zoomOutHere()"><div class="context">&nbsp;&nbsp;Zoom out here&nbsp;&nbsp;<\/div><\/a>'
								+ '<a href="javascript:centreMapHere()"><div class="context">&nbsp;&nbsp;Centre map here&nbsp;&nbsp;<\/div><\/a>';
	
		  map.getContainer().appendChild(contextmenu);
	
		  // === listen for singlerightclick ===
		  GEvent.addListener(map,"singlerightclick",function(pixel,tile) {
			// store the "pixel" info in case we need it later
			// adjust the context menu location if near an egde
			// create a GControlPosition
			// apply it to the context menu, and make the context menu visible
			clickedPixel = pixel;
			var x=pixel.x;
			var y=pixel.y;
			if (x > map.getSize().width - 120) { x = map.getSize().width - 120 }
			if (y > map.getSize().height - 100) { y = map.getSize().height - 100 }
			var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(x,y));  
			pos.apply(contextmenu);
			contextmenu.style.visibility = "visible";
		  });

		  // === If the user clicks on the map, close the context menu ===
		  GEvent.addListener(map, "click", function() {
			contextmenu.style.visibility="hidden";
		  });
			
		  geocoder = new GClientGeocoder();

		  filterDow(dow);
		  
		  drawIntergroups();		  
            
        }
        else
        {
            alert("Your browser does not support Google Maps.");
        }
        
    }
    catch (e)
    {
        alert("error initializing: " + e);
    }
}

function resetMap(){

		 map.setCenter(new GLatLng(43.653, -79.568), 10);

		filterDow(initialDOW);
		filterTod(0);
		filterMtype(0);
		asl = 1;
		filterAsl();
		wc = 1;
		filterWc();
		updateFilterSummary();
	
}


function filterDow(aDow)
{
    
    document.getElementById('dow' + dow).className = 'inactiveCtl';
    dow = aDow;
    document.getElementById('dow' + dow).className = 'activeCtl';
 
    if (!fetchedDows[aDow])
    {
        map.closeInfoWindow();
        fetchedDows[aDow] = true;
        fetch(aDow);
        
    }
    else
    {
        filter();
    }
	
   
}

function filterMtype(aMtype)
{
    document.getElementById('mType' + mType).className = 'inactiveCtl';
    mType = aMtype
    document.getElementById('mType' + mType).className = 'activeCtl';
    
    filter();
	
}

function filterTod(aTod)
{
    document.getElementById('tod' + tod).className = 'inactiveCtl';
    tod = aTod;
    document.getElementById('tod' + tod).className = 'activeCtl';
    
    filter();
    
}

function filterAsl()
{
    
    if (asl == 0)
    {
        asl = 1;
        document.getElementById('asl').className = 'activeCtl';
    }
    else
    {
        asl = 0;
        document.getElementById('asl').className = 'inactiveCtl';
    }
    
    filter();
}

function filterWc()
{
    if (wc == 0)
    {
        wc = 1;
        document.getElementById('wc').className = 'activeCtl';
    }
    else
    {
        wc = 0;
        document.getElementById('wc').className = 'inactiveCtl';
    }
    
    filter();
}

function filter()
{
    try
    {
		var strMtgList="";		
        map.closeInfoWindow();
		
        for ( var i = 0; i < meetings.length; i++)
        {
            var meeting = meetings[i];
            var mtgsAtPoint = meetingsAtPoint[meeting.pKey];
            
            var showMeeting = false;
            for (var j = 0; j < mtgsAtPoint.length; j++)
            {
                var mtgAtPoint = mtgsAtPoint[j];
                var showThisMeeting = isInDow(mtgAtPoint.Dow.id)
                        && isInTod(mtgAtPoint.Meeting.start_time)
                        && isAsl(mtgAtPoint.Meeting.asl) && isWc(mtgAtPoint.Meeting.wc)
                        && isMtype(mtgAtPoint.MeetingType.id);
                
                showMeeting = showMeeting || showThisMeeting;
            }
            
            if (meeting.gMarker != null)
            {
                if (showMeeting){
                    meeting.gMarker.show();
					strMtgList = strMtgList + "<div style='background-color:#eee; border-bottom:1px " + 
						"dashed #36C;padding:10px; padding-left: 10px; padding-bottom: 30px;'>" +  meetingInfo(meeting) + "</div>";  		
				}
                else
                    meeting.gMarker.hide();
            }
            
        }
        
         updateFilterSummary();

		 if (strMtgList != "") $('mtgList').innerHTML = strMtgList;
		 else $('mtgList').innerHTML = "No Meetings displayed...";
		 
		  document.getElementById('mtgList').scrollTop = 0;

    }
    catch (e)
    {
        
        alert("filter error: " + e + " m: " + meeting.Meeting.id);
    }
}

function isMtype(aMtype)
{
    var r = mType == 0 || aMtype == mType;
    return r;
}

function isWc(aWc)
{
    var r = wc == 0 || aWc == '1';
    // alert("isWc: " + r);
    return r;
}

function isAsl(aAsl)
{
    var r = asl == 0 || aAsl == '1';
    // alert("isAsl: " + r);
    return r;
}

function isInDow(aDow)
{
    var r = dow == 0 || dow == aDow;
    // alertOnce("aDow: " + aDow + " dow: " + dow);
    // throw new Error('');
    return r;
}

function isInTod(aTime)
{
    var hours = new Number(aTime.slice(0, 2));
    // alert(aTime + " hours: " + );
    // throw new Error();
    var ok = true;
    
    switch (tod)
    {
        case 0:
            ok = true;
            break;
        case 1:
            ok = hours < 12;
            break;
        case 2:
            ok = hours >= 12 && hours < 18;
            break;
        case 3:
            ok = hours >= 18;
            break;
        default:
            throw new Error("Unhandled time of day(" + tod + +")");
    }
    // alert("isInTod: " + ok);
    return ok;
}

function toTime(date)
{
    var hours = new Number(date.slice(0, 2));
    var minutes = new Number(date.slice(3, 5));
    
    var suffix = "AM";
    if (hours >= 12)
    {
        suffix = "PM";
        hours = hours - 12;
    }
    if (hours == 0)
    {
        hours = 12;
    }
    
    if (minutes < 10)
        minutes = "0" + minutes;
    
    return hours + ":" + minutes + " " + suffix;
}

function wrapIfNotNull(content)
{
    var wrapped = null;
    
    if (content && content != '')
        wrapped = "(" + content + ")";
    else
        wrapped = "";
    
    return wrapped;
}

function alertOnce(msg)
{
    if (!alertDone)
    {
        alert(msg);
        alertDone = true;
    }
    else
        ; // done
        
}
function emptyOrTarget(sTarget)
{
    return emptyOrOther(sTarget, sTarget);
}
function emptyOrOther(sTarget, other)
{
    var r = null;
    if (sTarget != null && sTarget.replace(/\s+/, "") != '')
        r = other;
    else
        r = '';
    
    return r;
}

function city(id)
{
    var name = '';
    for ( var i = 0; i < cities.length; i++)
    {
        if (cities[i].City.id == id)
            name = cities[i].City.idname;
        else
            ; // not found
    }
    return name;
}

function locAddress(loc)
{
    var addr = "";
 
    addr += emptyOrOther(loc.unit_suite, '#' + loc.unit_suite + ' ');
    addr += emptyOrTarget(loc.street_num);
    addr += emptyOrTarget(loc.num_suffix);
    addr += ' ';
    addr += loc.street_name;
    addr += ' ';
    addr += loc.street_type;
    addr += emptyOrOther(loc.street_direction, ' ' + loc.street_direction);
    addr += ', ';
    addr += city(loc.city_id);
    
    return addr;
    
}


function locName(loc)
{
    var locName = new String(loc.idname).replace(/\(.*\).*$/, "");
   return emptyOrOther(locName, "<i>" + locName + "</i><br />");
    
}

function groupLink(aMeeting)
{
    var link = null;
    var url = aMeeting.Aagroup.home_page_url;
    
    if (url != null && url != '')
    {
        link = gLink.replace('GURL', url);
        link = link.replace('NAME', aMeeting.Meeting.idname);
    }
    else{
 		link = "<span style='color:maroon;'>" + aMeeting.Meeting.idname + "</span>";
	}
    return link;
}

function attrSpan(attr, value)
{
    var span = "";
	
    if (attr == 'Wheelchair accessible' && value == '1'){
        span = wcAttr.replace("mATTR", attr);
	}

    if (attr == 'Allowing ASL interpreters' && value == '1'){
        span = aslAttr.replace("mATTR", attr);
	}        
    return span;
}


function allAttrs(aMeeting)
{
    var attrs = "";
    attrs += attrSpan('Wheelchair accessible', aMeeting.Meeting.wc);
    attrs += attrSpan('Allowing ASL interpreters', aMeeting.Meeting.asl);
    return attrs;
}

function meetingType(aMeeting)
{
    var type = aMeeting.MeetingType.idname;
    var desc = "";
    if (type == 'open')
        desc = 'Open AA Meeting';
    else if (type = 'closed')
        desc = 'Closed AA Meeting';
    else
        desc = 'Either an Open or a Closed AA Meeting.';

    return desc;
}

function printMtg(mID){
	
window.open('/webapp/app/webroot/index.php/meetings/print_view/' + mID,'MeetingDetails' + mID,'scrollbars=yes,width=850,height=800');
}

function fetch(aDow)
{
    new Ajax.Request('/webapp/app/webroot/index.php/meetings/ajax/' + aDow,
    {
        method :'get',
        // parameters: {dow: aDow},
        
        onException : function(request, err)
        {
         
        },
        onSuccess : function(transport)
        {
            var txt = transport.responseText;
            txt = txt.match("\\[{1,1}.*\\].*$") + new String("");
          
            var json = txt.evalJSON();
			


		var iconOptions = {};
		iconOptions.primaryColor = "#0033FF";
		iconOptions.strokeColor = "#000000";
		iconOptions.label = "AA";
		iconOptions.labelColor = "#FFFFFF";
		iconOptions.addStar = false;
		iconOptions.starPrimaryColor = "#FFFF00";
		iconOptions.starStrokeColor = "#0000FF";
		
		try{
			
		var iconAA = MapIconMaker.createLabeledMarkerIcon(iconOptions);
		
		}
		catch(e){ }
            
            try
            {
				var strMtgList = "";
				
                for ( var i = 0; i < json.length; i++)
                {
                    var meeting = json[i];
                    meetings[meetings.length] = meeting;
               
                    var gMarker = null;
                    
                    var pKey = "Dow" + aDow + "_Lat" + meeting.Location.latitude + "_Lng" +  meeting.Location.longitude;
                    if (meetingsAtPoint[pKey] == null)
                    {
                        var point = new GLatLng(meeting.Location.latitude,
                                meeting.Location.longitude);
                        
						
                        gMarker = new GMarker(point, {icon: iconAA});
                        meetingsAtPoint[pKey] = [meeting];
                        map.addOverlay(gMarker);
                        
                        gMarker.hide();
                    }
                    else
                    {
                       
                        gMarker = meetingsAtPoint[pKey][0].gMarker;
                        meetingsAtPoint[pKey][meetingsAtPoint[pKey].length] = meeting;
                        // alert(meetingsAtPoint[pKey].length);
                    }
                    
                    meeting.pKey = pKey;
                    
                    meeting.gMarker = gMarker;
                    
                    gMarker.bindInfoWindowTabsHtml(null);
                    var tabs = [];
                    

                    for (var j = 0; j < meetingsAtPoint[pKey].length; j++)
                    {
                        var mtg = meetingsAtPoint[pKey][j];
                        tabs[j] = new GInfoWindowTab(toTime(mtg.Meeting.start_time),meetingInfo(mtg));    
						
						strMtgList = strMtgList + "<div style='background-color: #EEF3E0; border-bottom:1px dashed #36C;padding:10px;padding-left:50px !important;'>" +  meetingInfo(mtg) + "</div>";   
						
                    }
                    gMarker.bindInfoWindowTabsHtml(tabs);
					
					
                }
                $('mtgList').innerHTML = strMtgList;
				
                filter();
            }
            catch (e)
            {
                // alert("error parsing json");
            }
        }
    });
    
}

function meetingInfo(aMeeting)
{
    
    var instance = mInfoTemplate;
    instance = instance.replace("mNAME", groupLink(aMeeting));
    instance = instance.replace("mADDR_TITLE", locName(aMeeting.Location));
    instance = instance.replace("mADDRESS", locAddress(aMeeting.Location));
	
    instance = instance.replace("mTIME", "<span style='font-color:#555; font-weight:bold;'>" + aMeeting.Dow.idname + ' '
            + toTime(aMeeting.Meeting.start_time) + "</span>");
				
//    instance = instance.replace("mTIME", aMeeting.Dow.idname + ' '
//            + toTime(aMeeting.Meeting.start_time));
//    
    var type = meetingType(aMeeting);
    
    instance = instance.replace("mTYPE", type);
    if (aMeeting.Meeting.srv == '1')
        instance = instance.replace("mSRV", " (Service meeting)");
    else
        instance = instance.replace("mSRV", "");
 
    instance = instance.replace("mLANGUAGE", aMeeting.Language.idname);
    instance = instance.replace("_LAT", aMeeting.Location.latitude);
    instance = instance.replace("_LNG", aMeeting.Location.longitude);
    instance = instance.replace("_ID", aMeeting.Meeting.id);	
    instance = instance.replace("ZOOM", "Zoom map in on meeting.")
		instance = instance.replace("mPRINTVIEW", 
	"<a title='Print this Meeting' href='javascript:;' onclick='printMtg(\"" + aMeeting.Meeting.id + "\");'></a>");		

    var attrs = allAttrs(aMeeting);
    instance = instance.replace("mATTR", attrs);
    
    instance = instance.replace("mFOCUS", emptyOrOther(aMeeting.Meeting.focus,
            "<b>Focus</b>: " + aMeeting.Meeting.focus));
    instance = instance.replace("mRESTRICTIONS", emptyOrOther(
            aMeeting.Meeting.restrictions, "<b>Restrictons</b>: "
                    + aMeeting.Meeting.restrictions));
    
    var comments = aMeeting.Meeting.comments;
   // comments = comments.replace(/\n\s*\n/, '<br/>&nbsp;<br/>');
  //  comments = comments.replace(/\n/, '<br/>');
    instance = instance.replace("mCOMMENTS",
            emptyOrOther(aMeeting.Meeting.comments, "<b>Comments</b>: " + comments +""));
    
    
    var notices = aMeeting.MeetingNotice;
    if (notices.length > 0)
    {
        var allNotices = "<b style='margin-top: 5px; border-top: 1px solid #666; display: block;'>Meeting Notice(s):</b>";
        for ( var i = 0; i < notices.length; i++)
        {
            var mNotice = notices[i];
            var note = mInfoNoticeTemplate;
            note = note.replace("mTITLE", "<b>* " + mNotice.title + "</b>");
            var remarks = mNotice.remarks;
            remarks = remarks.replace(/\n\s*\[.\]/, '');
            remarks = remarks.replace(/\[.\]/, '');
            note = note.replace("mREMARKS", remarks);
           allNotices += note;
        }
        instance = instance.replace("mNOTICES", allNotices);
    }
    else
    {
        instance = instance.replace("mNOTICES",
                "");
    }
    
    return instance;
    
}

function zoom(lat, lng)
{
	try{
		var pKey = "Dow" + dow + "_Lat" + lat + "_Lng" +  lng;
			
		var gMarker = null;
		gMarker = meetingsAtPoint[pKey][0].gMarker;
		
		var tabs = [];
	
		for (var j = 0; j < meetingsAtPoint[pKey].length; j++)
		{
			var mtg = meetingsAtPoint[pKey][j];
			tabs[j] = new GInfoWindowTab(toTime(mtg.Meeting.start_time),meetingInfo(mtg));    
		}		
		
		gMarker.openInfoWindowTabsHtml(tabs);
		
		scroll(0, 175);
		map.setCenter(new GLatLng(lat + 0.005, lng), 15);
	}
	catch (e){alert(e);}
}

function drawPoint(lat, lng)
{
    
    var point = new GLatLng(lat, lng);
    map.setCenter(point, 12);
    
    if (meMarker != null)
        map.removeOverlay(meMarker);
    else
        ; // not set yet
	var iconOptions = {};
	iconOptions.primaryColor = "#C00000";
	iconOptions.strokeColor = "#C00000";
	iconOptions.label = "me";
	iconOptions.labelColor = "#FFFFFF";
	iconOptions.addStar = false;
	iconOptions.starPrimaryColor = "#FFFF00";
	iconOptions.starStrokeColor = "#0000FF";
	iconOptions.width =29;
	var iconME = MapIconMaker.createLabeledMarkerIcon(iconOptions);	
	
    meMarker = new GMarker(point, {icon: iconME});
    
	 GEvent.addListener(meMarker, "click", function() {
          meMarker.openInfoWindowHtml("<br /><a href='http://www.aatoronto.org/webapp/app/webroot/index.php/meetings/map'><b>Click here if you need to change your location...</b></a>");
        });

    map.addOverlay(meMarker);
    
}

function findPoint()
{
    try
    {
        var lAddress = $('myAddress').value;
        
        geocoder.getLatLng(lAddress, function(point)
        {
            var lat = '';
            var lng = '';
            if (!point)
            {
                alert("Sorry, couldn't find your address; please try again.");
                return;
            }
            else
            {
                lat = point.lat();
                lng = point.lng();
            }
            
            drawPoint(lat, lng);
            
        });
        
    }
    catch (e)
    {
        alert("error finding lat and lng: " + e);
    }
    
}

function updateFilterSummary()
{
    var summary = filterSummary;
    summary = summary.replace("mDOW", dowLabels[dow]);
    summary = summary.replace("mTOD", todLabels[tod]);
    summary = summary.replace("mTYPE", mTypeLabels[mType]);
    summary = summaryDisplay('dASL', asl, summary);
    summary = summaryDisplay('dWc', wc, summary);
    
    $('filterSummary').innerHTML = summary;
	//$('filterSummaryBottom').innerHTML = summary;
}

function summaryDisplay(attr, state, summary)
{
    if (state == 1)
        summary = summary.replace(attr, "");
    else
        summary = summary.replace(attr, "none");
    
    return summary;
}


function showMtgTypeInfo()
{
    var top =  document.viewport.getScrollOffsets().top;
 
    $('meetingTypeInfo').style.top = top + "px";
	$('meetingTypeInfo').style.display = '';

}

function drawIntergroups()
{
    
/*	Barrie Intergroup Marker*/
    var point = new GLatLng(44.38421, -79.69511);
     
    var bIcon = new GIcon(G_DEFAULT_ICON);
    bIcon.image = "http://www.aatoronto.org/webapp/app/webroot/img/aabarrie.png";
    markerOptions =
    {
        icon :bIcon
    };
	
    barrieMarker = new GMarker(point,markerOptions);
    
	 GEvent.addListener(barrieMarker, "click", function() {
          barrieMarker.openInfoWindowHtml("Barrie and Area Intergroup<br /><a target='blank' href='http://www.barrieaa.com/meetings/1-monday.htm'><b>Click for Barrie Area AA Meetings</b></a><br />Phone: (705) 725-8682");
        });

    map.addOverlay(barrieMarker);
    
/*	Hamilton Intergroup Marker*/
    var point = new GLatLng(43.24938, -79.84570);
     
    var hIcon = new GIcon(G_DEFAULT_ICON);
    hIcon.image = "http://www.aatoronto.org/webapp/app/webroot/img/aahamilton.png";
    markerOptions =
    {
        icon :hIcon
    };
	
    hamiltonMarker = new GMarker(point,markerOptions);
    
	 GEvent.addListener(hamiltonMarker, "click", function() {
          hamiltonMarker.openInfoWindowHtml("Hamilton AA Central Office<br /><a target='blank' href='http://www.aahamilton.com/index.php?option=com_meetinglist&Itemid=16'><b>Click for Hamilton Area AA Meetings</b></a><br />Phone: (905) 522-8392");
        });

    map.addOverlay(hamiltonMarker);	
	
/*	Oshawa Intergroup Marker*/
    var point = new GLatLng(43.89910, -78.84277);
     
    var hIcon = new GIcon(G_DEFAULT_ICON);
    hIcon.image = "http://www.aatoronto.org/webapp/app/webroot/img/aaoshawa.png";
    markerOptions =
    {
        icon :hIcon
    };
	
    oshawaMarker = new GMarker(point,markerOptions);
    
	 GEvent.addListener(oshawaMarker, "click", function() {
         oshawaMarker.openInfoWindowHtml("Lakeshore Intergroup (Oshawa AA)<br /><a target='blank' href='http://www.aaoshawa.org/meetings.html'><b>Click for Oshawa Area AA Meetings</b></a><br />Phone: (905) 728-1020");
        });

    map.addOverlay(oshawaMarker);		
}

// === functions that perform the context menu options ===
function zoomIn() {
// perform the requested operation
map.zoomIn();
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}      
function zoomOut() {
// perform the requested operation
map.zoomOut();
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}      
function zoomInHere() {
// perform the requested operation
var point = map.fromContainerPixelToLatLng(clickedPixel)
map.zoomIn(point,true);
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}      
function zoomOutHere() {
// perform the requested operation
var point = map.fromContainerPixelToLatLng(clickedPixel)
map.setCenter(point,map.getZoom()-1); // There is no map.zoomOut() equivalent
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}      
function centreMapHere() {
// perform the requested operation
var point = map.fromContainerPixelToLatLng(clickedPixel)
map.setCenter(point);
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}


/**
 * Creates a labeled marker icon based on the specified options in the 
 *     {@link MarkerIconOptions} argument.
 *     Supported options are: primaryColor, strokeColor, 
 *     starPrimaryColor, starStrokeColor, label, labelColor, and addStar.
 * @param {MarkerIconOptions} [opts]
 * @return {GIcon}
 */
MapIconMaker.createLabeledMarkerIcon = function (opts) {
  var primaryColor = opts.primaryColor || "#DA7187";
  var strokeColor = opts.strokeColor || "#000000";
  var starPrimaryColor = opts.starPrimaryColor || "#FFFF00";
  var starStrokeColor = opts.starStrokeColor || "#0000FF";
  var label = MapIconMaker.escapeUserText_(opts.label) || "";
  var labelColor = opts.labelColor || "#000000";
  var addStar = opts.addStar || false;
  
  var pinProgram = (addStar) ? "pin_star" : "pin";
  var baseUrl = "http://chart.apis.google.com/chart?cht=d&chdp=mapsapi&chl=";
  var iconUrl = baseUrl + pinProgram + "'i\\" + "'[" + label + 
      "'-2'f\\"  + "hv'a\\]" + "h\\]o\\" + 
      primaryColor.replace("#", "")  + "'fC\\" + 
      labelColor.replace("#", "")  + "'tC\\" + 
      strokeColor.replace("#", "")  + "'eC\\";
  if (addStar) {
    iconUrl += starPrimaryColor.replace("#", "") + "'1C\\" + 
        starStrokeColor.replace("#", "") + "'0C\\";
  }
  iconUrl += "Lauto'f\\";

  var icon = new GIcon(G_DEFAULT_ICON);
  icon.image = iconUrl + "&ext=.png";
  
  icon.iconSize = new GSize(21, 34);
  return icon;
};

/**
 * Utility function for doing special chart API escaping first,
 *  and then typical URL escaping. Must be applied to user-supplied text.
 * @private
 */
MapIconMaker.escapeUserText_ = function (text) {
  if (text === undefined) {
    return null;
  }
  text = text.replace(/@/, "@@");
  text = text.replace(/\\/, "@\\");
  text = text.replace(/'/, "@'");
  text = text.replace(/\[/, "@[");
  text = text.replace(/\]/, "@]");
  return encodeURIComponent(text);
};

