1// @ts-nocheck 2/** 3 * @name Sidebar 4 * @class L.Control.Sidebar 5 * @extends L.Control 6 * @param {string} id - The id of the sidebar element (without the # character) 7 * @param {Object} [options] - Optional options object 8 * @param {string} [options.autopan=false] - whether to move the map when opening the sidebar to make maintain the visible center point 9 * @param {string} [options.position=left] - Position of the sidebar: 'left' or 'right' 10 * @param {string} [options.id] - ID of a predefined sidebar container that should be used 11 * @param {boolean} [data.close=true] Whether to add a close button to the pane header 12 * @see L.control.sidebar 13 */ 14L.Control.Sidebar = L.Control.extend(/** @lends L.Control.Sidebar.prototype */ { 15 includes: L.Evented ? L.Evented.prototype : L.Mixin.Events, 16 17 options: { 18 autopan: false, 19 closeButton: true, 20 container: null, 21 position: 'left' 22 }, 23 24 /** 25 * Create a new sidebar on this object. 26 * 27 * @constructor 28 * @param {Object} [options] - Optional options object 29 * @param {string} [options.autopan=false] - whether to move the map when opening the sidebar to make maintain the visible center point 30 * @param {string} [options.position=left] - Position of the sidebar: 'left' or 'right' 31 * @param {string} [options.container] - ID of a predefined sidebar container that should be used 32 * @param {bool} [data.close=true] Whether to add a close button to the pane header 33 */ 34 initialize: function(options, deprecatedOptions) { 35 if (typeof options === 'string') { 36 console.warn('this syntax is deprecated. please use L.control.sidebar({ container }) now'); 37 options = { container: options }; 38 } 39 40 if (typeof options === 'object' && options.id) { 41 console.warn('this syntax is deprecated. please use L.control.sidebar({ container }) now'); 42 options.container = options.id; 43 } 44 45 this._tabitems = []; 46 this._panes = []; 47 this._closeButtons = []; 48 49 L.setOptions(this, options); 50 L.setOptions(this, deprecatedOptions); 51 return this; 52 }, 53 54 /** 55 * Add this sidebar to the specified map. 56 * 57 * @param {L.Map} map 58 * @returns {Sidebar} 59 */ 60 onAdd: function(map) { 61 var i, child, tabContainers, newContainer, container; 62 63 // use container from previous onAdd() 64 container = this._container 65 66 // use the container given via options. 67 if (!container) { 68 container = this._container || typeof this.options.container === 'string' 69 ? L.DomUtil.get(this.options.container) 70 : this.options.container; 71 } 72 73 // if no container was specified or not found, create it and apply an ID 74 if (!container) { 75 container = L.DomUtil.create('div', 'leaflet-sidebar collapsed'); 76 if (typeof this.options.container === 'string') 77 container.id = this.options.container; 78 } 79 80 // Find paneContainer in DOM & store reference 81 this._paneContainer = container.querySelector('div.leaflet-sidebar-content'); 82 83 // If none is found, create it 84 if (this._paneContainer === null) 85 this._paneContainer = L.DomUtil.create('div', 'leaflet-sidebar-content', container); 86 87 // Find tabContainerTop & tabContainerBottom in DOM & store reference 88 tabContainers = container.querySelectorAll('ul.leaflet-sidebar-tabs, div.leaflet-sidebar-tabs > ul'); 89 this._tabContainerTop = tabContainers[0] || null; 90 this._tabContainerBottom = tabContainers[1] || null; 91 92 // If no container was found, create it 93 if (this._tabContainerTop === null) { 94 newContainer = L.DomUtil.create('div', 'leaflet-sidebar-tabs', container); 95 newContainer.setAttribute('role', 'tablist'); 96 this._tabContainerTop = L.DomUtil.create('ul', '', newContainer); 97 } 98 if (this._tabContainerBottom === null) { 99 newContainer = this._tabContainerTop.parentNode; 100 this._tabContainerBottom = L.DomUtil.create('ul', '', newContainer); 101 } 102 103 // Store Tabs in Collection for easier iteration 104 for (i = 0; i < this._tabContainerTop.children.length; i++) { 105 child = this._tabContainerTop.children[i]; 106 child._sidebar = this; 107 child._id = child.querySelector('a').hash.slice(1); // FIXME: this could break for links! 108 this._tabitems.push(child); 109 } 110 for (i = 0; i < this._tabContainerBottom.children.length; i++) { 111 child = this._tabContainerBottom.children[i]; 112 child._sidebar = this; 113 child._id = child.querySelector('a').hash.slice(1); // FIXME: this could break for links! 114 this._tabitems.push(child); 115 } 116 117 // Store Panes in Collection for easier iteration 118 for (i = 0; i < this._paneContainer.children.length; i++) { 119 child = this._paneContainer.children[i]; 120 if (child.tagName === 'DIV' && 121 L.DomUtil.hasClass(child, 'leaflet-sidebar-pane')) { 122 this._panes.push(child); 123 124 // Save references to close buttons 125 var closeButtons = child.querySelectorAll('.leaflet-sidebar-close'); 126 if (closeButtons.length) { 127 this._closeButtons.push(closeButtons[closeButtons.length - 1]); 128 this._closeClick(closeButtons[closeButtons.length - 1], 'on'); 129 } 130 } 131 } 132 133 // set click listeners for tab & close buttons 134 for (i = 0; i < this._tabitems.length; i++) { 135 this._tabClick(this._tabitems[i], 'on'); 136 } 137 138 // leaflet moves the returned container to the right place in the DOM 139 return container; 140 }, 141 142 /** 143 * Remove this sidebar from the map. 144 * 145 * @param {L.Map} map 146 * @returns {Sidebar} 147 */ 148 onRemove: function (map) { 149 // Remove click listeners for tab & close buttons 150 for (var i = 0; i < this._tabitems.length; i++) 151 this._tabClick(this._tabitems[i], 'off'); 152 for (var i = 0; i < this._closeButtons.length; i++) 153 this._closeClick(this._closeButtons[i], 'off'); 154 155 this._tabitems = []; 156 this._panes = []; 157 this._closeButtons = []; 158 159 return this; 160 }, 161 162 /** 163 * @method addTo(map: Map): this 164 * Adds the control to the given map. Overrides the implementation of L.Control, 165 * changing the DOM mount target from map._controlContainer.topleft to map._container 166 */ 167 addTo: function (map) { 168 this.onRemove(); 169 this._map = map; 170
171 this._container = this.onAdd(map); 172 173 L.DomUtil.addClass(this._container, 'leaflet-control'); 174 L.DomUtil.addClass(this._container, 'leaflet-sidebar-' + this.getPosition()); 175 if (L.Browser.touch) 176 L.DomUtil.addClass(this._container, 'leaflet-touch'); 177 178 // when adding to the map container, we should stop event propagation 179 L.DomEvent.disableScrollPropagation(this._container); 180 L.DomEvent.disableClickPropagation(this._container); 181 L.DomEvent.on(this._container, 'contextmenu', L.DomEvent.stopPropagation); 182 183 // insert as first child of map container (important for css) 184 map._container.insertBefore(this._container, map._container.firstChild); 185 186 return this; 187 }, 188 189 /** 190 * @deprecated - Please use remove() instead of removeFrom(), as of Leaflet 0.8-dev, the removeFrom() has been replaced with remove() 191 * Removes this sidebar from the map. 192 * @param {L.Map} map 193 * @returns {Sidebar} 194 */ 195 removeFrom: function(map) { 196 console.warn('removeFrom() has been deprecated, please use remove() instead as support for this function will be ending soon.'); 197 this._map._container.removeChild(this._container); 198 this.onRemove(map); 199 200 return this; 201 }, 202 203 /** 204 * Open sidebar (if it's closed) and show the specified tab. 205 * 206 * @param {string} id - The ID of the tab to show (without the # character) 207 * @returns {L.Control.Sidebar} 208 */ 209 open: function(id) { 210 var i, child, tab; 211 212 // If panel is disabled, stop right here 213 tab = this._getTab(id); 214 if (L.DomUtil.hasClass(tab, 'disabled')) 215 return this; 216 217 // Hide old active contents and show new content 218 for (i = 0; i < this._panes.length; i++) { 219 child = this._panes[i]; 220 if (child.id === id) 221 L.DomUtil.addClass(child, 'active'); 222 else if (L.DomUtil.hasClass(child, 'active')) 223 L.DomUtil.removeClass(child, 'active'); 224 } 225 226 // Remove old active highlights and set new highlight 227 for (i = 0; i < this._tabitems.length; i++) { 228 child = this._tabitems[i]; 229 if (child.querySelector('a').hash === '#' + id) 230 L.DomUtil.addClass(child, 'active'); 231 else if (L.DomUtil.hasClass(child, 'active')) 232 L.DomUtil.removeClass(child, 'active'); 233 } 234 235 this.fire('content', { id: id }); 236 237 // Open sidebar if it's closed 238 if (L.DomUtil.hasClass(this._container, 'collapsed')) { 239 this.fire('opening'); 240 L.DomUtil.removeClass(this._container, 'collapsed'); 241 if (this.options.autopan) this._panMap('open'); 242 } 243 244 return this; 245 }, 246 247 /** 248 * Close the sidebar (if it's open). 249 * 250 * @returns {L.Control.Sidebar} 251 */ 252 close: function() { 253 var i; 254 255 // Remove old active highlights 256 for (i = 0; i < this._tabitems.length; i++) { 257 var child = this._tabitems[i]; 258 if (L.DomUtil.hasClass(child, 'active')) 259 L.DomUtil.removeClass(child, 'active'); 260 } 261 262 // close sidebar, if it's opened 263 if (!L.DomUtil.hasClass(this._container, 'collapsed')) { 264 this.fire('closing'); 265 L.DomUtil.addClass(this._container, 'collapsed'); 266 if (this.options.autopan) this._panMap('close'); 267 } 268 269 return this; 270 }, 271 272 /** 273 * Add a panel to the sidebar 274 * 275 * @example 276 * sidebar.addPanel({ 277 * id: 'userinfo', 278 * tab: '<i class="fa fa-gear"></i>', 279 * pane: someDomNode.innerHTML, 280 * position: 'bottom' 281 * }); 282 * 283 * @param {Object} [data] contains the data for the new Panel: 284 * @param {String} [data.id] the ID for the new Panel, must be unique for the whole page 285 * @param {String} [data.position='top'] where the tab will appear: 286 * on the top or the bottom of the sidebar. 'top' or 'bottom' 287 * @param {HTMLString} {DOMnode} [data.tab] content of the tab item, as HTMLstring or DOM node 288 * @param {HTMLString} {DOMnode} [data.pane] content of the panel, as HTMLstring or DOM node 289 * @param {String} [data.link] URL to an (external) link that will be opened instead of a panel 290 * @param {String} [data.title] Title for the pane header 291 * @param {String} {Function} [data.button] URL to an (external) link or a click listener function that will be opened instead of a panel 292 * @param {bool} [data.disabled] If the tab should be disabled by default 293 * 294 * @returns {L.Control.Sidebar} 295 */ 296 addPanel: function(data) { 297 var pane, tab, tabHref, closeButtons, content; 298 299 // Create tab node 300 tab = L.DomUtil.create('li', data.disabled ? 'disabled' : ''); 301 tabHref = L.DomUtil.create('a', '', tab); 302 tabHref.href = '#' + data.id; 303 tabHref.setAttribute('role', 'tab'); 304 tabHref.innerHTML = data.tab; 305 tab._sidebar = this; 306 tab._id = data.id; 307 tab._button = data.button; // to allow links to be disabled, the href cannot be used 308 if (data.title && data.title[0] !== '<') tab.title = data.title; 309 310 // append it to the DOM and store JS references 311 if (data.position === 'bottom') 312 this._tabContainerBottom.appendChild(tab); 313 else 314 this._tabContainerTop.appendChild(tab); 315 316 this._tabitems.push(tab); 317 318 // Create pane node 319 if (data.pane) { 320 if (typeof data.pane === 'string') { 321 // pane is given as HTML string 322 pane = L.DomUtil.create('DIV', 'leaflet-sidebar-pane', this._paneContainer); 323 content = ''; 324 if (data.title) 325 content += '<h1 class="leaflet-sidebar-header">' + data.title; 326 if (this.options.closeButton) 327 content += '<span class="leaflet-sidebar-close"><i class="fa fa-caret-' + this.options.position + '"></i></span>'; 328 if (data.title) 329 content += '</h1>'; 330 pane.innerHTML = content + data.pane; 331 } else { 332 // pane is given as DOM object 333 pane = data.pane; 334 this._paneContainer.appendChild(pane); 335 } 336 pane.id = data.id; 337 338 this._panes.push(pane); 339 340 // Save references to close button & register click listener 341 closeButtons = pane.querySelectorAll('.leaflet-sidebar-close'); 342 if (closeButtons.length) { 343 // select last button, because thats rendered on top 344 this._closeButtons.push(closeButtons[closeButtons.length - 1]); 345 this._closeClick(closeButtons[closeButtons.length - 1], 'on'); 346 } 347 } 348 349 // Register click listeners, if the sidebar is on the map 350 this._tabClick(tab, 'on'); 351 352 return this; 353 }, 354 355 /** 356 * Removes a panel from the sidebar 357 * 358 * @example 359 * sidebar.remove('userinfo'); 360 * 361 * @param {String} [id] the ID of the panel that is to be removed 362 * @returns {L.Control.Sidebar} 363 */ 364 removePanel: function(id) { 365 var i, j, tab, pane, closeButtons; 366 367 // find the tab & panel by ID, remove them, and clean up 368 for (i = 0; i < this._tabitems.length; i++) { 369 if (this._tabitems[i]._id === id) { 370 tab = this._tabitems[i]; 371 372 // Remove click listeners 373 this._tabClick(tab, 'off'); 374 375 tab.remove(); 376 this._tabitems.splice(i, 1); 377 break; 378 } 379 } 380 381 for (i = 0; i < this._panes.length; i++) { 382 if (this._panes[i].id === id) { 383 pane = this._panes[i]; 384 closeButtons = pane.querySelectorAll('.leaflet-sidebar-close'); 385 for (j = 0; j < closeButtons.length; j++) { 386 this._closeClick(closeButtons[j], 'off'); 387 } 388 389 pane.remove(); 390 this._panes.splice(i, 1); 391 392 break; 393 } 394 } 395 396 return this; 397 }, 398 399 /** 400 * enables a disabled tab/panel 401 * 402 * @param {String} [id] ID of the panel to enable 403 * @returns {L.Control.Sidebar} 404 */ 405 enablePanel: function(id) { 406 var tab = this._getTab(id); 407 L.DomUtil.removeClass(tab, 'disabled'); 408 409 return this; 410 }, 411 412 /** 413 * disables an enabled tab/panel 414 * 415 * @param {String} [id] ID of the panel to disable 416 * @returns {L.Control.Sidebar} 417 */ 418 disablePanel: function(id) { 419 var tab = this._getTab(id); 420 L.DomUtil.addClass(tab, 'disabled'); 421 422 return this; 423 }, 424 425 onTabClick: function(e) { 426 // `this` points to the tab DOM element! 427 if (L.DomUtil.hasClass(this, 'active')) { 428 this._sidebar.close(); 429 } else if (!L.DomUtil.hasClass(this, 'disabled')) { 430 if (typeof this._button === 'string') // an url 431 window.location.href = this._button; 432 else if (typeof this._button === 'function') // a clickhandler 433 this._button(e); 434 else // a normal pane 435 this._sidebar.open(this.querySelector('a').hash.slice(1)); 436 } 437 }, 438 439 /** 440 * (un)registers the onclick event for the given tab, 441 * depending on the second argument. 442 * @private 443 * 444 * @param {DOMelement} [tab] 445 * @param {String} [on] 'on' or 'off' 446 */ 447 _tabClick: function(tab, on) { 448 var link = tab.querySelector('a'); 449 if (!link.hasAttribute('href') || link.getAttribute('href')[0] !== '#') 450 return; 451 452 if (on === 'on') { 453 L.DomEvent 454 .on(tab.querySelector('a'), 'click', L.DomEvent.preventDefault, tab) 455 .on(tab.querySelector('a'), 'click', this.onTabClick, tab); 456 } else { 457 L.DomEvent.off(tab.querySelector('a'), 'click', this.onTabClick, tab); 458 } 459 }, 460 461 onCloseClick: function() { 462 this.close(); 463 }, 464 465 /** 466 * (un)registers the onclick event for the given close button 467 * depending on the second argument 468 * @private 469 * 470 * @param {DOMelement} [closeButton] 471 * @param {String} [on] 'on' or 'off' 472 */ 473 _closeClick: function(closeButton, on) { 474 if (on === 'on') { 475 L.DomEvent.on(closeButton, 'click', this.onCloseClick, this); 476 } else { 477 L.DomEvent.off(closeButton, 'click', this.onCloseClick); 478 } 479 }, 480 481 /** 482 * Finds & returns the DOMelement of a tab 483 * 484 * @param {String} [id] the id of the tab 485 * @returns {DOMelement} the tab specified by id, null if not found 486 */ 487 _getTab: function(id) { 488 for (var i = 0; i < this._tabitems.length; i++) { 489 if (this._tabitems[i]._id === id) 490 return this._tabitems[i]; 491 } 492 493 throw Error('tab "' + id + '" not found'); 494 }, 495 496 /** 497 * Helper for autopan: Pans the map for open/close events 498 * 499 * @param {String} [openClose] The behaviour to enact ('open' | 'close') 500 */ 501 _panMap: function(openClose) { 502 var panWidth = Number.parseInt(L.DomUtil.getStyle(this._container, 'max-width')) / 2; 503 if ( 504 openClose === 'open' && this.options.position === 'left' || 505 openClose === 'close' && this.options.position === 'right' 506 ) panWidth *= -1; 507 this._map.panBy([panWidth, 0], { duration: 0.5 }); 508 } 509}); 510 511/** 512 * Create a new sidebar. 513 * 514 * @example 515 * var sidebar = L.control.sidebar({ container: 'sidebar' }).addTo(map); 516 * 517 * @param {Object} [options] - Optional options object 518 * @param {string} [options.autopan=false] - whether to move the map when opening the sidebar to make maintain the visible center point 519 * @param {string} [options.position=left] - Position of the sidebar: 'left' or 'right' 520 * @param {string} [options.container] - ID of a predefined sidebar container that should be used 521 * @param {boolean} [data.close=true] Whether to add a close button to the pane header 522 * @returns {Sidebar} A new sidebar instance 523 */ 524L.control.sidebar = function(options, deprecated) { 525 return new L.Control.Sidebar(options, deprecated); 526};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.