PageSourceSearch

https://cdn26585-fc.alshuruqstore.com/wp-content/site26585/plugins…/libs/leaflet/leaflet-search.js?ver=6.7.16

js alshuruqstore.com collected 2026-09-24 14:39:09 UTC 30,770 bytes, 1,016 lines download raw bytes

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	
56	includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,
57
58	options: {
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 
82if 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
121	initialize: 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
134	onAdd: 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	},
166	addTo: 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
180	onRemove: 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
200	setLayer: 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	
207	showAlert: 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	
220	hideAlert: function() {
221		this._alert.style.display = 'none';
222		return this;
223	},
224		
225	cancel: 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	
236	expand: 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
248	collapse: 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	
267	collapseDelayed: 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
277	collapseDelayedStop: 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 I, icase, regSearch, frecords = {};
418
419		text = text.replace(/[.*+?^${}()|[\]\\]/g, '');  //sanitize remove all special characters
420		if(text==='')
421			return [];
422
423		I = this.options.initial ? '^' : '';  //search only initial text
424		icase = !this.options.casesensitive ? 'i' : undefined;
425
426		regSearch = new RegExp(I + text, icase);
427
428		//TODO use .filter or .map
429		for(var key in records) {
430			if( regSearch.test(key) )
431				frecords[key]= records[key];
432		}
433		
434		return frecords;
435	},
436
437	showTooltip: function(records) {
438		
439
440		this._countertips = 0;
441		this._tooltip.innerHTML = '';
442		this._tooltip.currentSelection = -1;  //inizialized for _handleArrowSelect()
443
444		if(this.options.tooltipLimit)
445		{
446			for(var key in records)//fill tooltip
447			{
448				if(this._countertips === this.options.tooltipLimit)
449					break;
450				
451				this._countertips++;
452
453				this._tooltip.appendChild( this._createTip(key, records[key]) );
454			}
455		}
456		
457		if(this._countertips > 0)
458		{
459			this._tooltip.style.display = 'block';
460			
461			if(this._autoTypeTmp)
462				this._autoType();
463
464			this._autoTypeTmp = this.options.autoType;//reset default value
465		}
466		else
467			this._hideTooltip();
468
469		this._tooltip.scrollTop = 0;
470
471		return this._countertips;
472	},
473
474	_hideTooltip: function() {
475		this._tooltip.style.display = 'none';
476		this._tooltip.innerHTML = '';
477		return 0;
478	},
479
480	_defaultFormatData: function(json) {	//default callback for format data to indexed data
481		var self = this,
482			propName = this.options.propertyName,
483			propLoc = this.options.propertyLoc,
484			i, jsonret = {};
485
486		if( L.Util.isArray(propLoc) )
487			for(i in json)
488				jsonret[ self._getPath(json[i],propName) ]= L.latLng( json[i][ propLoc[0] ], json[i][ propLoc[1] ] );
489		else
490			for(i in json)
491				jsonret[ self._getPath(json[i],propName) ]= L.latLng( self._getPath(json[i],propLoc) );
492		//TODO throw new Error("propertyName '"+propName+"' not found in JSON data");
493		return jsonret;
494	},
495
496	_recordsFromJsonp: function(text, callAfter) {  //extract searched records from remote jsonp service
497		L.Control.Search.callJsonp = callAfter;
498		var script = L.DomUtil.create('script','leaflet-search-jsonp', document.getElementsByTagName('body')[0] ),			
499			url = L.Util.template(this._getUrl(text)+'&'+this.options.jsonpParam+'=L.Control.Search.callJsonp', {s: text}); //parsing url
500			//rnd = '&_='+Math.floor(Math.random()*10000);
501			//TODO add rnd param or randomize callback name! in recordsFromJsonp
502		script.type = 'text/javascript';
503		script.src = url;
504		return { abort: function() { script.parentNode.removeChild(script); } };
505	},
506
507	_recordsFromAjax: function(text, callAfter) {	//Ajax request
508		if (window.XMLHttpRequest === undefined) {
509			window.XMLHttpRequest = function() {
510				try { return new ActiveXObject("Microsoft.XMLHTTP.6.0"); }
511				catch  (e1) {
512					try { return new ActiveXObject("Microsoft.XMLHTTP.3.0"); }
513					catch (e2) { throw new Error("XMLHttpRequest is not supported"); }
514				}
515			};
516		}
517		var IE8or9 = ( L.Browser.ie && !window.atob && document.querySelector ),
518			request = IE8or9 ? new XDomainRequest() : new XMLHttpRequest(),
519			url = L.Util.template(this._getUrl(text), {s: text});
520
521		//rnd = '&_='+Math.floor(Math.random()*10000);
522		//TODO add rnd param or randomize callback name! in recordsFromAjax			
523		
524		request.open("GET", url);
525		
526
527		request.onload = function() {
528			callAfter( JSON.parse(request.responseText) );
529		};
530		request.onreadystatechange = function() {
531		    if(request.readyState === 4 && request.status === 200) {
532		    	this.onload();
533		    }
534		};
535
536		request.send();
537		return request;   
538	},
539
540  _searchInLayer: function(layer, retRecords, propName) {
541    var self = this, loc;
542
543    if(layer instanceof L.Control.Search.Marker) return;
544
545    if(layer instanceof L.Marker || layer instanceof L.CircleMarker)
546    {
547      if(self._getPath(layer.options,propName))
548      {
549        loc = layer.getLatLng();
550        loc.layer = layer;
551        retRecords[ self._getPath(layer.options,propName) ] = loc;
552      }
553      else if(self._getPath(layer.feature.properties,propName))
554      {
555        loc = layer.getLatLng();
556        loc.layer = layer;
557        retRecords[ self._getPath(layer.feature.properties,propName) ] = loc;
558      }
559      else {
560        //throw new Error("propertyName '"+propName+"' not found in marker"); 
561        console.warn("propertyName '"+propName+"' not found in marker"); 
562      }
563    }
564    else if(layer instanceof L.Path || layer instanceof L.Polyline || layer instanceof L.Polygon)
565    {
566      if(self._getPath(layer.options,propName))
567      {
568        loc = layer.getBounds().getCenter();
569        loc.layer = layer;
570        retRecords[ self._getPath(layer.options,propName) ] = loc;
571      }
572      else if(self._getPath(layer.feature.properties,propName))
573      {
574        loc = layer.getBounds().getCenter();
575        loc.layer = layer;
576        retRecords[ self._getPath(layer.feature.properties,propName) ] = loc;
577      }
578      else {
579        //throw new Error("propertyName '"+propName+"' not found in shape"); 
580        console.warn("propertyName '"+propName+"' not found in shape"); 
581      }
582    }
583    else if(layer.hasOwnProperty('feature'))//GeoJSON
584    {
585      if(layer.feature.properties.hasOwnProperty(propName))
586      {
587        if(layer.getLatLng && typeof layer.getLatLng === 'function') {
588          loc = layer.getLatLng();
589          loc.layer = layer;			
590          retRecords[ layer.feature.properties[propName] ] = loc;
591        } else if(layer.getBounds && typeof layer.getBounds === 'function') {
592          loc = layer.getBounds().getCenter();
593          loc.layer = layer;			
594          retRecords[ layer.feature.properties[propName] ] = loc;
595        } else {
596          console.warn("Unknown type of Layer");
597        }
598      }
599      else {
600        //throw new Error("propertyName '"+propName+"' not found in feature");
601        console.warn("propertyName '"+propName+"' not found in feature"); 
602      }
603    }
604    else if(layer instanceof L.LayerGroup)
605    {
606      layer.eachLayer(function (layer) {
607        self._searchInLayer(layer, retRecords, propName);
608      });
609    }
610  },
611	
612	_recordsFromLayer: function() {	//return table: key,value from layer
613		var self = this,
614			retRecords = {},
615			propName = this.options.propertyName;
616		
617		this._layer.eachLayer(function (layer) {
618			self._searchInLayer(layer, retRecords, propName);
619		});
620		
621		return retRecords;
622	},
623	
624	_autoType: function() {
625		
626		//TODO implements autype without selection(useful for mobile device)
627		
628		var start = this._input.value.length,
629			firstRecord = this._tooltip.firstChild ? this._tooltip.firstChild._text : '',
630			end = firstRecord.length;
631
632		if (firstRecord.indexOf(this._input.value) === 0) { // If prefix match
633			this._input.value = firstRecord;
634			this._handleAutoresize();
635
636			if (this._input.createTextRange) {
637				var selRange = this._input.createTextRange();
638				selRange.collapse(true);
639				selRange.moveStart('character', start);
640				selRange.moveEnd('character', end);
641				selRange.select();
642			}
643			else if(this._input.setSelectionRange) {
644				this._input.setSelectionRange(start, end);
645			}
646			else if(this._input.selectionStart) {
647				this._input.selectionStart = start;
648				this._input.selectionEnd = end;
649			}
650		}
651	},
652
653	_hideAutoType: function() {	// deselect text:
654
655		var sel;
656		if ((sel = this._input.selection) && sel.empty) {
657			sel.empty();
658		}
659		else if (this._input.createTextRange) {
660			sel = this._input.createTextRange();
661			sel.collapse(true);
662			var end = this._input.value.length;
663			sel.moveStart('character', end);
664			sel.moveEnd('character', end);
665			sel.select();
666		}
667		else {
668			if (this._input.getSelection) {
669				this._input.getSelection().removeAllRanges();
670			}
671			this._input.selectionStart = this._input.selectionEnd;
672		}
673	},
674	
675	_handleKeypress: function (e) {	//run _input keyup event
676		var self = this;
677
678		switch(e.keyCode)
679		{
680			case 27://Esc
681				this.collapse();
682			break;
683			case 13://Enter
684				if(this._countertips == 1 || (this.options.firstTipSubmit && this._countertips > 0)) {
685          			if(this._tooltip.currentSelection == -1) {
686						this._handleArrowSelect(1);
687          			}
688				}
689				this._handleSubmit();	//do search
690			break;
691			case 38://Up
692				this._handleArrowSelect(-1);
693			break;
694			case 40://Down
695				this._handleArrowSelect(1);
696			break;
697			case  8://Backspace
698			case 45://Insert
699			case 46://Delete
700				this._autoTypeTmp = false;//disable temporarily autoType
701			break;
702			case 37://Left
703			case 39://Right
704			case 16://Shift
705			case 17://Ctrl
706			case 35://End
707			case 36://Home
708			break;
709			default://All keys
710				if(this._input.value.length)
711					this._cancel.style.display = 'block';
712				else
713					this._cancel.style.display = 'none';
714
715				if(this._input.value.length >= this.options.minLength)
716				{
717					clearTimeout(this.timerKeypress);	//cancel last search request while type in				
718					this.timerKeypress = setTimeout(function() {	//delay before request, for limit jsonp/ajax request
719
720						self._fillRecordsCache();
721					
722					}, this.options.delayType);
723				}
724				else
725					this._hideTooltip();
726		}
727
728		this._handleAutoresize();
729	},
730
731	searchText: function(text) {
732		var code = text.charCodeAt(text.length);
733
734		this._input.value = text;
735
736		this._input.style.display = 'block';
737		L.DomUtil.addClass(this._container, 'search-exp');
738
739		this._autoTypeTmp = false;
740
741		this._handleKeypress({keyCode: code});
742	},
743	
744	_fillRecordsCache: function() {
745
746		var self = this,
747			inputText = this._input.value, records;
748
749		if(this._curReq && this._curReq.abort)
750			this._curReq.abort();
751		//abort previous requests
752
753		L.DomUtil.addClass(this._container, 'search-load');	
754
755		if(this.options.layer)
756		{
757			//TODO _recordsFromLayer must return array of objects, formatted from _formatData
758			this._recordsCache = this._recordsFromLayer();
759			
760			records = this._filterData( this._input.value, this._recordsCache );
761
762			this.showTooltip( records );
763
764			L.DomUtil.removeClass(this._container, 'search-load');
765		}
766		else
767		{
768			if(this.options.sourceData)
769				this._retrieveData = this.options.sourceData;
770
771			else if(this.options.url)	//jsonp or ajax
772				this._retrieveData = this.options.jsonpParam ? this._recordsFromJsonp : this._recordsFromAjax;
773
774			this._curReq = this._retrieveData.call(this, inputText, function(data) {
775				
776				self._recordsCache = self._formatData.call(self, data);
777
778				//TODO refact!
779				if(self.options.sourceData)
780					records = self._filterData( self._input.value, self._recordsCache );
781				else
782					records = self._recordsCache;
783
784				self.showTooltip( records );
785 
786				L.DomUtil.removeClass(self._container, 'search-load');
787			});
788		}
789	},
790	
791	_handleAutoresize: function() {
792	    var maxWidth;
793
794		if (this._input.style.maxWidth !== this._map._container.offsetWidth) {
795			maxWidth = this._map._container.clientWidth;
796
797			// other side margin + padding + width border + width search-button + width search-cancel
798			maxWidth -= 10 + 20 + 1 + 30 + 22; 
799
800			this._input.style.maxWidth = maxWidth.toString() + 'px';
801		}
802
803		if (this.options.autoResize && (this._container.offsetWidth + 20 < this._map._container.offsetWidth)) {
804			this._input.size = this._input.value.length < this._inputMinSize ? this._inputMinSize : this._input.value.length;
805		}
806	},
807
808	_handleArrowSelect: function(velocity) {
809	
810		var searchTips = this._tooltip.hasChildNodes() ? this._tooltip.childNodes : [];
811			
812		for (i=0; i<searchTips.length; i++)
813			L.DomUtil.removeClass(searchTips[i], 'search-tip-select');
814		
815		if ((velocity == 1 ) && (this._tooltip.currentSelection >= (searchTips.length - 1))) {// If at end of list.
816			L.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select');
817		}
818		else if ((velocity == -1 ) && (this._tooltip.currentSelection <= 0)) { // Going back up to the search box.
819			this._tooltip.currentSelection = -1;
820		}
821		else if (this._tooltip.style.display != 'none') {
822			this._tooltip.currentSelection += velocity;
823			
824			L.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select');
825			
826			this._input.value = searchTips[this._tooltip.currentSelection]._text;
827
828			// scroll:
829			var tipOffsetTop = searchTips[this._tooltip.currentSelection].offsetTop;
830			
831			if (tipOffsetTop + searchTips[this._tooltip.currentSelection].clientHeight >= this._tooltip.scrollTop + this._tooltip.clientHeight) {
832				this._tooltip.scrollTop = tipOffsetTop - this._tooltip.clientHeight + searchTips[this._tooltip.currentSelection].clientHeight;
833			}
834			else if (tipOffsetTop <= this._tooltip.scrollTop) {
835				this._tooltip.scrollTop = tipOffsetTop;
836			}
837		}
838	},
839
840	_handleSubmit: function() {	//button and tooltip click and enter submit
841
842		this._hideAutoType();
843		
844		this.hideAlert();
845		this._hideTooltip();
846
847		if(this._input.style.display == 'none')	//on first click show _input only
848			this.expand();
849		else
850		{
851			if(this._input.value === '')	//hide _input only
852				this.collapse();
853			else
854			{
855				var loc = this._getLocation(this._input.value);
856				
857				if(loc===false)
858					this.showAlert();
859				else
860				{
861					this.showLocation(loc, this._input.value);
862					this.fire('search:locationfound', {
863							latlng: loc,
864							text: this._input.value,
865							layer: loc.layer ? loc.layer : null
866						});
867				}
868			}
869		}
870	},
871
872	_getLocation: function(key) {	//extract latlng from _recordsCache
873
874		if( this._recordsCache.hasOwnProperty(key) )
875			return this._recordsCache[key];//then after use .loc attribute
876		else
877			return false;
878	},
879
880	_defaultMoveToLocation: function(latlng, title, map) {
881		if(this.options.zoom)
882 			this._map.setView(latlng, this.options.zoom);
883 		else
884			this._map.panTo(latlng);
885	},
886
887	showLocation: function(latlng, title) {	//set location on map from _recordsCache
888		var self = this;
889
890		self._map.once('moveend zoomend', function(e) {
891
892			if(self._markerSearch) {
893				self._markerSearch.addTo(self._map).setLatLng(latlng);
894			}
895			
896		});
897
898		self._moveToLocation(latlng, title, self._map);
899		//FIXME autoCollapse option hide self._markerSearch before visualized!!
900		if(self.options.autoCollapse)
901			self.collapse();
902
903		return self;
904	}
905});
906
907L.Control.Search.Marker = L.Marker.extend({
908
909	includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,
910	
911	options: {
912		icon: new L.Icon.Default(),
913		animate: true,
914		circle: {
915			radius: 10,
916			weight: 3,
917			color: '#e03',
918			stroke: true,
919			fill: false
920		}
921	},
922	
923	initialize: function (latlng, options) {
924		L.setOptions(this, options);
925
926		if(options.icon === true)
927			options.icon = new L.Icon.Default();
928
929		L.Marker.prototype.initialize.call(this, latlng, options);
930		
931		if( L.Control.Search.prototype._isObject(this.options.circle) )
932			this._circleLoc = new L.CircleMarker(latlng, this.options.circle);
933	},
934
935	onAdd: function (map) {
936		L.Marker.prototype.onAdd.call(this, map);
937		if(this._circleLoc) {
938			map.addLayer(this._circleLoc);
939			if(this.options.animate)
940				this.animate();
941		}
942	},
943
944	onRemove: function (map) {
945		L.Marker.prototype.onRemove.call(this, map);
946		if(this._circleLoc)
947			map.removeLayer(this._circleLoc);
948	},
949	
950	setLatLng: function (latlng) {
951		L.Marker.prototype.setLatLng.call(this, latlng);
952		if(this._circleLoc)
953			this._circleLoc.setLatLng(latlng);
954		return this;
955	},
956	
957	_initIcon: function () {
958		if(this.options.icon)
959			L.Marker.prototype._initIcon.call(this);
960	},
961
962	_removeIcon: function () {
963		if(this.options.icon)
964			L.Marker.prototype._removeIcon.call(this);
965	},
966
967	animate: function() {
968	//TODO refact animate() more smooth! like this: http://goo.gl/DDlRs
969		if(this._circleLoc)
970		{
971			var circle = this._circleLoc,
972				tInt = 200,	//time interval
973				ss = 5,	//frames
974				mr = parseInt(circle._radius/ss),
975				oldrad = this.options.circle.radius,
976				newrad = circle._radius * 2,
977				acc = 0;
978
979			circle._timerAnimLoc = setInterval(function() {
980				acc += 0.5;
981				mr += acc;	//adding acceleration
982				newrad -= mr;
983				
984				circle.setRadius(newrad);
985
986				if(newrad<oldrad)
987				{
988					clearInterval(circle._timerAnimLoc);
989					circle.setRadius(oldrad);//reset radius
990					//if(typeof afterAnimCall == 'function')
991						//afterAnimCall();
992						//TODO use create event 'animateEnd' in L.Control.Search.Marker 
993				}
994			}, tInt);
995		}
996		
997		return this;
998	}
999});
1000
1001L.Map.addInitHook(function () {
1002    if (this.options.searchControl) {
1003        this.searchControl = L.control.search(this.options.searchControl);
1004        this.addControl(this.searchControl);
1005    }
1006});
1007
1008L.control.search = function (options) {
1009    return new L.Control.Search(options);
1010};
1011
1012return L.Control.Search;
1013
1014});
1015
1016

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.