Newer Version Available
Building the Map and List View
The next step in building the mapping application is creating the Visualforce page that displays the map and the corresponding list of accounts. The Visualforce page defines a panel for the Google Maps object, creates a group sub-panel to display the list of accounts, and uses JavaScript to retrieve the account addresses and populate the map with color-coded markers based on the customer's priority. The JavaScript sets up the map object by performing the following logic:
- Get the addresses to map from the {!AddrArStr} string array
- Unpack the address array by keying off the delimiters defined in the controller
- Call doAddLocationToMap for all account addresses and the current user
- Use Account.CustomerPriority__c as the key to determine which marker color to use—green, yellow, or red
- Retrieve the custom image markers stored in the $Resource.markers static resource
It's good practice to place any JavaScript code within a static resource, in case it needs to be referenced in multiple locations. Create a static resource named MobileListView:
1swfobject.registerObject("clippy.codeblock-0", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 function addLoadEvent(func) {
18 var oldonload = window.onload;
19 if (typeof window.onload != 'function') {
20 window.onload = func;
21 } else {
22 window.onload = function() {
23 oldonload();
24 func();
25 }
26 }
27 }
28
29 addLoadEvent(
30 function() {
31 if (GBrowserIsCompatible()) {
32 var my_geocoder = new GClientGeocoder();
33 var map = new GMap2(document.getElementById("map"));
34 var TC = new GMapTypeControl();
35 var bottomRight = new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,10));
36 var mCount =0;
37
38 map.addControl(new GSmallMapControl()); // Small arrows
39 map.addControl(TC, bottomRight); // Map type buttons
40
41 function LTrim( value ) {
42 var re = /\s*((\S+\s*)*)/;
43 return value.replace(re, "$1");
44 }
45
46 function RTrim( value ) {
47 var re = /((\s*\S+)*)\s*/;
48 return value.replace(re, "$1");
49 }
50
51 // Remove leading and ending whitespaces
52 function trim( value ) {
53 return LTrim(RTrim(value));
54 }
55
56 function doAddLocationToMap(SiteName, Street, City, State, Zip, typ) {
57 var addr = Street + ", " + City + ", " + State + " " + Zip;
58 my_geocoder.getLatLng (addr,
59 function(point) {
60 if (point) {
61 var mTag = '';
62 var myIcon = new GIcon(G_DEFAULT_ICON);
63
64 if(typ == 'self') {
65 mTag = "<b>" + SiteName + "</b>" + "<br>" + City ;
66 myIcon.image = "http://maps.google.com/mapfiles/arrow.png";
67 myIcon.iconSize=new GSize(32,32);
68 } else {
69 if(typ == 'acct') {
70 mCount ++;
71 var priAr = SiteName.split(":");
72 var compName = priAr[0]; // company name
73 var pri = trim(priAr[1]); // priority
74 var acctId = priAr[2]; //account id
75 var index = "";
76 var imgName = "marker"; // default marker image
77 var color = "";
78
79 mTag = "<b>" + compName + "</b>" + "<br>"
80 + "Priority: "
81 + pri + "<br>" + City ;
82 // Set up marker colors based on priority
83 if (pri == 'Medium') color="Yellow";
84 else if (pri == 'High') color="Red";
85 else if (pri == 'Low') color="Green";
86
87 if(mCount>10){ // use default marker
88 myIcon.image =
89 "http://maps.google.com/mapfiles/marker.png";
90 } else { // use custom marker 1-10
91 index = String(mCount);
92 imgName = imgName + color + index + ".png";
93 myIcon.image = "{!URLFOR($Resource.markers,
94 'markers/" + imgName + "')}";
95 }
96
97 document.getElementById(acctId).src = myIcon.image;
98 myIcon.iconSize=new GSize(20,34);
99 }
100 }
101 myIcon.shadowSize=new GSize(56,32);
102 myIcon.iconAnchor=new GPoint(16,32);
103 myIcon.infoWindowAnchor=new GPoint(16,0);
104 markerOptions2 = { icon:myIcon };
105 var marker = new GMarker(point, markerOptions2);
106 map.setCenter(point, 8);
107 map.addOverlay(marker);
108
109 // Set up listener action to show info on click event
110 GEvent.addListener(marker, "click",
111 function() {
112 marker.openInfoWindowHtml(mTag);
113 }) ;
114 }
115 }
116 );
117 }
118
119 //Get accts and draw address
120 var arAllStr = '';
121 arAllStr = '{!AddrArStr}'; // Get all address recs
122 var arLi = arAllStr.split("~::~"); // Split on line break delim
123 for (var i = 0; i < arLi.length-1; i++) {
124 var arLiStr =arLi[i];
125 var arCols =arLiStr.split("~:~"); //Split to get columns
126
127 if(arCols[1].length >0)
128 doAddLocationToMap(arCols[0],arCols[1],arCols[2],
129 arCols[3],arCols[4],'acct');
130 }
131
132 //Get user address and draw
133 doAddLocationToMap('{!$User.FirstName} {!$User.LastName}'
134 +' (Me)','{!$User.Street}','{!$User.City}','
135 {!$User.State}','{!$User.PostalCode}','self');
136 }
137 }
138 );The following code defines the landing page of our mapping application:
1swfobject.registerObject("clippy.codeblock-1", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<apex:page controller="mapController" showHeader="false">
18 <apex:composition template="iuivf" />
19 <script src="{!myKey}" type="text/javascript"> </script>
20 <apex:includeScript value="{!$Resource.MobileListView}"/>
21
22
23 <ul title="Accounts" selected="true" id="home" >
24 <!-- Draw user name at top of panel -->
25 <li class="group">
26 User: {!$User.FirstName} {!$User.LastName}
27 </li>
28
29 <!-- Create panel for Google Maps object -->
30 <div class="panel" style="padding: 10px;" >
31 <div id="map" style="width: 300px; height: 300px;">
32 </div>
33 </div>
34
35 <!-- Create group sub-panel to display list -->
36 <li class="group">Accounts</li>
37
38 <!-- Draw accounts, one per row -->
39 <apex:repeat value="{!MyAccts}" var="p" >
40 <li>
41 <a href="accountDetail?id={!p.Id}" >
42 <img id="{!p.Id}"
43 src="http://maps.google.com/mapfiles/marker.png"/>
44 {!p.Name}
45 </a>
46 </li>
47 </apex:repeat>
48 </ul>
49</apex:page>
50The markup in our page uses the <apex:composition> component to reference a template. The template leverages the iUI framework, which lets us apply iPhone-like styling to our page. The iUI framework is included from the $Resource.IUI static resource. By defining a template, we can easily apply the same styling to all of the Visualforce pages we create for the iPhone platform.
The following markup defines the iuivf page used as the template:
1swfobject.registerObject("clippy.codeblock-2", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!--
18* Page definition: iuivf
19* Visualforce template for iUI includes needed for
20* using the iui framework <http://code.google.com/p/iui/>
21* in any Visualforce page.
22-->
23
24<apex:page>
25 <meta name="viewport" content="width=320; initial-scale=1.0;
26 maximum-scale=1.0; user scalable=0;"/>
27 <apex:includeScript value="{!URLFOR($Resource.IUI, 'iui-0.13/iui/iui.js')}" />
28 <apex:styleSheet value="{!URLFOR($Resource.IUI, 'iui-0.13/iui/iui.css')}" />
29
30 <style> #home { position: relative; top: 0px; } </style>
31
32</apex:page>Note the following about the template:
- The markup overrides the #home style from the iUI library. This ensures that our application will render in Salesforce Classic without any noticeable gap at the top of the page.
- The markup avoids the use of the class="Toolbar" element. The embedded browser in Salesforce Classic has a navigation toolbar at the top of the page, so a second toolbar would likely confuse users. If you want to use the button styles provided in the iUI framework, don't use the Toolbar class to render the buttons.