maplace.js 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. /**
  2. * Maplace.js
  3. *
  4. * Copyright (c) 2013 Daniele Moraschi
  5. * Licensed under the MIT license
  6. * For all details and documentation:
  7. * http://maplacejs.com
  8. *
  9. * @version 0.2.10
  10. * @preserve
  11. */
  12. ;(function(root, factory) {
  13. if (typeof define === 'function' && define.amd) {
  14. define(['jquery'], factory);
  15. } else if (typeof exports === 'object') {
  16. module.exports = factory(require('jquery'));
  17. } else {
  18. root.Maplace = factory(root.jQuery);
  19. }
  20. }(this, function($) {
  21. 'use strict';
  22. var html_dropdown,
  23. html_ullist;
  24. //dropdown menu type
  25. html_dropdown = {
  26. activateCurrent: function(index) {
  27. this.html_element.find('select').val(index);
  28. },
  29. getHtml: function() {
  30. var self = this,
  31. html = '',
  32. title,
  33. a;
  34. if (this.ln > 1) {
  35. html += '<select class="dropdown controls ' + this.o.controls_cssclass + '">';
  36. if (this.ShowOnMenu(this.view_all_key)) {
  37. html += '<option value="' + this.view_all_key + '">' + this.o.view_all_text + '</option>';
  38. }
  39. for (a = 0; a < this.ln; a += 1) {
  40. if (this.ShowOnMenu(a)) {
  41. html += '<option value="' + (a + 1) + '">' + (this.o.locations[a].title || ('#' + (a + 1))) + '</option>';
  42. }
  43. }
  44. html += '</select>';
  45. html = $(html).bind('change', function() {
  46. self.ViewOnMap(this.value);
  47. });
  48. }
  49. title = this.o.controls_title;
  50. if (this.o.controls_title) {
  51. title = $('<div class="controls_title"></div>').css(this.o.controls_applycss ? {
  52. fontWeight: 'bold',
  53. fontSize: this.o.controls_on_map ? '12px' : 'inherit',
  54. padding: '3px 10px 5px 0'
  55. } : {}).append(this.o.controls_title);
  56. }
  57. this.html_element = $('<div class="wrap_controls"></div>').append(title).append(html);
  58. return this.html_element;
  59. }
  60. };
  61. //ul list menu type
  62. html_ullist = {
  63. html_a: function(i, hash, ttl) {
  64. var self = this,
  65. index = hash || (i + 1),
  66. title = ttl || this.o.locations[i].title,
  67. el_a = $('<a data-load="' + index + '" id="ullist_a_' + index + '" href="#' + index + '" title="' + title + '"><span>' + (title || ('#' + (i + 1))) + '</span></a>');
  68. el_a.css(this.o.controls_applycss ? {
  69. color: '#666',
  70. display: 'block',
  71. padding: '5px',
  72. fontSize: this.o.controls_on_map ? '12px' : 'inherit',
  73. textDecoration: 'none'
  74. } : {});
  75. el_a.on('click', function(e) {
  76. e.preventDefault();
  77. var i = $(this).attr('data-load');
  78. self.ViewOnMap(i);
  79. });
  80. return el_a;
  81. },
  82. activateCurrent: function(index) {
  83. this.html_element.find('li').removeClass('active');
  84. this.html_element.find('#ullist_a_' + index).parent().addClass('active');
  85. },
  86. getHtml: function() {
  87. var html = $('<ul class=\'ullist controls ' + this.o.controls_cssclass + '\'></ul>').css(this.o.controls_applycss ? {
  88. margin: 0,
  89. padding: 0,
  90. listStyleType: 'none'
  91. } : {}),
  92. title, a;
  93. if (this.ShowOnMenu(this.view_all_key)) {
  94. html.append($('<li></li>').append(html_ullist.html_a.call(this, false, this.view_all_key, this.o.view_all_text)));
  95. }
  96. for (a = 0; a < this.ln; a++) {
  97. if (this.ShowOnMenu(a)) {
  98. html.append($('<li></li>').append(html_ullist.html_a.call(this, a)));
  99. }
  100. }
  101. title = this.o.controls_title;
  102. if (this.o.controls_title) {
  103. title = $('<div class="controls_title"></div>').css(this.o.controls_applycss ? {
  104. fontWeight: 'bold',
  105. padding: '3px 10px 5px 0',
  106. fontSize: this.o.controls_on_map ? '12px' : 'inherit'
  107. } : {}).append(this.o.controls_title);
  108. }
  109. this.html_element = $('<div class="wrap_controls"></div>').append(title).append(html);
  110. return this.html_element;
  111. }
  112. };
  113. /**
  114. * Create a new instance
  115. * @class Maplace
  116. * @constructor
  117. */
  118. function Maplace(args) {
  119. this.VERSION = '0.2.10';
  120. this.loaded = false;
  121. this.markers = [];
  122. this.circles = [];
  123. this.oMap = false;
  124. this.view_all_key = 'all';
  125. this.infowindow = null;
  126. this.maxZIndex = 0;
  127. this.ln = 0;
  128. this.oMap = false;
  129. this.oBounds = null;
  130. this.map_div = null;
  131. this.canvas_map = null;
  132. this.controls_wrapper = null;
  133. this.current_control = null;
  134. this.current_index = null;
  135. this.Polyline = null;
  136. this.Polygon = null;
  137. this.Fusion = null;
  138. this.directionsService = null;
  139. this.directionsDisplay = null;
  140. //default options
  141. this.o = {
  142. debug: false,
  143. map_div: '#gmap',
  144. controls_div: '#controls',
  145. generate_controls: true,
  146. controls_type: 'dropdown',
  147. controls_cssclass: '',
  148. controls_title: '',
  149. controls_on_map: true,
  150. controls_applycss: true,
  151. controls_position: google.maps.ControlPosition.RIGHT_TOP,
  152. type: 'marker',
  153. view_all: true,
  154. view_all_text: 'View All',
  155. pan_on_click: true,
  156. start: 0,
  157. locations: [],
  158. shared: {},
  159. map_options: {
  160. mapTypeId: google.maps.MapTypeId.ROADMAP
  161. },
  162. stroke_options: {
  163. strokeColor: '#0000FF',
  164. strokeOpacity: 0.8,
  165. strokeWeight: 2,
  166. fillColor: '#0000FF',
  167. fillOpacity: 0.4
  168. },
  169. directions_options: {
  170. travelMode: google.maps.TravelMode.DRIVING,
  171. unitSystem: google.maps.UnitSystem.METRIC,
  172. optimizeWaypoints: false,
  173. provideRouteAlternatives: false,
  174. avoidHighways: false,
  175. avoidTolls: false
  176. },
  177. circle_options: {
  178. radius: 100,
  179. visible: true
  180. },
  181. styles: {},
  182. fusion_options: {},
  183. directions_panel: null,
  184. draggable: false,
  185. editable: false,
  186. show_infowindows: true,
  187. show_markers: true,
  188. infowindow_type: 'bubble',
  189. listeners: {},
  190. //events
  191. beforeViewAll: function() {},
  192. afterViewAll: function() {},
  193. beforeShow: function(index, location, marker) {},
  194. afterShow: function(index, location, marker) {},
  195. afterCreateMarker: function(index, location, marker) {},
  196. beforeCloseInfowindow: function(index, location) {},
  197. afterCloseInfowindow: function(index, location) {},
  198. beforeOpenInfowindow: function(index, location, marker) {},
  199. afterOpenInfowindow: function(index, location, marker) {},
  200. afterRoute: function(distance, status, result) {},
  201. onPolylineClick: function(obj) {},
  202. onPolygonClick: function(obj) {},
  203. circleRadiusChanged: function(index, circle, marker) {},
  204. circleCenterChanged: function(index, circle, marker) {},
  205. drag: function(index, location, marker) {},
  206. dragEnd: function(index, location, marker) {},
  207. dragStart: function(index, location, marker) {}
  208. };
  209. //default menu types
  210. this.AddControl('dropdown', html_dropdown);
  211. this.AddControl('list', html_ullist);
  212. if (args && args.type === 'directions') {
  213. !args.show_markers && (args.show_markers = false);
  214. !args.show_infowindows && (args.show_infowindows = false);
  215. }
  216. //init
  217. $.extend(true, this.o, args);
  218. }
  219. //where to store the menu types
  220. Maplace.prototype.controls = {};
  221. //initialize google map object
  222. Maplace.prototype.create_objMap = function() {
  223. var self = this,
  224. count = 0,
  225. i;
  226. //if styled
  227. for (i in this.o.styles) {
  228. if (this.o.styles.hasOwnProperty(i)) {
  229. if (count === 0) {
  230. this.o.map_options.mapTypeControlOptions = {
  231. mapTypeIds: [google.maps.MapTypeId.ROADMAP]
  232. };
  233. }
  234. count++;
  235. this.o.map_options.mapTypeControlOptions.mapTypeIds.push('map_style_' + count);
  236. }
  237. }
  238. //if init
  239. if (!this.loaded) {
  240. try {
  241. this.map_div.css({
  242. position: 'relative',
  243. overflow: 'hidden'
  244. });
  245. //create the container div into map_div
  246. this.canvas_map = $('<div>').addClass('canvas_map').css({
  247. width: '100%',
  248. height: '100%'
  249. }).appendTo(this.map_div);
  250. this.oMap = new google.maps.Map(this.canvas_map.get(0), this.o.map_options);
  251. } catch (err) {
  252. this.debug('create_objMap::' + this.map_div.selector, err.toString());
  253. }
  254. //else loads the new optionsl
  255. } else {
  256. self.oMap.setOptions(this.o.map_options);
  257. }
  258. //if styled
  259. count = 0;
  260. for (i in this.o.styles) {
  261. if (this.o.styles.hasOwnProperty(i)) {
  262. count++;
  263. this.oMap.mapTypes.set('map_style_' + count, new google.maps.StyledMapType(this.o.styles[i], {
  264. name: i
  265. }));
  266. this.oMap.setMapTypeId('map_style_' + count);
  267. }
  268. }
  269. };
  270. //adds markers to the map
  271. Maplace.prototype.add_markers_to_objMap = function() {
  272. var a,
  273. point,
  274. type = this.o.type || 'marker';
  275. //switch how to display the locations
  276. switch (type) {
  277. case 'marker':
  278. for (a = 0; a < this.ln; a++) {
  279. point = this.create_objPoint(a);
  280. this.create.marker.call(this, a, point);
  281. }
  282. break;
  283. default:
  284. this.create[type].apply(this);
  285. break;
  286. }
  287. };
  288. //create the main object point
  289. Maplace.prototype.create_objPoint = function(index) {
  290. var point = $.extend({}, this.o.locations[index]),
  291. visibility = point.visible === undefined ? undefined : point.visible;
  292. !point.type && (point.type = this.o.type);
  293. //set obj map
  294. point.map = this.oMap;
  295. point.position = new google.maps.LatLng(point.lat, point.lon);
  296. point.zIndex = point.zIndex === undefined ? 10000 : (point.zIndex + 100);
  297. point.visible = visibility === undefined ? this.o.show_markers : visibility;
  298. this.o.maxZIndex = point.zIndex > this.maxZIndex ? point.zIndex : this.maxZIndex;
  299. if (point.image) {
  300. point.icon = new google.maps.MarkerImage(
  301. point.image,
  302. new google.maps.Size(point.image_w || 32, point.image_h || 32),
  303. new google.maps.Point(0, 0),
  304. new google.maps.Point((point.image_w || 32) / 2, (point.image_h || 32) / 2)
  305. );
  306. }
  307. return point;
  308. };
  309. //create the main object circle
  310. Maplace.prototype.create_objCircle = function(point) {
  311. var def_stroke_opz,
  312. def_circle_opz,
  313. circle;
  314. circle = $.extend({}, point);
  315. def_stroke_opz = $.extend({}, this.o.stroke_options);
  316. def_circle_opz = $.extend({}, this.o.circle_options);
  317. $.extend(def_stroke_opz, point.stroke_options || {});
  318. $.extend(circle, def_stroke_opz);
  319. $.extend(def_circle_opz, point.circle_options || {});
  320. $.extend(circle, def_circle_opz);
  321. circle.center = point.position;
  322. circle.draggable = false;
  323. circle.zIndex = point.zIndex > 0 ? point.zIndex - 10 : 1;
  324. return circle;
  325. };
  326. //create the main object point
  327. Maplace.prototype.add_markerEv = function(index, point, marker) {
  328. var self = this;
  329. google.maps.event.addListener(marker, 'click', function(ev) {
  330. self.CloseInfoWindow();
  331. self.o.beforeShow.call(self, index, point, marker);
  332. //show infowindow?
  333. if (self.o.show_infowindows && (point.show_infowindow === false ? false : true)) {
  334. self.open_infowindow(index, marker, ev);
  335. }
  336. //pan and zoom the map
  337. if (self.o.pan_on_click && (point.pan_on_click === false ? false : true)) {
  338. self.oMap.panTo(point.position);
  339. point.zoom && self.oMap.setZoom(point.zoom);
  340. }
  341. //activate related menu link
  342. if (self.current_control && self.o.generate_controls && self.current_control.activateCurrent) {
  343. self.current_control.activateCurrent.call(self, index + 1);
  344. }
  345. //update current location index
  346. self.current_index = index;
  347. self.o.afterShow.call(self, index, point, marker);
  348. });
  349. if (point.draggable) {
  350. this.add_dragEv(index, point, marker);
  351. }
  352. };
  353. //add events to circles objs
  354. Maplace.prototype.add_circleEv = function(index, circle, marker) {
  355. var self = this;
  356. google.maps.event.addListener(marker, 'click', function() {
  357. self.ViewOnMap(index + 1);
  358. });
  359. google.maps.event.addListener(marker, 'center_changed', function() {
  360. self.o.circleCenterChanged.call(self, index, circle, marker);
  361. });
  362. google.maps.event.addListener(marker, 'radius_changed', function() {
  363. self.o.circleRadiusChanged.call(self, index, circle, marker);
  364. });
  365. if (circle.draggable) {
  366. this.add_dragEv(index, circle, marker);
  367. }
  368. };
  369. //add drag events
  370. Maplace.prototype.add_dragEv = function(index, obj, marker) {
  371. var self = this;
  372. google.maps.event.addListener(marker, 'drag', function(ev) {
  373. var pos,
  374. extraType;
  375. if (marker.getPosition) {
  376. pos = marker.getPosition();
  377. } else if (marker.getCenter) {
  378. pos = marker.getCenter();
  379. } else {
  380. return;
  381. }
  382. //update circle position
  383. if (self.circles[index]) {
  384. self.circles[index].setCenter(pos);
  385. }
  386. //update polygon or polyline if defined
  387. if (self.Polyline) {
  388. extraType = 'Polyline';
  389. } else if (self.Polygon) {
  390. extraType = 'Polygon';
  391. }
  392. if (extraType) {
  393. var path = self[extraType].getPath(),
  394. pathArray = path.getArray(),
  395. arr = [],
  396. i = 0;
  397. for (; i < pathArray.length; ++i) {
  398. arr[i] = (index === i) ?
  399. new google.maps.LatLng(pos.lat(), pos.lng())
  400. : new google.maps.LatLng(pathArray[i].lat(), pathArray[i].lng());
  401. }
  402. self[extraType].setPath(new google.maps.MVCArray(arr));
  403. self.add_polyEv(extraType);
  404. }
  405. //fire drag event
  406. self.o.drag.call(self, index, obj, marker);
  407. });
  408. google.maps.event.addListener(marker, 'dragend', function() {
  409. self.o.dragEnd.call(self, index, obj, marker);
  410. });
  411. google.maps.event.addListener(marker, 'dragstart', function() {
  412. self.o.dragStart.call(self, index, obj, marker);
  413. });
  414. google.maps.event.addListener(marker, 'center_changed', function() {
  415. //update marker position
  416. if (self.markers[index] && marker.getCenter) {
  417. self.markers[index].setPosition(marker.getCenter());
  418. }
  419. self.o.drag.call(self, index, obj, marker);
  420. });
  421. };
  422. //add events to poly objs
  423. Maplace.prototype.add_polyEv = function(typeName) {
  424. var self = this;
  425. google.maps.event.addListener(this[typeName].getPath(), 'set_at', function(index, obj) {
  426. self.trigger_polyEv(typeName, index, obj);
  427. });
  428. google.maps.event.addListener(this[typeName].getPath(), 'insert_at', function(index, obj) {
  429. self.trigger_polyEv(typeName, index, obj);
  430. });
  431. };
  432. //trigger events to poly objs
  433. Maplace.prototype.trigger_polyEv = function(typeName, index, obj) {
  434. var item = this[typeName].getPath().getAt(index),
  435. newPos = new google.maps.LatLng(item.lat(), item.lng());
  436. this.markers[index] && this.markers[index].setPosition(newPos);
  437. this.circles[index] && this.circles[index].setCenter(newPos);
  438. this.o['on' + typeName + 'Changed'](index, obj, this[typeName].getPath().getArray());
  439. };
  440. //wrapper for the map types
  441. Maplace.prototype.create = {
  442. //single marker
  443. marker: function(index, point, marker) {
  444. //allow mix circles with markers
  445. if (point.type === 'circle' && !marker) {
  446. var circle = this.create_objCircle(point);
  447. if (!point.visible) {
  448. circle.draggable = point.draggable;
  449. }
  450. marker = new google.maps.Circle(circle);
  451. this.add_circleEv(index, circle, marker);
  452. //store the new circle
  453. this.circles[index] = marker;
  454. }
  455. point.type = 'marker';
  456. //create the marker and add click event
  457. marker = new google.maps.Marker(point);
  458. this.add_markerEv(index, point, marker);
  459. //extends bounds with this location
  460. this.oBounds.extend(point.position);
  461. //store the new marker
  462. this.markers[index] = marker;
  463. this.o.afterCreateMarker.call(this, index, point, marker);
  464. return marker;
  465. },
  466. //circle mode
  467. circle: function() {
  468. var self = this,
  469. a,
  470. point,
  471. circle,
  472. marker;
  473. for (a = 0; a < this.ln; a++) {
  474. point = this.create_objPoint(a);
  475. //allow mix markers with circles
  476. if (point.type === 'circle') {
  477. circle = this.create_objCircle(point);
  478. if (!point.visible) {
  479. circle.draggable = point.draggable;
  480. }
  481. marker = new google.maps.Circle(circle);
  482. this.add_circleEv(a, circle, marker);
  483. //store the new circle
  484. this.circles[a] = marker;
  485. }
  486. point.type = 'marker';
  487. this.create.marker.call(this, a, point, marker);
  488. }
  489. },
  490. //polyline mode
  491. polyline: function() {
  492. var self = this,
  493. a,
  494. point,
  495. stroke = $.extend({}, this.o.stroke_options);
  496. stroke.path = [];
  497. stroke.draggable = this.o.draggable;
  498. stroke.editable = this.o.editable;
  499. stroke.map = this.oMap;
  500. stroke.zIndex = this.o.maxZIndex + 100;
  501. //create the path and location marker
  502. for (a = 0; a < this.ln; a++) {
  503. point = this.create_objPoint(a);
  504. this.create.marker.call(this, a, point);
  505. stroke.path.push(point.position);
  506. }
  507. this.Polyline ?
  508. this.Polyline.setOptions(stroke)
  509. : this.Polyline = new google.maps.Polyline(stroke);
  510. this.add_polyEv('Polyline');
  511. },
  512. //polygon mode
  513. polygon: function() {
  514. var self = this,
  515. a,
  516. point,
  517. stroke = $.extend({}, this.o.stroke_options);
  518. stroke.path = [];
  519. stroke.draggable = this.o.draggable;
  520. stroke.editable = this.o.editable;
  521. stroke.map = this.oMap;
  522. stroke.zIndex = this.o.maxZIndex + 100;
  523. //create the path and location marker
  524. for (a = 0; a < this.ln; a++) {
  525. point = this.create_objPoint(a);
  526. this.create.marker.call(this, a, point);
  527. stroke.path.push(point.position);
  528. }
  529. this.Polygon ?
  530. this.Polygon.setOptions(stroke)
  531. : this.Polygon = new google.maps.Polygon(stroke);
  532. google.maps.event.addListener(this.Polygon, 'click', function(obj) {
  533. self.o.onPolygonClick.call(self, obj);
  534. });
  535. this.add_polyEv('Polygon');
  536. },
  537. //fusion tables
  538. fusion: function() {
  539. this.o.fusion_options.styles = [this.o.stroke_options];
  540. this.o.fusion_options.map = this.oMap;
  541. this.Fusion ?
  542. this.Fusion.setOptions(this.o.fusion_options)
  543. : this.Fusion = new google.maps.FusionTablesLayer(this.o.fusion_options);
  544. },
  545. //directions mode
  546. directions: function() {
  547. var self = this,
  548. a,
  549. point,
  550. stopover,
  551. origin,
  552. destination,
  553. waypoints = [],
  554. distance = 0;
  555. //create the waypoints and location marker
  556. for (a = 0; a < this.ln; a++) {
  557. point = this.create_objPoint(a);
  558. //first location start point
  559. if (a === 0) {
  560. origin = point.position;
  561. //last location end point
  562. } else if (a === (this.ln - 1)) {
  563. destination = point.position;
  564. //waypoints in the middle
  565. } else {
  566. stopover = this.o.locations[a].stopover === true ? true : false;
  567. waypoints.push({
  568. location: point.position,
  569. stopover: stopover
  570. });
  571. }
  572. this.create.marker.call(this, a, point);
  573. }
  574. this.o.directions_options.origin = origin;
  575. this.o.directions_options.destination = destination;
  576. this.o.directions_options.waypoints = waypoints;
  577. this.directionsService || (this.directionsService = new google.maps.DirectionsService());
  578. this.directionsDisplay ?
  579. this.directionsDisplay.setOptions({draggable: this.o.draggable})
  580. : this.directionsDisplay = new google.maps.DirectionsRenderer({draggable: this.o.draggable});
  581. this.directionsDisplay.setMap(this.oMap);
  582. //show the directions panel
  583. if (this.o.directions_panel) {
  584. this.o.directions_panel = $(this.o.directions_panel);
  585. this.directionsDisplay.setPanel(this.o.directions_panel.get(0));
  586. }
  587. if (this.o.draggable) {
  588. google.maps.event.addListener(this.directionsDisplay, 'directions_changed', function() {
  589. var result = self.directionsDisplay.getDirections();
  590. distance = self.compute_distance(self.directionsDisplay.directions);
  591. self.o.afterRoute.call(self, distance, result.status, result);
  592. });
  593. }
  594. this.directionsService.route(this.o.directions_options, function(result, status) {
  595. //directions found
  596. if (status === google.maps.DirectionsStatus.OK) {
  597. distance = self.compute_distance(result);
  598. self.directionsDisplay.setDirections(result);
  599. }
  600. self.o.afterRoute.call(self, distance, status, result);
  601. });
  602. }
  603. };
  604. //route distance
  605. Maplace.prototype.compute_distance = function(result) {
  606. var total = 0,
  607. i,
  608. myroute = result.routes[0],
  609. rlen = myroute.legs.length;
  610. for (i = 0; i < rlen; i++) {
  611. total += myroute.legs[i].distance.value;
  612. }
  613. return total;
  614. };
  615. //wrapper for the infowindow types
  616. Maplace.prototype.type_to_open = {
  617. //google default infowindow
  618. bubble: function(location) {
  619. var self = this,
  620. infoWindow = { content: location.html || '' };
  621. if (location.infoWindowMaxWidth) {
  622. infoWindow.maxWidth = location.infoWindowMaxWidth;
  623. }
  624. this.infowindow = new google.maps.InfoWindow(infoWindow);
  625. google.maps.event.addListener(this.infowindow, 'closeclick', function(){
  626. self.CloseInfoWindow();
  627. });
  628. }
  629. };
  630. //open the infowindow
  631. Maplace.prototype.open_infowindow = function(index, marker, ev) {
  632. var point = this.o.locations[index],
  633. type = this.o.infowindow_type;
  634. //show if content and valid infowindow type provided
  635. if (point.html && this.type_to_open[type]) {
  636. this.o.beforeOpenInfowindow.call(this, index, point, marker);
  637. this.type_to_open[type].call(this, point);
  638. this.infowindow.open(this.oMap, marker);
  639. this.o.afterOpenInfowindow.call(this, index, point, marker);
  640. }
  641. };
  642. //gets the html for the menu
  643. Maplace.prototype.get_html_controls = function() {
  644. if (this.controls[this.o.controls_type] && this.controls[this.o.controls_type].getHtml) {
  645. this.current_control = this.controls[this.o.controls_type];
  646. return this.current_control.getHtml.apply(this);
  647. }
  648. return '';
  649. };
  650. //creates the controls menu
  651. Maplace.prototype.generate_controls = function() {
  652. //append menu on the div container
  653. if (!this.o.controls_on_map) {
  654. this.controls_wrapper.empty();
  655. this.controls_wrapper.append(this.get_html_controls());
  656. return;
  657. }
  658. //else
  659. //controls in map
  660. var cntr = $('<div class="on_gmap ' + this.o.controls_type + ' gmap_controls"></div>')
  661. .css(this.o.controls_applycss ? {margin: '5px'} : {}),
  662. inner = $(this.get_html_controls()).css(this.o.controls_applycss ? {
  663. background: '#fff',
  664. padding: '5px',
  665. border: '1px solid #eee',
  666. boxShadow: 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px',
  667. maxHeight: this.map_div.find('.canvas_map').outerHeight() - 80,
  668. minWidth: 100,
  669. overflowY: 'auto',
  670. overflowX: 'hidden'
  671. } : {});
  672. cntr.append(inner);
  673. //attach controls
  674. this.oMap.controls[this.o.controls_position].clear();
  675. this.oMap.controls[this.o.controls_position].push(cntr.get(0));
  676. };
  677. //resets obj map, markers, bounds, listeners, controllers
  678. Maplace.prototype.init_map = function() {
  679. var self = this;
  680. this.Polyline && this.Polyline.setMap(null);
  681. this.Polygon && this.Polygon.setMap(null);
  682. this.Fusion && this.Fusion.setMap(null);
  683. this.directionsDisplay && this.directionsDisplay.setMap(null);
  684. for (var i = this.markers.length - 1; i >= 0; i -= 1) {
  685. try {
  686. this.markers[i] && this.markers[i].setMap(null);
  687. } catch (err) {
  688. self.debug('init_map::markers::setMap', err.stack);
  689. }
  690. }
  691. this.markers.length = 0;
  692. this.markers = [];
  693. for (var e = this.circles.length - 1; e >= 0; e -= 1) {
  694. try {
  695. this.circles[e] && this.circles[e].setMap(null);
  696. } catch (err) {
  697. self.debug('init_map::circles::setMap', err.stack);
  698. }
  699. }
  700. this.circles.length = 0;
  701. this.circles = [];
  702. if (this.o.controls_on_map && this.oMap.controls) {
  703. this.oMap.controls[this.o.controls_position].forEach(function(element, index) {
  704. try {
  705. self.oMap.controls[this.o.controls_position].removeAt(index);
  706. } catch (err) {
  707. self.debug('init_map::removeAt', err.stack);
  708. }
  709. });
  710. }
  711. this.oBounds = new google.maps.LatLngBounds();
  712. };
  713. //perform the first view of the map
  714. Maplace.prototype.perform_load = function() {
  715. this.CloseInfoWindow();
  716. //one location
  717. if (this.ln === 1) {
  718. if (this.o.map_options.set_center) {
  719. this.oMap.setCenter(new google.maps.LatLng(this.o.map_options.set_center[0], this.o.map_options.set_center[1]));
  720. } else {
  721. this.oMap.fitBounds(this.oBounds);
  722. this.ViewOnMap(1);
  723. }
  724. this.o.map_options.zoom && this.oMap.setZoom(this.o.map_options.zoom);
  725. //no locations
  726. } else if (this.ln === 0) {
  727. if (this.o.map_options.set_center) {
  728. this.oMap.setCenter(new google.maps.LatLng(this.o.map_options.set_center[0], this.o.map_options.set_center[1]));
  729. } else {
  730. this.oMap.fitBounds(this.oBounds);
  731. }
  732. this.oMap.setZoom(this.o.map_options.zoom || 1);
  733. //n+ locations
  734. } else {
  735. this.oMap.fitBounds(this.oBounds);
  736. //check the start option
  737. if (typeof (this.o.start - 0) === 'number' && this.o.start > 0 && this.o.start <= this.ln) {
  738. this.ViewOnMap(this.o.start);
  739. //check if set_center exists
  740. } else if (this.o.map_options.set_center) {
  741. this.oMap.setCenter(new google.maps.LatLng(this.o.map_options.set_center[0], this.o.map_options.set_center[1]));
  742. //view all
  743. } else {
  744. this.ViewOnMap(this.view_all_key);
  745. }
  746. this.o.map_options.zoom && this.oMap.setZoom(this.o.map_options.zoom);
  747. }
  748. };
  749. Maplace.prototype.debug = function(code, msg) {
  750. this.o.debug && console.log(code, msg);
  751. return this;
  752. };
  753. /////////////////////////////////////////////////////////////////////////
  754. /////////////////////////////////////////////////////////////////////////
  755. //adds a custom menu to the class
  756. Maplace.prototype.AddControl = function(name, func) {
  757. if (!name || !func) {
  758. self.debug('AddControl', 'Missing "name" and "func" callback.');
  759. return false;
  760. }
  761. this.controls[name] = func;
  762. return this;
  763. };
  764. //close the infowindow
  765. Maplace.prototype.CloseInfoWindow = function() {
  766. if (this.infowindow && (this.current_index || this.current_index === 0)) {
  767. this.o.beforeCloseInfowindow.call(this, this.current_index, this.o.locations[this.current_index]);
  768. this.infowindow.close();
  769. this.infowindow = null;
  770. this.o.afterCloseInfowindow.call(this, this.current_index, this.o.locations[this.current_index]);
  771. }
  772. return this;
  773. };
  774. //checks if a location has to be in menu
  775. Maplace.prototype.ShowOnMenu = function(index) {
  776. if (index === this.view_all_key && this.o.view_all && this.ln > 1) {
  777. return true;
  778. }
  779. index = parseInt(index, 10);
  780. if (typeof (index - 0) === 'number' && index >= 0 && index < this.ln) {
  781. var on_menu = this.o.locations[index].on_menu === false ? false : true;
  782. if (on_menu) {
  783. return true;
  784. }
  785. }
  786. return false;
  787. };
  788. //triggers to show a location in map
  789. Maplace.prototype.ViewOnMap = function(index) {
  790. //view all
  791. if (index === this.view_all_key) {
  792. this.o.beforeViewAll.call(this);
  793. this.current_index = index;
  794. if (this.o.locations.length > 0 && this.o.generate_controls && this.current_control && this.current_control.activateCurrent) {
  795. this.current_control.activateCurrent.apply(this, [index]);
  796. }
  797. this.oMap.fitBounds(this.oBounds);
  798. this.o.afterViewAll.call(this);
  799. //specific location
  800. } else {
  801. index = parseInt(index, 10);
  802. if (typeof (index - 0) === 'number' && index > 0 && index <= this.ln) {
  803. try {
  804. google.maps.event.trigger(this.markers[index - 1], 'click');
  805. } catch (err) {
  806. this.debug('ViewOnMap::trigger', err.stack);
  807. }
  808. }
  809. }
  810. return this;
  811. };
  812. //replace current locations
  813. Maplace.prototype.SetLocations = function(locs, reload) {
  814. this.o.locations = locs;
  815. reload && this.Load();
  816. return this;
  817. };
  818. //adds one or more locations to the end of the array
  819. Maplace.prototype.AddLocations = function(locs, reload) {
  820. var self = this;
  821. if ($.isArray(locs)) {
  822. $.each(locs, function(index, value) {
  823. self.o.locations.push(value);
  824. });
  825. }
  826. if ($.isPlainObject(locs)) {
  827. this.o.locations.push(locs);
  828. }
  829. reload && this.Load();
  830. return this;
  831. };
  832. //adds a location at the specific index
  833. Maplace.prototype.AddLocation = function(location, index, reload) {
  834. var self = this;
  835. if ($.isPlainObject(location)) {
  836. this.o.locations.splice(index, 0, location);
  837. }
  838. reload && this.Load();
  839. return this;
  840. };
  841. //remove one or more locations
  842. Maplace.prototype.RemoveLocations = function(locs, reload) {
  843. var self = this,
  844. k = 0;
  845. if ($.isArray(locs)) {
  846. $.each(locs, function(index, value) {
  847. if ((value - k) < self.ln) {
  848. self.o.locations.splice(value - k, 1);
  849. }
  850. k++;
  851. });
  852. } else {
  853. if (locs < this.ln) {
  854. this.o.locations.splice(locs, 1);
  855. }
  856. }
  857. reload && this.Load();
  858. return this;
  859. };
  860. //check if already initialized with a Load()
  861. Maplace.prototype.Loaded = function() {
  862. return this.loaded;
  863. };
  864. //loads the options
  865. Maplace.prototype._init = function() {
  866. //store the locations length
  867. this.ln = this.o.locations.length;
  868. //update all locations with shared
  869. for (var i = 0; i < this.ln; i++) {
  870. var common = $.extend({}, this.o.shared);
  871. this.o.locations[i] = $.extend(common, this.o.locations[i]);
  872. if (this.o.locations[i].html) {
  873. this.o.locations[i].html = this.o.locations[i].html.replace('%index', i + 1);
  874. this.o.locations[i].html = this.o.locations[i].html.replace('%title', (this.o.locations[i].title || ''));
  875. }
  876. }
  877. //store dom references
  878. this.map_div = $(this.o.map_div);
  879. this.controls_wrapper = $(this.o.controls_div);
  880. return this;
  881. };
  882. //creates the map and menu
  883. Maplace.prototype.Load = function(args) {
  884. $.extend(true, this.o, args);
  885. args && args.locations && (this.o.locations = args.locations);
  886. this._init();
  887. //reset/init google map objects
  888. this.o.visualRefresh === false ? (google.maps.visualRefresh = false) : (google.maps.visualRefresh = true);
  889. this.init_map();
  890. this.create_objMap();
  891. //add markers
  892. this.add_markers_to_objMap();
  893. //generate controls
  894. if ((this.ln > 1 && this.o.generate_controls) || this.o.force_generate_controls) {
  895. this.o.generate_controls = true;
  896. this.generate_controls();
  897. } else {
  898. this.o.generate_controls = false;
  899. }
  900. var self = this;
  901. //first call
  902. if (!this.loaded) {
  903. google.maps.event.addListenerOnce(this.oMap, 'idle', function() {
  904. self.perform_load();
  905. });
  906. //add custom listeners
  907. for (var i in this.o.listeners) {
  908. if (this.o.listeners.hasOwnProperty(i)) {
  909. google.maps.event.addListener(this.oMap, i, this.o.listeners[i]);
  910. }
  911. }
  912. //all other calls
  913. } else {
  914. this.perform_load();
  915. }
  916. this.loaded = true;
  917. return this;
  918. };
  919. return Maplace;
  920. }));