PageSourceSearch

https://ghostwhite-duck-238601.hostingersite.com/wp-content/plugin…/src/leaflet/leaflet-search.js?ver=1.4.51

js ghostwhite-duck-238601.hostingersite.com collected 2026-10-03 04:24:47 UTC 31,016 bytes, 1,025 lines download raw bytes

vendor: 31,016 bytes, lines 1-1025
1/*
2	Name					Data passed			   Description
3
4	Managed Events:
5	 search:locationfound	{latlng, title, layer} fired after moved and show markerLocation
6	 search:expanded		{}					   fired after control was expanded
7	 search:collapsed		{}					   fired after control was collapsed
8 	 search:cancel			{}					   fired after cancel button clicked
9
10	Public methods:
11	 setLayer()				L.LayerGroup()         set layer search at runtime
12	 showAlert()            'Text message'         show alert message
13	 searchText()			'Text searched'        search text by external code
14*/
15
16//TODO implement can do research on multiple sources layers and remote		
17//TODO history: false,		//show latest searches in tooltip		
18//FIXME option condition problem {autoCollapse: true, markerLocation: true} not show location
19//FIXME option condition problem {autoCollapse: false }
20//
21//TODO here insert function  search inputText FIRST in _recordsCache keys and if not find results.. 
22//  run one of callbacks search(sourceData,jsonpUrl or options.layer) and run this.showTooltip
23//
24//TODO change structure of _recordsCache
25//	like this: _recordsCache = {"text-key1": {loc:[lat,lng], ..other attributes.. }, {"text-key2": {loc:[lat,lng]}...}, ...}
26//	in this mode every record can have a free structure of attributes, only 'loc' is required
27//TODO important optimization!!! always append data in this._recordsCache
28//  now _recordsCache content is emptied and replaced with new data founded
29//  always appending data on _recordsCache give the possibility of caching ajax, jsonp and layersearch!
30//
31//TODO here insert function  search inputText FIRST in _recordsCache keys and if not find results.. 
32//  run one of callbacks search(sourceData,jsonpUrl or options.layer) and run this.showTooltip
33//
34//TODO change structure of _recordsCache
35//	like this: _recordsCache = {"text-key1": {loc:[lat,lng], ..other attributes.. }, {"text-key2": {loc:[lat,lng]}...}, ...}
36//	in this way every record can have a free structure of attributes, only 'loc' is required
37
38(function (factory) {
39  if(typeof define === 'function' && define.amd) {
40  //AMD
41      define(['leaflet'], factory);
42  } else if(typeof module !== 'undefined') {
43  // Node/CommonJS
44      module.exports = factory(require('leaflet'));
45  } else {
46  // Browser globals
47      if(typeof window.L === 'undefined')
48          throw 'Leaflet must be loaded first';
49      factory(window.L);
50  }
51})(function (L) {
52
53
54L.Control.Search = L.Control.extend({
55
56includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,
57
58options: {
59  url: '',						//url for search by ajax request, ex: "search.php?q={s}". Can be function to returns string for dynamic parameter setting
60  layer: null,					//layer where search markers(is a L.LayerGroup)				
61  sourceData: null,				//function to fill _recordsCache, passed searching text by first param and callback in second				
62  //TODO implements uniq option 'sourceData' to recognizes source type: url,array,callback or layer				
63  jsonpParam: null,				//jsonp param name for search by jsonp service, ex: "callback"
64  propertyLoc: 'loc',				//field for remapping location, using array: ['latname','lonname'] for select double fields(ex. ['lat','lon'] ) support dotted format: 'prop.subprop.title'
65  propertyName: 'title',			//property in marker.options(or feature.properties for vector layer) trough filter elements in layer,
66  formatData: null,				//callback for reformat all data from source to indexed data object
67  filterData: null,				//callback for filtering data from text searched, params: textSearch, allRecords
68  moveToLocation: null,			//callback run on location found, params: latlng, title, map
69  buildTip: null,					//function to return row tip html node(or html string), receive text tooltip in first param
70  container: '',					//container id to insert Search Control		
71  zoom: null,						//default zoom level for move to location
72  minLength: 1,					//minimal text length for autocomplete
73  initial: true,					//search elements only by initial text
74  casesensitive: false,			//search elements in case sensitive text
75  autoType: true,					//complete input with first suggested result and select this filled-in text.
76  delayType: 400,					//delay while typing for show tooltip
77  tooltipLimit: -1,				//limit max results to show in tooltip. -1 for no limit, 0 for no results
78  tipAutoSubmit: true,			//auto map panTo when click on tooltip
79  firstTipSubmit: false,			//auto select first result con enter click
80  autoResize: true,				//autoresize on input change
81  collapsed: true,				//collapse search control at startup
82  autoCollapse: false,			//collapse search control after submit(on button or on tips if enabled tipAutoSubmit)
83  autoCollapseTime: 1200,			//delay for autoclosing alert and collapse after blur
84  textErr: 'Location not found',	//error message
85  textCancel: 'Cancel',		    //title in cancel button		
86  textPlaceholder: 'Search...',   //placeholder value			
87  hideMarkerOnCollapse: false,    //remove circle and marker on search control collapsed		
88  position: 'topleft',		
89  marker: {						//custom L.Marker or false for hide
90    icon: false,				//custom L.Icon for maker location or false for hide
91    animate: true,				//animate a circle over location found
92    circle: {					//draw a circle in location found
93      radius: 10,
94      weight: 3,
95      color: '#e03',
96      stroke: true,
97      fill: false
98    }
99  }
100},
101
102_getPath: function(obj, prop) {
103  var parts = prop.split('.'),
104    last = parts.pop(),
105    len = parts.length,
106    cur = parts[0],
107    i = 1;
108
109  if(len > 0)
110    while((obj = obj[cur]) && i < len)
111      cur = parts[i++];
112
113  if(obj)
114    return obj[last];
115},
116
117_isObject: function(obj) {
118  return Object.prototype.toString.call(obj) === "[object Object]";
119},
120
121initialize: function(options) {
122  L.Util.setOptions(this, options || {});
123  this._inputMinSize = this.options.textPlaceholder ? this.options.textPlaceholder.length : 10;
124  this._layer = this.options.layer || new L.LayerGroup();
125  this._filterData = this.options.filterData || this._defaultFilterData;
126  this._formatData = this.options.formatData || this._defaultFormatData;
127  this._moveToLocation = this.options.moveToLocation || this._defaultMoveToLocation;
128  this._autoTypeTmp = this.options.autoType;	//useful for disable autoType temporarily in delete/backspace keydown
129  this._countertips = 0;		//number of tips items
130  this._recordsCache = {};	//key,value table! to store locations! format: key,latlng
131  this._curReq = null;
132},
133
134onAdd: function (map) {
135  this._map = map;
136  this._container = L.DomUtil.create('div', 'leaflet-control-search');
137  this._input = this._createInput(this.options.textPlaceholder, 'search-input');
138  this._tooltip = this._createTooltip('search-tooltip');
139  this._cancel = this._createCancel(this.options.textCancel, 'search-cancel');
140  this._button = this._createButton(this.options.textPlaceholder, 'search-button');
141  this._alert = this._createAlert('search-alert');
142
143  if(this.options.collapsed===false)
144    this.expand(this.options.collapsed);
145
146  if(this.options.marker) {
147    
148    if(this.options.marker instanceof L.Marker || this.options.marker instanceof L.CircleMarker)
149      this._markerSearch = this.options.marker;
150
151    else if(this._isObject(this.options.marker))
152      this._markerSearch = new L.Control.Search.Marker([0,0], this.options.marker);
153
154    this._markerSearch._isMarkerSearch = true;
155  }
156
157  this.setLayer( this._layer );
158
159  map.on({
160    // 		'layeradd': this._onLayerAddRemove,
161    // 		'layerremove': this._onLayerAddRemove
162    'resize': this._handleAutoresize
163    }, this);
164  return this._container;
165},
166addTo: function (map) {
167
168  if(this.options.container) {
169    this._container = this.onAdd(map);
170    this._wrapper = L.DomUtil.get(this.options.container);
171    this._wrapper.style.position = 'relative';
172    this._wrapper.appendChild(this._container);
173  }
174  else
175    L.Control.prototype.addTo.call(this, map);
176
177  return this;
178},
179
180onRemove: function(map) {
181  this._recordsCache = {};
182  // map.off({
183  // 		'layeradd': this._onLayerAddRemove,
184  // 		'layerremove': this._onLayerAddRemove
185  // 	}, this);
186  map.off({
187    // 		'layeradd': this._onLayerAddRemove,
188    // 		'layerremove': this._onLayerAddRemove
189    'resize': this._handleAutoresize
190    }, this);
191},
192
193// _onLayerAddRemove: function(e) {
194// 	//without this, run setLayer also for each Markers!! to optimize!
195// 	if(e.layer instanceof L.LayerGroup)
196// 		if( L.stamp(e.layer) != L.stamp(this._layer) )
197// 			this.setLayer(e.layer);
198// },
199
200setLayer: function(layer) {	//set search layer at runtime
201  //this.options.layer = layer; //setting this, run only this._recordsFromLayer()
202  this._layer = layer;
203  this._layer.addTo(this._map);
204  return this;
205},
206
207showAlert: function(text) {
208  var self = this;
209  text = text || this.options.textErr;
210  this._alert.style.display = 'block';
211  this._alert.innerHTML = text;
212  clearTimeout(this.timerAlert);
213  
214  this.timerAlert = setTimeout(function() {
215    self.hideAlert();
216  },this.options.autoCollapseTime);
217  return this;
218},
219
220hideAlert: function() {
221  this._alert.style.display = 'none';
222  return this;
223},
224  
225cancel: function() {
226  this._input.value = '';
227  this._handleKeypress({ keyCode: 8 });//simulate backspace keypress
228  this._input.size = this._inputMinSize;
229  this._input.focus();
230  this._cancel.style.display = 'none';
231  this._hideTooltip();
232  this.fire('search:cancel');
233  return this;
234},
235
236expand: function(toggle) {
237  toggle = typeof toggle === 'boolean' ? toggle : true;
238  this._input.style.display = 'block';
239  L.DomUtil.addClass(this._container, 'search-exp');
240  if ( toggle !== false ) {
241    this._input.focus();
242    this._map.on('dragstart click', this.collapse, this);
243  }
244  this.fire('search:expanded');
245  return this;	
246},
247
248collapse: function() {
249  this._hideTooltip();
250  this.cancel();
251  this._alert.style.display = 'none';
252  this._input.blur();
253  if(this.options.collapsed)
254  {
255    this._input.style.display = 'none';
256    this._cancel.style.display = 'none';			
257    L.DomUtil.removeClass(this._container, 'search-exp');		
258    if (this.options.hideMarkerOnCollapse) {
259      this._map.removeLayer(this._markerSearch);
260    }
261    this._map.off('dragstart click', this.collapse, this);
262  }
263  this.fire('search:collapsed');
264  return this;
265},
266
267collapseDelayed: function() {	//collapse after delay, used on_input blur
268  var self = this;
269  if (!this.options.autoCollapse) return this;
270  clearTimeout(this.timerCollapse);
271  this.timerCollapse = setTimeout(function() {
272    self.collapse();
273  }, this.options.autoCollapseTime);
274  return this;		
275},
276
277collapseDelayedStop: function() {
278  clearTimeout(this.timerCollapse);
279  return this;		
280},
281
282////start DOM creations
283_createAlert: function(className) {
284  var alert = L.DomUtil.create('div', className, this._container);
285  alert.style.display = 'none';
286
287  L.DomEvent
288    .on(alert, 'click', L.DomEvent.stop, this)
289    .on(alert, 'click', this.hideAlert, this);
290
291  return alert;
292},
293
294_createInput: function (text, className) {
295  var self = this;
296  var label = L.DomUtil.create('label', className, this._container);
297  var input = L.DomUtil.create('input', className, this._container);
298  input.type = 'text';
299  input.size = this._inputMinSize;
300  input.value = '';
301  input.autocomplete = 'off';
302  input.autocorrect = 'off';
303  input.autocapitalize = 'off';
304  input.placeholder = text;
305  input.style.display = 'none';
306  input.role = 'search';
307  input.id = input.role + input.type + input.size;
308  
309  label.htmlFor = input.id;
310  label.style.display = 'none';
311  label.value = text;
312
313  L.DomEvent
314    .disableClickPropagation(input)
315    .on(input, 'keyup', this._handleKeypress, this)
316    .on(input, 'paste', function(e) {
317      setTimeout(function(e) {
318        self._handleKeypress(e);
319      },10,e);
320    }, this)
321    .on(input, 'blur', this.collapseDelayed, this)
322    .on(input, 'focus', this.collapseDelayedStop, this);
323  
324  return input;
325},
326
327_createCancel: function (title, className) {
328  var cancel = L.DomUtil.create('a', className, this._container);
329  cancel.href = '#';
330  cancel.title = title;
331  cancel.style.display = 'none';
332  cancel.innerHTML = "<span>&otimes;</span>";//imageless(see css)
333
334  L.DomEvent
335    .on(cancel, 'click', L.DomEvent.stop, this)
336    .on(cancel, 'click', this.cancel, this);
337
338  return cancel;
339},
340
341_createButton: function (title, className) {
342  var button = L.DomUtil.create('a', className, this._container);
343  button.href = '#';
344  button.title = title;
345
346  L.DomEvent
347    .on(button, 'click', L.DomEvent.stop, this)
348    .on(button, 'click', this._handleSubmit, this)			
349    .on(button, 'focus', this.collapseDelayedStop, this)
350    .on(button, 'blur', this.collapseDelayed, this);
351
352  return button;
353},
354
355_createTooltip: function(className) {
356  var self = this;		
357  var tool = L.DomUtil.create('ul', className, this._container);
358  tool.style.display = 'none';
359  L.DomEvent
360    .disableClickPropagation(tool)
361    .on(tool, 'blur', this.collapseDelayed, this)
362    .on(tool, 'mousewheel', function(e) {
363      self.collapseDelayedStop();
364      L.DomEvent.stopPropagation(e);//disable zoom map
365    }, this)
366    .on(tool, 'mouseover', function(e) {
367      self.collapseDelayedStop();
368    }, this);
369  return tool;
370},
371
372_createTip: function(text, val) {//val is object in recordCache, usually is Latlng
373  var tip;
374  
375  if(this.options.buildTip)
376  {
377    tip = this.options.buildTip.call(this, text, val); //custom tip node or html string
378    if(typeof tip === 'string')
379    {
380      var tmpNode = L.DomUtil.create('div');
381      tmpNode.innerHTML = tip;
382      tip = tmpNode.firstChild;
383    }
384  }
385  else
386  {
387    tip = L.DomUtil.create('li', '');
388    tip.innerHTML = text;
389  }
390  
391  L.DomUtil.addClass(tip, 'search-tip');
392  tip._text = text; //value replaced in this._input and used by _autoType
393
394  if(this.options.tipAutoSubmit)
395    L.DomEvent
396      .disableClickPropagation(tip)		
397      .on(tip, 'click', L.DomEvent.stop, this)
398      .on(tip, 'click', function(e) {
399        this._input.value = text;
400        this._handleAutoresize();
401        this._input.focus();
402        this._hideTooltip();	
403        this._handleSubmit();
404      }, this);
405
406  return tip;
407},
408
409//////end DOM creations
410
411_getUrl: function(text) {
412  return (typeof this.options.url === 'function') ? this.options.url(text) : this.options.url;
413},
414
415_defaultFilterData: function(text, records) {
416
417  var init, icase, regSearch, frecords = {};
418
419  text = text.replace(/[.*+?^${}()|[\]\\]/g, '');
420  //sanitize remove all special characters
421
422  if(text==='') {
423    return [];
424  }
425
426  init = this.options.initial ? '^' : '';
427  icase = !this.options.casesensitive ? 'i' : undefined;
428
429  regSearch = new RegExp(init + text, icase);
430
431  for (let key in records) {
432    if( regSearch.test(key) ) {
433      frecords[key]= records[key];
434    }
435  }
436  
437  return frecords;
438},
439
440showTooltip: function(records) {
441  
442  this._countertips = 0;
443  this._tooltip.innerHTML = '';
444  this._tooltip.currentSelection = -1;  //inizialized for _handleArrowSelect()
445
446  if(this.options.tooltipLimit)
447  {
448    for (let key in records)	//fill tooltip
449    {
450      if(this._countertips === this.options.tooltipLimit) {
451        break;
452      }
453      
454      this._countertips++;
455
456      this._tooltip.appendChild( this._createTip(key, records[key]) );
457    }
458  }
459  
460  if(this._countertips > 0)
461  {
462    this._tooltip.style.display = 'block';
463    
464    if(this._autoTypeTmp) {
465      this._autoType();
466    }
467
468    this._autoTypeTmp = this.options.autoType;//reset default value
469  }
470  else {
471    this._hideTooltip();
472  }
473
474  this._tooltip.scrollTop = 0;
475
476  return this._countertips;
477},
478
479_hideTooltip: function() {
480  this._tooltip.style.display = 'none';
481  this._tooltip.innerHTML = '';
482  return 0;
483},
484
485_defaultFormatData: function(json) {	//default callback for format data to indexed data
486  var self = this,
487    propName = this.options.propertyName,
488    propLoc = this.options.propertyLoc,
489    jsonret = {};
490
491  if( L.Util.isArray(propLoc) ) {
492    for (let i in json) {
493      jsonret[ self._getPath(json[i],propName) ]= L.latLng( self._getPath(json[i], propLoc[0]), self._getPath(json[i], propLoc[1]) );
494    }
495  }
496  else {
497    for (let i in json) {
498      jsonret[ self._getPath(json[i],propName) ]= L.latLng( self._getPath(json[i],propLoc) );
499    }
500  }
501  //TODO throw new Error("propertyName '"+propName+"' not found in JSON data");
502  return jsonret;
503},
504
505_recordsFromJsonp: function(text, callAfter) {  //extract searched records from remote jsonp service
506  L.Control.Search.callJsonp = callAfter;
507  var script = L.DomUtil.create('script','leaflet-search-jsonp', document.getElementsByTagName('body')[0] ),			
508    url = L.Util.template(this._getUrl(text)+'&'+this.options.jsonpParam+'=L.Control.Search.callJsonp', {s: text}); //parsing url
509    //rnd = '&_='+Math.floor(Math.random()*10000);
510    //TODO add rnd param or randomize callback name! in recordsFromJsonp
511  script.type = 'text/javascript';
512  script.src = url;
513  return { abort: function() { script.parentNode.removeChild(script); } };
514},
515
516_recordsFromAjax: function(text, callAfter) {	//Ajax request
517  if (window.XMLHttpRequest === undefined) {
518    window.XMLHttpRequest = function() {
519      try { return new ActiveXObject("Microsoft.XMLHTTP.6.0"); }
520      catch  (e1) {
521        try { return new ActiveXObject("Microsoft.XMLHTTP.3.0"); }
522        catch (e2) { throw new Error("XMLHttpRequest is not supported"); }
523      }
524    };
525  }
526  var IE8or9 = ( L.Browser.ie && !window.atob && document.querySelector ),
527    request = IE8or9 ? new XDomainRequest() : new XMLHttpRequest(),
528    url = L.Util.template(this._getUrl(text), {s: text});
529
530  //rnd = '&_='+Math.floor(Math.random()*10000);
531  //TODO add rnd param or randomize callback name! in recordsFromAjax			
532  
533  request.open("GET", url);
534  
535
536  request.onload = function() {
537    callAfter( JSON.parse(request.responseText) );
538  };
539  request.onreadystatechange = function() {
540      if(request.readyState === 4 && request.status === 200) {
541        this.onload();
542      }
543  };
544
545  request.send();
546  return request;   
547},
548
549_searchInLayer: function(layer, retRecords, propName) {
550  var self = this, loc;
551
552  if(layer instanceof L.Control.Search.Marker) return;
553
554  if(layer instanceof L.Marker || layer instanceof L.CircleMarker)
555  {
556    if(self._getPath(layer.options,propName))
557    {
558      loc = layer.getLatLng();
559      loc.layer = layer;
560      retRecords[ self._getPath(layer.options,propName) ] = loc;
561    }
562    else if(self._getPath(layer.feature.properties,propName))
563    {
564      loc = layer.getLatLng();
565      loc.layer = layer;
566      retRecords[ self._getPath(layer.feature.properties,propName) ] = loc;
567    }
568    else {
569      //throw new Error("propertyName '"+propName+"' not found in marker"); 
570      console.warn("propertyName '"+propName+"' not found in marker"); 
571    }
572  }
573  else if(layer instanceof L.Path || layer instanceof L.Polyline || layer instanceof L.Polygon)
574  {
575    if(self._getPath(layer.options,propName))
576    {
577      loc = layer.getBounds().getCenter();
578      loc.layer = layer;
579      retRecords[ self._getPath(layer.options,propName) ] = loc;
580    }
581    else if(self._getPath(layer.feature.properties,propName))
582    {
583      loc = layer.getBounds().getCenter();
584      loc.layer = layer;
585      retRecords[ self._getPath(layer.feature.properties,propName) ] = loc;
586    }
587    else {
588      //throw new Error("propertyName '"+propName+"' not found in shape"); 
589      console.warn("propertyName '"+propName+"' not found in shape"); 
590    }
591  }
592  else if(layer.hasOwnProperty('feature'))//GeoJSON
593  {
594    if(layer.feature.properties.hasOwnProperty(propName))
595    {
596      if(layer.getLatLng && typeof layer.getLatLng === 'function') {
597        loc = layer.getLatLng();
598        loc.layer = layer;			
599        retRecords[ layer.feature.properties[propName] ] = loc;
600      } else if(layer.getBounds && typeof layer.getBounds === 'function') {
601        loc = layer.getBounds().getCenter();
602        loc.layer = layer;			
603        retRecords[ layer.feature.properties[propName] ] = loc;
604      } else {
605        console.warn("Unknown type of Layer");
606      }
607    }
608    else {
609      //throw new Error("propertyName '"+propName+"' not found in feature");
610      console.warn("propertyName '"+propName+"' not found in feature"); 
611    }
612  }
613  else if(layer instanceof L.LayerGroup)
614  {
615    layer.eachLayer(function (layer) {
616      self._searchInLayer(layer, retRecords, propName);
617    });
618  }
619},
620
621_recordsFromLayer: function() {	//return table: key,value from layer
622  var self = this,
623    retRecords = {},
624    propName = this.options.propertyName;
625  
626  this._layer.eachLayer(function (layer) {
627    self._searchInLayer(layer, retRecords, propName);
628  });
629  
630  return retRecords;
631},
632
633_autoType: function() {
634  
635  //TODO implements autype without selection(useful for mobile device)
636  
637  var start = this._input.value.length,
638    firstRecord = this._tooltip.firstChild ? this._tooltip.firstChild._text : '',
639    end = firstRecord.length;
640
641  if (firstRecord.indexOf(this._input.value) === 0) { // If prefix match
642    this._input.value = firstRecord;
643    this._handleAutoresize();
644
645    if (this._input.createTextRange) {
646      var selRange = this._input.createTextRange();
647      selRange.collapse(true);
648      selRange.moveStart('character', start);
649      selRange.moveEnd('character', end);
650      selRange.select();
651    }
652    else if(this._input.setSelectionRange) {
653      this._input.setSelectionRange(start, end);
654    }
655    else if(this._input.selectionStart) {
656      this._input.selectionStart = start;
657      this._input.selectionEnd = end;
658    }
659  }
660},
661
662_hideAutoType: function() {	// deselect text:
663
664  var sel;
665  if ((sel = this._input.selection) && sel.empty) {
666    sel.empty();
667  }
668  else if (this._input.createTextRange) {
669    sel = this._input.createTextRange();
670    sel.collapse(true);
671    var end = this._input.value.length;
672    sel.moveStart('character', end);
673    sel.moveEnd('character', end);
674    sel.select();
675  }
676  else {
677    if (this._input.getSelection) {
678      this._input.getSelection().removeAllRanges();
679    }
680    this._input.selectionStart = this._input.selectionEnd;
681  }
682},
683
684_handleKeypress: function (e) {	//run _input keyup event
685  var self = this;
686
687  switch(e.keyCode)
688  {
689    case 27://Esc
690      this.collapse();
691    break;
692    case 13://Enter
693      if(this._countertips == 1 || (this.options.firstTipSubmit && this._countertips > 0)) {
694              if(this._tooltip.currentSelection == -1) {
695          this._handleArrowSelect(1);
696              }
697      }
698      this._handleSubmit();	//do search
699    break;
700    case 38://Up
701      this._handleArrowSelect(-1);
702    break;
703    case 40://Down
704      this._handleArrowSelect(1);
705    break;
706    case  8://Backspace
707    case 45://Insert
708    case 46://Delete
709      this._autoTypeTmp = false;//disable temporarily autoType
710    break;
711    case 37://Left
712    case 39://Right
713    case 16://Shift
714    case 17://Ctrl
715    case 35://End
716    case 36://Home
717    break;
718    default://All keys
719      if(this._input.value.length)
720        this._cancel.style.display = 'block';
721      else
722        this._cancel.style.display = 'none';
723
724      if(this._input.value.length >= this.options.minLength)
725      {
726        clearTimeout(this.timerKeypress);	//cancel last search request while type in				
727        this.timerKeypress = setTimeout(function() {	//delay before request, for limit jsonp/ajax request
728
729          self._fillRecordsCache();
730        
731        }, this.options.delayType);
732      }
733      else
734        this._hideTooltip();
735  }
736
737  this._handleAutoresize();
738},
739
740searchText: function(text) {
741  var code = text.charCodeAt(text.length);
742
743  this._input.value = text;
744
745  this._input.style.display = 'block';
746  L.DomUtil.addClass(this._container, 'search-exp');
747
748  this._autoTypeTmp = false;
749
750  this._handleKeypress({keyCode: code});
751},
752
753_fillRecordsCache: function() {
754
755  var self = this,
756    inputText = this._input.value, records;
757
758  if(this._curReq && this._curReq.abort)
759    this._curReq.abort();
760  //abort previous requests
761
762  L.DomUtil.addClass(this._container, 'search-load');	
763
764  if(this.options.layer)
765  {
766    //TODO _recordsFromLayer must return array of objects, formatted from _formatData
767    this._recordsCache = this._recordsFromLayer();
768    
769    records = this._filterData( this._input.value, this._recordsCache );
770
771    this.showTooltip( records );
772
773    L.DomUtil.removeClass(this._container, 'search-load');
774  }
775  else
776  {
777    if(this.options.sourceData)
778      this._retrieveData = this.options.sourceData;
779
780    else if(this.options.url)	//jsonp or ajax
781      this._retrieveData = this.options.jsonpParam ? this._recordsFromJsonp : this._recordsFromAjax;
782
783    this._curReq = this._retrieveData.call(this, inputText, function(data) {
784      
785      self._recordsCache = self._formatData.call(self, data);
786
787      //TODO refact!
788      if(self.options.sourceData)
789        records = self._filterData( self._input.value, self._recordsCache );
790      else
791        records = self._recordsCache;
792
793      self.showTooltip( records );
794
795      L.DomUtil.removeClass(self._container, 'search-load');
796    });
797  }
798},
799
800_handleAutoresize: function() {
801    var maxWidth;
802
803  if (this._input.style.maxWidth !== this._map._container.offsetWidth) {
804    maxWidth = this._map._container.clientWidth;
805
806    // other side margin + padding + width border + width search-button + width search-cancel
807    maxWidth -= 10 + 20 + 1 + 30 + 22; 
808
809    this._input.style.maxWidth = maxWidth.toString() + 'px';
810  }
811
812  if (this.options.autoResize && (this._container.offsetWidth + 20 < this._map._container.offsetWidth)) {
813    this._input.size = this._input.value.length < this._inputMinSize ? this._inputMinSize : this._input.value.length;
814  }
815},
816
817_handleArrowSelect: function(velocity) {
818
819  var searchTips = this._tooltip.hasChildNodes() ? this._tooltip.childNodes : [];
820    
821  for (let i=0; i<searchTips.length; i++) {
822    L.DomUtil.removeClass(searchTips[i], 'search-tip-select');
823  }
824  
825  if ((velocity == 1 ) && (this._tooltip.currentSelection >= (searchTips.length - 1))) {// If at end of list.
826    L.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select');
827  }
828  else if ((velocity == -1 ) && (this._tooltip.currentSelection <= 0)) { // Going back up to the search box.
829    this._tooltip.currentSelection = -1;
830  }
831  else if (this._tooltip.style.display != 'none') {
832    this._tooltip.currentSelection += velocity;
833    
834    L.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select');
835    
836    this._input.value = searchTips[this._tooltip.currentSelection]._text;
837
838    // scroll:
839    var tipOffsetTop = searchTips[this._tooltip.currentSelection].offsetTop;
840    
841    if (tipOffsetTop + searchTips[this._tooltip.currentSelection].clientHeight >= this._tooltip.scrollTop + this._tooltip.clientHeight) {
842      this._tooltip.scrollTop = tipOffsetTop - this._tooltip.clientHeight + searchTips[this._tooltip.currentSelection].clientHeight;
843    }
844    else if (tipOffsetTop <= this._tooltip.scrollTop) {
845      this._tooltip.scrollTop = tipOffsetTop;
846    }
847  }
848},
849
850_handleSubmit: function() {	//button and tooltip click and enter submit
851
852  this._hideAutoType();
853  
854  this.hideAlert();
855  this._hideTooltip();
856
857  if(this._input.style.display == 'none')	//on first click show _input only
858    this.expand();
859  else
860  {
861    if(this._input.value === '')	//hide _input only
862      this.collapse();
863    else
864    {
865      var loc = this._getLocation(this._input.value);
866      
867      if(!loc) {
868        this.showAlert();
869      }
870      else
871      {
872        this.showLocation(loc, this._input.value);
873        this.fire('search:locationfound', {
874            latlng: loc,
875            text: this._input.value,
876            layer: loc.layer ? loc.layer : null
877          });
878      }
879    }
880  }
881},
882
883_getLocation: function(key) {	//extract latlng from _recordsCache
884
885  if( this._recordsCache.hasOwnProperty(key) )
886    return this._recordsCache[key];//then after use .loc attribute
887  else
888    return false;
889},
890
891_defaultMoveToLocation: function(latlng, title, map) {
892  if(this.options.zoom)
893     this._map.setView(latlng, this.options.zoom);
894   else
895    this._map.panTo(latlng);
896},
897
898showLocation: function(latlng, title) {	//set location on map from _recordsCache
899  var self = this;
900
901  self._map.once('moveend zoomend', function(e) {
902
903    if(self._markerSearch) {
904      self._markerSearch.addTo(self._map).setLatLng(latlng);
905    }
906    
907  });
908
909  self._moveToLocation(latlng, title, self._map);
910  //FIXME autoCollapse option hide self._markerSearch before visualized!!
911  if(self.options.autoCollapse)
912    self.collapse();
913
914  return self;
915}
916});
917
918L.Control.Search.Marker = L.Marker.extend({
919
920includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,
921
922options: {
923  icon: new L.Icon.Default(),
924  animate: true,
925  circle: {
926    radius: 10,
927    weight: 3,
928    color: '#e03',
929    stroke: true,
930    fill: false
931  }
932},
933
934initialize: function (latlng, options) {
935  L.setOptions(this, options);
936
937  if(options.icon === true)
938    options.icon = new L.Icon.Default();
939
940  L.Marker.prototype.initialize.call(this, latlng, options);
941  
942  if( L.Control.Search.prototype._isObject(this.options.circle) )
943    this._circleLoc = new L.CircleMarker(latlng, this.options.circle);
944},
945
946onAdd: function (map) {
947  L.Marker.prototype.onAdd.call(this, map);
948  if(this._circleLoc) {
949    map.addLayer(this._circleLoc);
950    if(this.options.animate)
951      this.animate();
952  }
953},
954
955onRemove: function (map) {
956  L.Marker.prototype.onRemove.call(this, map);
957  if(this._circleLoc)
958    map.removeLayer(this._circleLoc);
959},
960
961setLatLng: function (latlng) {
962  L.Marker.prototype.setLatLng.call(this, latlng);
963  if(this._circleLoc)
964    this._circleLoc.setLatLng(latlng);
965  return this;
966},
967
968_initIcon: function () {
969  if(this.options.icon)
970    L.Marker.prototype._initIcon.call(this);
971},
972
973_removeIcon: function () {
974  if(this.options.icon)
975    L.Marker.prototype._removeIcon.call(this);
976},
977
978animate: function() {
979//TODO refact animate() more smooth! like this: http://goo.gl/DDlRs
980  if(this._circleLoc)
981  {
982    var circle = this._circleLoc,
983      tInt = 200,	//time interval
984      ss = 5,	//frames
985      mr = parseInt(circle._radius/ss),
986      oldrad = this.options.circle.radius,
987      newrad = circle._radius * 2,
988      acc = 0;
989
990    circle._timerAnimLoc = setInterval(function() {
991      acc += 0.5;
992      mr += acc;	//adding acceleration
993      newrad -= mr;
994      
995      circle.setRadius(newrad);
996
997      if(newrad<oldrad)
998      {
999        clearInterval(circle._timerAnimLoc);
1000        circle.setRadius(oldrad);//reset radius
1001        //if(typeof afterAnimCall == 'function')
1002          //afterAnimCall();
1003          //TODO use create event 'animateEnd' in L.Control.Search.Marker 
1004      }
1005    }, tInt);
1006  }
1007  
1008  return this;
1009}
1010});
1011
1012L.Map.addInitHook(function () {
1013  if (this.options.searchControl) {
1014      this.searchControl = L.control.search(this.options.searchControl);
1015      this.addControl(this.searchControl);
1016  }
1017});
1018
1019L.control.search = function (options) {
1020  return new L.Control.Search(options);
1021};
1022
1023return L.Control.Search;
1024
1025});

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.