60 | /*global L, JSONP*/
61 |
62 | 'use strict';
63 |
64 | /**
65 | Initialises a new instance of the L.Icon.WikipediaIcon class.
66 | The image is taken from here:
67 | {@link http://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Tango_style_Wikipedia_Icon.svg/40px-Tango_style_Wikipedia_Icon.svg.png }
68 | @class WikipediaIcon
69 | */
70 | L.Icon.WikipediaIcon = L.Icon.extend({
71 | options: {
72 | iconUrl: 'images/wikipedia-icon.png',
73 | iconRetinaUrl: 'images/wikipedia-icon-2x.png',
74 | iconSize: [40, 40],
75 | popupAnchor: [0, -20]
76 | }
77 | });
78 |
79 | /**
80 | Creates a new Wikipedia icon.
81 | @method wikipediaIcon
82 | @returns {module:wikipedia-layer~WikipediaIcon} A new Wikipedia icon.
83 | */
84 | L.icon.wikipediaIcon = function () {
85 | return new L.Icon.WikipediaIcon();
86 | };
87 |
88 | /**
89 | A Wikipedia layer group for leaflet.
90 | @class WikipediaLayer
91 | @param {Object} [options] - These layer options are merged with the default options
92 | @param {string} [options.url='https://en.wikipedia.org/'] - The URL for Wikipedia
93 | @param {number} [options.limit=100] - The maximum number of search results to return
94 | @param {Boolean} [options.popupOnMouseover=false] - If true then the popup will open on mouse over; otherwise it won't
95 | @param {Boolean} [options.clearOutsideBounds=false] - If true then markers outside the current map bounds will be removed; otherwise they won't
96 | @param {string} [options.target='_self'] - specifies where to open the linked Wikipedia page
97 | @param {string} [options.images='images/'] - specifies the folder that contains the Wikipedia icon images
98 | @param {number} [options.minZoom='0'] - minimum zoom number
99 | @param {number} [options.maxZoom='18'] - maximum zoom number
100 | */
101 | L.LayerGroup.WikipediaLayer = L.LayerGroup.extend(
102 | /** @lends module:wikipedia-layer~WikipediaLayer */
103 | {
104 | /**
105 | Query string fragment to use when linking to a Wikipedia page.
106 | @constant
107 | @default
108 | @private
109 | */
110 | PAGE: '?curid=',
111 | /**
112 | URL fragment to use when connecting to the API.
113 | @constant
114 | @default
115 | @private
116 | */
117 | API: 'w/api.php',
118 | /**
119 | Default layer options.
120 | @default
121 | @private
122 | */
123 | options: {
124 | url: 'https://en.wikipedia.org/',
125 | limit: 100,
126 | popupOnMouseover: false,
127 | clearOutsideBounds: false,
128 | target: '_self',
129 | images: 'images/',
130 | minZoom: 0,
131 | maxZoom: 18
132 | },
133 | /**
134 | Create the layer group using the passed options.
135 | @param {Object} options
136 | @private
137 | */
138 | initialize: function (options) {
139 | options = options || {};
140 | L.Util.setOptions(this, options);
141 |
142 | if (this.options.images.indexOf('/', this.options.images.length - 1) === -1) {
143 | this.options.images += '/';
144 | }
145 |
146 | this._layers = {};
147 | },
148 | /**
149 | Store a reference to the map and call the requestData() method.
150 | @private
151 | */
152 | onAdd: function (map) {
153 | map.on('moveend', this.requestData, this);
154 | this._map = map;
155 | this.requestData();
156 | },
157 | /**
158 | Remove the 'moveend' event listener and clear all the markers.
159 | @private
160 | */
161 | onRemove: function (map) {
162 | map.off('moveend', this.requestData, this);
163 | this.clearLayers();
164 | this._layers = {};
165 | },
166 | /**
167 | Send a query request for JSONP data.
168 | @private
169 | */
170 | requestData: function () {
171 | var zoom = this._map.getZoom(),
172 | origin = this._map.getCenter(),
173 | data = {
174 | format: 'json',
175 | action: 'query',
176 | list: 'geosearch',
177 | gslimit: this.options.limit,
178 | gsradius: this.getRadius(),
179 | gscoord: origin.lat + '|' + origin.lng
180 | },
181 | self = this;
182 |
183 | if (zoom >= this.options.minZoom && zoom <= this.options.maxZoom) {
184 | JSONP({
185 | url: this.options.url + this.API,
186 | data: data,
187 | success: function (response) { self.parseData(response); }
188 | });
189 | } else {
190 | this.clearLayers();
191 | this._layers = {};
192 | }
193 | },
194 | /**
195 | Create a new marker.
196 | @param {Object} result - JSON data
197 | @return {L.marker} The new marker.
198 | @private
199 | */
200 | getMarker: function (result) {
201 | var icon = new L.Icon.WikipediaIcon({
202 | iconUrl: this.options.images + 'wikipedia-icon.png',
203 | iconRetinaUrl: this.options.images + 'wikipedia-icon-2x.png'
204 | }),
205 | marker = L.marker([result.lat, result.lon], { icon: icon }),
206 | href = this.options.url + this.PAGE + result.pageid,
207 | popup = '<a href="' + href + '" target="' + this.options.target + '">' + result.title + '</a>';
208 |
209 | marker.bindPopup(popup);
210 |
211 | if (this.options.popupOnMouseover) {
212 | marker.on('mouseover', function (event) {
213 | event.target.openPopup();
214 | });
215 | }
216 |
217 | return marker;
218 | },
219 | /**
220 | Add a marker by calling the addMarker() method.
221 | @param {Object} result - JSON data
222 | @private
223 | */
224 | addMarker: function (result) {
225 | var marker = this.getMarker(result),
226 | key = result.pageid;
227 |
228 | if (!this._layers[key]) {
229 | this._layers[key] = marker;
230 | this.addLayer(marker);
231 | }
232 | },
233 | /**
234 | Parse the response data and call the addMarker() method for each result.
235 | @param {Object} response - JSON data
236 | @private
237 | */
238 | parseData: function (response) {
239 | var results = response.query.geosearch,
240 | index = 0,
241 | length = results.length;
242 |
243 | for (index; index < length; index += 1) {
244 | this.addMarker(results[index]);
245 | }
246 |
247 | if (this.options.clearOutsideBounds) {
248 | this.clearOutsideBounds();
249 | }
250 | },
251 | /**
252 | Get the radius for the Wikipedia search based on the current map bounds.
253 | This is limited to the maximum supported by the API, which is 10000.
254 | @return {number} The radius to search.
255 | @private
256 | */
257 | getRadius: function () {
258 | var bounds = this._map.getBounds(),
259 | northWest = bounds.getNorthWest(),
260 | southEast = bounds.getSouthEast(),
261 | radius = northWest.distanceTo(southEast) / 2;
262 |
263 | return radius > 10000 ? 10000 : radius;
264 | },
265 | /**
266 | Clear all markers currently outside the map bounds.
267 | @private
268 | */
269 | clearOutsideBounds: function () {
270 | var bounds = this._map.getBounds(),
271 | latLng,
272 | key;
273 |
274 | for (key in this._layers) {
275 | if (this._layers.hasOwnProperty(key)) {
276 | latLng = this._layers[key].getLatLng();
277 |
278 | if (!bounds.contains(latLng)) {
279 | this.removeLayer(this._layers[key]);
280 | delete this._layers[key];
281 | }
282 | }
283 | }
284 | }
285 | }
286 | );
287 |
288 | /**
289 | Creates a new Wikipedia layer.
290 | @method wikipediaLayer
291 | @returns {module:wikipedia-layer~WikipediaLayer} A new Wikipedia layer.
292 | */
293 | L.layerGroup.wikipediaLayer = function (options) {
294 | return new L.LayerGroup.WikipediaLayer(options);
295 | };
296 |
297 |
298 |