replace ajax downloadurl disallowed on IE (not same domain?) by PHP
[maps-routes-comparison] / index.php
1 <?php
2
3 ini_set("track_errors", 1);
4 ini_set('display_errors', 0);
5
6 // Helper function for fatal errors
7 function fatal($msg) {
8     echo "<h2>Error</h2>\n";
9     echo "$msg\n";
10     echo "</body></html>";
11     exit;
12
13 }
14
15 $host = 'api.hostip.info';
16 $port = 80;
17 $path = '/get_html.php?position=true&ip=' . $_SERVER['REMOTE_ADDR'];
18
19 $fp = @fsockopen($host, $port, $errno, $errstr);
20 if (!$fp)
21     fatal("<p>Unable to open connection to <tt>$host</tt> ".
22           "(port <tt>$port</tt>):</p><pre>$errstr</pre>");
23
24 fwrite($fp, "GET $path HTTP/1.0\r\n");
25 fwrite($fp, "Host: $host:$port\r\n");
26 fwrite($fp, "\r\n");
27
28 $buf = '';
29 $length = '';
30 while (!feof($fp)) {
31     $buf .= fread($fp, 8192);
32     // Parse the HTTP answer and try to extract response code,
33     // headers and body
34     $answer = parse_answer($buf);
35
36     // We need to discover Content-Length, to know when server
37     // finishes sending data
38     if (!$length) {
39         if ($answer[0] == 200)
40             $length = $answer[1]['content-length'];
41     }
42     
43     // When length is available, we can check if response has
44     // been fully transmitted; this is the case when all what
45     // we received equals the headers plus the full contents.
46     if ($length) {
47         if (strlen_b($buf) == strlen_b($answer[3]) + $length)
48             break;
49     }
50 }
51
52 fclose($fp);
53 $body = $answer[2];
54
55 $latitude = $longitude = $zoom = 0;
56 if (preg_match('/Latitude: (\S+)/', $body, $matches)) {
57     $latitude = $matches[1];
58     $zoom = 8;
59 }
60 if (preg_match('/Longitude: (\S+)/', $body, $matches)) {
61     $longitude = $matches[1];
62 }
63 if (($latitude == 0 || $longitude == 0) && preg_match('/Country: .*?\((\S{2})\)/', $body, $matches)) {
64     $country = $matches[1];
65     include('countries.phpi');
66     $latitude = $countries[$country][0];
67     $longitude = $countries[$country][1];
68     $zoom = 5;
69 }
70
71 // PHP's strlen should return the length of a string in bytes,
72 // except when using multibyte strings, in which case
73 // mb_orig_strlen must be used (in case strlen was overloaded
74 // with mb_strlen).
75 function strlen_b($str) {
76     if (function_exists('mb_orig_strlen')) {
77         return mb_orig_strlen($str);
78     } else {
79         return strlen($str);
80     }
81 }
82
83 // Parse an HTTP answer to extract response code, headers and body
84 // Returns an array containing: response code, headers, body, raw headers
85 function parse_answer($answer) {
86
87     // This first pattern matching allows to separate first line
88     // (server response status), headers, and data contents 
89     if (ereg("^(([^\n]+)\r\n(.*)\r\n\r\n)(.*)", $answer, $regs)) {
90         $full_headers   = $regs[1];
91         $response       = $regs[2];
92         $headers_string = $regs[3];
93         $body           = $regs[4];
94         // Parse first line (server response status)
95         if (ereg("^HTTP/[0-9\.]+ ([0-9]+)", $response, $regs)) {
96             $response_code = $regs[1];
97         } else {
98             fatal("<p>Unable to parse response line <tt>$response</tt> ".
99                   "from the server.</p>");
100         }
101         // Parse headers and build a hash with them
102         foreach (split("\r\n", $headers_string) as $line) {
103             if (ereg("^([^:]+): (.*)", $line, $regs)) {
104                 $headers[strtolower($regs[1])] = $regs[2];
105             } else {
106                 fatal("<p>Unable to parse header line <tt>$line</tt> ".
107                       "from the server. ".
108                       "All headers:</p><pre>$headers_string</pre>");
109             }
110         }
111     } else {
112         // Return -1 as response code if parsing was not possible
113         return array(-1);
114     }
115     return array($response_code, $headers, $body, $full_headers);
116 }
117
118 ?>
119
120 <html>
121 <head>
122     <title>Routes Compare</title>
123     <link rel="stylesheet" type="text/css" media="screen" href="style.css"/>
124     <script type="text/javascript" src="prototype.js"></script>
125     <script type="text/javascript" src="effects.js"></script>
126     <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
127     <script type="text/javascript" src="googlemaps.js"></script>
128     <script type="text/javascript" src="countries.js"></script>
129     <script type="text/javascript">
130
131       // Copyright (c) Guillaume Cottenceau and licensed under the Apache 2.0 License
132
133       var mylat, mylng;
134       var map;
135       var places = new Array();
136       var geocoder = new google.maps.Geocoder();
137       var directions_service = new google.maps.DirectionsService();
138       var info_window = null;
139       var bounds;
140       var travelmode = google.maps.DirectionsTravelMode.DRIVING;
141
142       function reload(new_places) {
143           for (var i = 0; i < places.length; i++) {
144               places[i].marker.setMap(null);
145               places[i].directions_display.setMap(null);
146           }
147           places = new Array();
148           document.getElementById("resultslist").innerHTML = '';
149           for (var i = 0; i < new_places.length; i++) {
150               createPlace(new_places[i].latlng, new_places[i].name);
151           }
152       }
153
154       function travelmodeChanged() {
155           travelmode = eval(document.getElementById("travelmode").value);
156           reload(places);
157       }
158
159       function showDirectionCapture(i) {
160           var request = { origin: new google.maps.LatLng(mylat, mylng),
161                           destination: places[i].latlng,
162                           travelMode: travelmode };
163           directions_service.route(request, function(result, status) {
164               if (status == google.maps.DirectionsStatus.OK) {
165                   places[i].directions_display.setMap(map);
166                   places[i].directions_display.setDirections(result);
167                   displayDirectionsMoreinfo(result.routes[0], document.getElementById("directions" + ( 1 + i )));
168               }
169           });
170       }
171       
172       function fitmap() {
173           if (document.getElementById("fitmap").checked) {
174               map.fitBounds(bounds);
175           }
176       }
177
178       function showDirections() {
179           if (places.length > 0) {
180               bounds = new google.maps.LatLngBounds();
181               bounds.extend(new google.maps.LatLng(mylat, mylng));
182           }
183           for (var i = 0; i < places.length; i++) {
184               if (!places[i].directions_display.getMap()) {
185                   showDirectionCapture(i);
186               }
187               bounds.extend(places[i].latlng);
188           }
189           fitmap();
190       }
191
192       function showDel(number) {
193           document.getElementById("delete" + number).style.visibility = 'visible';
194       }
195
196       function hideDel(number) {
197           document.getElementById("delete" + number).style.visibility = 'hidden';
198       }
199
200       function del(number) {
201           var new_places = new Array();
202           for (var i = 0; i < places.length; i++) {
203               if (i == number - 1) {
204                   places[i].marker.setMap(null);
205                   places[i].directions_display.setMap(null);
206               } else {
207                   new_places.push(places[i]);
208               }
209           }
210           reload(new_places);
211       }
212
213       function createPlace(latlng, name) {
214           var number = places.length + 1;
215           var iconname = 'icon' + ( 7 + number );
216           var marker = new google.maps.Marker({ position: latlng,
217                                                 title:name,
218                                                 icon:getIcon(iconname),
219                                                 shadow:getShadow(iconname) });
220           marker.setMap(map);
221           document.getElementById("resultslist").innerHTML += "<li onmouseover='showDel(" + number + ")' onmouseout='hideDel(" + number + ")'><img class='iconwithtext' src='icons/" + iconname + ".png'/> " + name
222                                                               + "<div style='float:right; padding:3; visibility:hidden' id='delete" + number
223                                                               + "'><a href='#' onclick='del(" + number + ")'><img src='icons/bin.png'/></a></div>"
224                                                               + "<br/><span id='directions" + number + "'></span></li>";
225           var directions_display = new google.maps.DirectionsRenderer({ preserveViewport: true, suppressMarkers: true, draggable: true });
226           google.maps.event.addListener(directions_display, "directions_changed", function() {
227               displayDirectionsMoreinfo(directions_display.getDirections().routes[0], document.getElementById("directions" + number ));
228           });
229           places.push({marker:marker, latlng:latlng, name:name, directions_display:directions_display});
230           showDirections();
231       }
232
233       function submit() {
234           if (info_window) {
235               info_window.close();
236           }
237           var address = document.getElementById('address').value;
238           geocoder.geocode({ 'address': address },
239                            function(results, status) {
240                                if (status == google.maps.GeocoderStatus.OK) {
241                                    createPlace(results[0].geometry.location, address);
242                                    document.getElementById('address').value = '';
243                                    document.getElementById('address').focus();
244                                } else {
245                                    document.getElementById('submitshortresult').innerHTML = "Address not found...";
246                                    document.getElementById('submitshortresult').style.display = '';
247                                    setTimeout('new Effect.Fade(document.getElementById("submitshortresult"), {duration: 0.4})', 1000);
248                                }
249                            });
250       }
251
252       function save() {
253           var link = "#me=" + mylat + ";" + mylng;
254           for (var i = 0; i < places.length; i++) {
255               link += "&p=" + places[i].latlng.lat() + ";" + places[i].latlng.lng() + ";" + encodeURI(places[i].name);
256           }
257           document.getElementById('savediv').style.display = '';
258           var save = document.getElementById('save');
259           save.value = 'http://zarb.org/~gc/maps-routes-comparison/' + link;
260           save.focus();
261           save.select();
262       }
263
264       function startUp() {
265           var me = new google.maps.Marker({position: new google.maps.LatLng(mylat, mylng),
266                                            title: 'Me',
267                                            draggable: true,
268                                            icon: getIcon('icon31'),
269                                            shadow: getShadow('icon31')});
270           me.setMap(map);
271           google.maps.event.addListener(me, "dragend", function(mouseEvent) {
272                   mylat = mouseEvent.latLng.lat();
273                   mylng = mouseEvent.latLng.lng();
274                   reload(places);
275               });
276           map.setCenter(new google.maps.LatLng(mylat, mylng));
277           return me;
278       }
279                          
280       function initialize() {
281           map = new google.maps.Map(document.getElementById("mapbox"),
282                                     { mapTypeId: google.maps.MapTypeId.ROADMAP,
283                                       zoom: 2 });
284
285           mylat = 0;
286           mylng = 0;
287           var all_params = location.href.split("#")
288           if (all_params.length > 1 && all_params[1].length > 0) {
289               var params = all_params[1].split("&");
290               for (i = 0; i < params.length; i++) {
291                   var keyvalue = params[i].split("=");
292                   var values = keyvalue[1].split(";");
293                   if (keyvalue[0] == 'me') {
294                       mylat = values[0];
295                       mylng = values[1];
296                   } else if (keyvalue[0] == 'p') {
297                       createPlace(new google.maps.LatLng(values[0], values[1]), decodeURI(values[2]));
298                   }
299               }
300               startUp();
301           } else {
302               <?php
303                   if ($latitude) {
304                ?>
305               mylat = <?php echo $latitude ?>;
306               mylng = <?php echo $longitude ?>;
307               var baseinfomessage = 'This is where I located you,<br/>based on your IP address.<br/>';
308               <?php
309                   } else {
310                ?>
311               var baseinfomessage = 'It was not possible to locate<br/>you, based on your IP address<br/>'
312                                   + '<a href="http://www.hostip.info/">(submit your location!)</a>.<br/>';
313               <?php
314                   }
315                ?>
316               map.setZoom(<?php echo $zoom ?>);
317               me = startUp();
318               info_window = new google.maps.InfoWindow({ content: baseinfomessage
319                                                                   + 'You can drag this icon<br/>to move your position.<br/>'
320                                                                   + '<div class="fakelink" style="float: right" onclick="javascript:info_window.close()">Okay</div>' });
321               info_window.open(map, me);
322               if (!document.cookie || document.cookie.indexOf("not-a-newbie") == -1) {
323                   document.getElementById("newbie").style.display = '';
324               }
325               var expires = new Date(new Date().getTime() + (10 * 86400000));  // 10 days
326               document.cookie = "not-a-newbie=true"
327                                 + "; expires=" + expires.toGMTString()
328                                 + "; path=/";
329           }
330       }
331
332     </script>
333 </head>
334
335 <body onload="initialize()">
336
337   <div id="mainbox" style="height: 100%">
338
339     <div id="debugbox"></div>
340
341     <div style="height: 2em">
342       <div style="float: right">
343         travel mode:
344         <select id="travelmode" onchange="travelmodeChanged()">
345           <option value="google.maps.DirectionsTravelMode.DRIVING">driving</option>
346           <option value="google.maps.DirectionsTravelMode.WALKING">walking</option>
347 <!-- doesn't work  <option value="google.maps.DirectionsTravelMode.BICYCLING">Bicycling</option> -->
348         </select>
349         - 
350         <input type="checkbox" id="fitmap" checked="true" onchange="fitmap()">fit to elements
351         - 
352         <a href="#" onclick="save()">
353           save this page
354           <img src="icons/save.png" class="iconwithtext"/>
355         </a>
356         <div id="savediv" style="display: none; position:absolute; right:1em; top:3em; background:#E0E0FF; border:solid 1px black; padding:0.5em; z-index:1">
357           The following link will directly present the current data:
358           <br/>
359           <input type="text" id="save" size="40"/>
360           <br/>
361           <div style="float:right"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('savediv'), {duration: 0.4})">Okay</a></div>
362         </div>
363       </div>
364
365       <form action="javascript:submit()">
366       Address:
367       
368       <input type="text" size="32" id="address"/>
369       <script type="text/javascript">
370         document.getElementById('address').focus();
371       </script>
372       <input type="submit" value="Show route!"/>
373       <span id="submitshortresult" style="color: red">
374       </span>
375       </form>
376
377     </div>
378
379
380     <div class="containermainbox">
381       <div style="float: right; width: 75%; height: 100%">
382          <div id="mapbox" style="width: 100%; height: 95%">
383          </div>
384          <div style="float:right; font-size:70%; margin-top:3px">
385            <em>
386              uses goodies from Google Maps and Scriptaculous, and is glued together by
387              <a href="http://zarb.org/~gc/">Guillaume Cottenceau</a>
388            </em>
389          </div>
390       </div>
391       <div id="newbie" style="display: none; background-color: lightpink; border: solid 1px blue; position:absolute; width:20%; margin:1.5em">
392         <p align="right">
393           <img src="icons/top.png"/>
394         </p>
395         <p align="right" style="margin-right:1em; margin-top:-1em">
396           1. add an address in the top area
397         </p>
398         <br/>
399         <p align="right">
400           <img src="icons/right.png"/>
401         </p>
402         <p align="right" style="margin-right:1em; margin-top:-1em">
403           2. the routes from all added addresses to the "home"
404           icon will be shown on the map
405         </p>
406         <br/>
407         <br/>
408         <p align="right" style="margin-right:1em; margin-top:-1em">
409           <a href="#" onclick="javascript:new Effect.Fade(document.getElementById('newbie'), {duration: 0.4})">Okay</a>
410         </p>
411       </div>
412       <ul id="resultslist">
413       </ul>
414     </div>
415
416   </div>
417
418 </body>
419 </html>