Commit | Line | Data |
---|---|---|
84de180d MS |
1 | /** |
2 | * Map route planner based on Google Maps. | |
3 | * | |
867c4493 AE |
4 | * @author Matthias Schmidt |
5 | * @copyright 2001-2019 WoltLab GmbH | |
6 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
7 | * @module WoltLabSuite/Core/Controller/Map/Route/Planner | |
a99dfb92 | 8 | * @woltlabExcludeBundle all |
84de180d | 9 | */ |
867c4493 AE |
10 | define(["require", "exports", "tslib", "../../../Ajax/Status", "../../../Core", "../../../Dom/Util", "../../../Language", "../../../Ui/Dialog"], function (require, exports, tslib_1, AjaxStatus, Core, Util_1, Language, Dialog_1) { |
11 | "use strict"; | |
716617cf TD |
12 | AjaxStatus = tslib_1.__importStar(AjaxStatus); |
13 | Core = tslib_1.__importStar(Core); | |
14 | Util_1 = tslib_1.__importDefault(Util_1); | |
15 | Language = tslib_1.__importStar(Language); | |
16 | Dialog_1 = tslib_1.__importDefault(Dialog_1); | |
867c4493 AE |
17 | class ControllerMapRoutePlanner { |
18 | constructor(buttonId, destination) { | |
19 | this.didInitDialog = false; | |
20 | this.directionsRenderer = undefined; | |
21 | this.directionsService = undefined; | |
22 | this.googleLink = undefined; | |
23 | this.lastOrigin = undefined; | |
24 | this.map = undefined; | |
25 | this.originInput = undefined; | |
26 | this.travelMode = undefined; | |
27 | const button = document.getElementById(buttonId); | |
28 | if (button === null) { | |
29 | throw new Error(`Unknown button with id '${buttonId}'`); | |
30 | } | |
31 | this.button = button; | |
32 | this.button.addEventListener("click", (ev) => this.openDialog(ev)); | |
33 | this.destination = destination; | |
50aa3a01 | 34 | } |
50aa3a01 TD |
35 | /** |
36 | * Calculates the route based on the given result of a location search. | |
50aa3a01 | 37 | */ |
867c4493 AE |
38 | _calculateRoute(data) { |
39 | const dialog = Dialog_1.default.getDialog(this).dialog; | |
50aa3a01 | 40 | if (data.label) { |
867c4493 | 41 | this.originInput.value = data.label; |
50aa3a01 | 42 | } |
867c4493 AE |
43 | if (this.map === undefined) { |
44 | const mapContainer = dialog.querySelector(".googleMap"); | |
45 | this.map = new google.maps.Map(mapContainer, { | |
46 | disableDoubleClickZoom: window.WCF.Location.GoogleMaps.Settings.get("disableDoubleClickZoom"), | |
47 | draggable: window.WCF.Location.GoogleMaps.Settings.get("draggable"), | |
50aa3a01 | 48 | mapTypeId: google.maps.MapTypeId.ROADMAP, |
867c4493 AE |
49 | scaleControl: window.WCF.Location.GoogleMaps.Settings.get("scaleControl"), |
50 | scrollwheel: window.WCF.Location.GoogleMaps.Settings.get("scrollwheel"), | |
50aa3a01 | 51 | }); |
867c4493 AE |
52 | this.directionsService = new google.maps.DirectionsService(); |
53 | this.directionsRenderer = new google.maps.DirectionsRenderer(); | |
54 | this.directionsRenderer.setMap(this.map); | |
55 | const directionsContainer = dialog.querySelector(".googleMapsDirections"); | |
56 | this.directionsRenderer.setPanel(directionsContainer); | |
57 | this.googleLink = dialog.querySelector(".googleMapsDirectionsGoogleLink"); | |
50aa3a01 | 58 | } |
867c4493 AE |
59 | const request = { |
60 | destination: this.destination, | |
50aa3a01 TD |
61 | origin: data.location, |
62 | provideRouteAlternatives: true, | |
867c4493 | 63 | travelMode: google.maps.TravelMode[this.travelMode.value.toUpperCase()], |
50aa3a01 TD |
64 | }; |
65 | AjaxStatus.show(); | |
5ffe7716 TD |
66 | // .route() returns a promise, but we rely on the callback API for compatibility reasons. |
67 | void this.directionsService.route(request, (result, status) => this.setRoute(result, status)); | |
867c4493 AE |
68 | this.googleLink.href = this.getGoogleMapsLink(data.location, this.travelMode.value); |
69 | this.lastOrigin = data.location; | |
70 | } | |
50aa3a01 TD |
71 | /** |
72 | * Returns the Google Maps link based on the given optional directions origin | |
73 | * and optional travel mode. | |
50aa3a01 | 74 | */ |
867c4493 | 75 | getGoogleMapsLink(origin, travelMode) { |
50aa3a01 | 76 | if (origin) { |
867c4493 | 77 | let link = `https://www.google.com/maps/dir/?api=1&origin=${origin.lat()},${origin.lng()}&destination=${this.destination.lat()},${this.destination.lng()}`; |
50aa3a01 | 78 | if (travelMode) { |
867c4493 | 79 | link += `&travelmode=${travelMode}`; |
50aa3a01 TD |
80 | } |
81 | return link; | |
82 | } | |
867c4493 AE |
83 | return `https://www.google.com/maps/search/?api=1&query=${this.destination.lat()},${this.destination.lng()}`; |
84 | } | |
50aa3a01 TD |
85 | /** |
86 | * Initializes the route planning dialog. | |
87 | */ | |
867c4493 AE |
88 | initDialog() { |
89 | if (!this.didInitDialog) { | |
90 | const dialog = Dialog_1.default.getDialog(this).dialog; | |
50aa3a01 | 91 | // make input element a location search |
867c4493 AE |
92 | this.originInput = dialog.querySelector('input[name="origin"]'); |
93 | new window.WCF.Location.GoogleMaps.LocationSearch(this.originInput, (data) => this._calculateRoute(data)); | |
94 | this.travelMode = dialog.querySelector('select[name="travelMode"]'); | |
95 | this.travelMode.addEventListener("change", this.updateRoute.bind(this)); | |
96 | this.didInitDialog = true; | |
50aa3a01 | 97 | } |
867c4493 | 98 | } |
50aa3a01 TD |
99 | /** |
100 | * Opens the route planning dialog. | |
101 | */ | |
867c4493 AE |
102 | openDialog(event) { |
103 | event.preventDefault(); | |
104 | Dialog_1.default.open(this); | |
105 | } | |
50aa3a01 TD |
106 | /** |
107 | * Handles the response of the direction service. | |
50aa3a01 | 108 | */ |
867c4493 | 109 | setRoute(result, status) { |
50aa3a01 | 110 | AjaxStatus.hide(); |
6b64df9d | 111 | if (status === google.maps.DirectionsStatus.OK) { |
867c4493 AE |
112 | Util_1.default.show(this.map.getDiv().parentElement); |
113 | google.maps.event.trigger(this.map, "resize"); | |
114 | this.directionsRenderer.setDirections(result); | |
115 | Util_1.default.show(this.travelMode.closest("dl")); | |
116 | Util_1.default.show(this.googleLink); | |
117 | Util_1.default.innerError(this.originInput, false); | |
50aa3a01 TD |
118 | } |
119 | else { | |
120 | // map irrelevant errors to not found error | |
6b64df9d TD |
121 | if (status !== google.maps.DirectionsStatus.OVER_QUERY_LIMIT && |
122 | status !== google.maps.DirectionsStatus.REQUEST_DENIED) { | |
867c4493 | 123 | status = google.maps.DirectionsStatus.NOT_FOUND; |
50aa3a01 | 124 | } |
867c4493 | 125 | Util_1.default.innerError(this.originInput, Language.get(`wcf.map.route.error.${status.toLowerCase()}`)); |
50aa3a01 | 126 | } |
867c4493 | 127 | } |
50aa3a01 TD |
128 | /** |
129 | * Updates the route after the travel mode has been changed. | |
130 | */ | |
867c4493 | 131 | updateRoute() { |
50aa3a01 | 132 | this._calculateRoute({ |
867c4493 | 133 | location: this.lastOrigin, |
50aa3a01 TD |
134 | }); |
135 | } | |
867c4493 AE |
136 | /** |
137 | * Sets up the route planner dialog. | |
138 | */ | |
139 | _dialogSetup() { | |
140 | return { | |
141 | id: this.button.id + "Dialog", | |
142 | options: { | |
143 | onShow: this.initDialog.bind(this), | |
144 | title: Language.get("wcf.map.route.planner"), | |
145 | }, | |
146 | source: ` | |
147 | <div class="googleMapsDirectionsContainer" style="display: none;"> | |
148 | <div class="googleMap"></div> | |
149 | <div class="googleMapsDirections"></div> | |
150 | </div> | |
151 | <small class="googleMapsDirectionsGoogleLinkContainer"> | |
152 | <a href="${this.getGoogleMapsLink()}" class="googleMapsDirectionsGoogleLink" target="_blank" style="display: none;">${Language.get("wcf.map.route.viewOnGoogleMaps")}</a> | |
153 | </small> | |
154 | <dl> | |
155 | <dt>${Language.get("wcf.map.route.origin")}</dt> | |
156 | <dd> | |
157 | <input type="text" name="origin" class="long" autofocus> | |
158 | </dd> | |
159 | </dl> | |
160 | <dl style="display: none;"> | |
161 | <dt>${Language.get("wcf.map.route.travelMode")}</dt> | |
162 | <dd> | |
163 | <select name="travelMode"> | |
164 | <option value="driving">${Language.get("wcf.map.route.travelMode.driving")}</option> | |
165 | <option value="walking">${Language.get("wcf.map.route.travelMode.walking")}</option> | |
166 | <option value="bicycling">${Language.get("wcf.map.route.travelMode.bicycling")}</option> | |
167 | <option value="transit">${Language.get("wcf.map.route.travelMode.transit")}</option> | |
168 | </select> | |
169 | </dd> | |
170 | </dl>`, | |
171 | }; | |
172 | } | |
173 | } | |
174 | Core.enableLegacyInheritance(ControllerMapRoutePlanner); | |
175 | return ControllerMapRoutePlanner; | |
84de180d | 176 | }); |