PageSourceSearch

https://www.rbauto.ru/bitrix/components/bitrix/main.lookup.input/script.js

js rbauto.ru collected 2026-10-02 17:22:53 UTC 32,139 bytes, 1,277 lines download raw bytes

1/************ Main control object class ****************/
2function JCMainLookupSelector(arParams)
3{
4	var _this = this;
5
6	this.timerId = null;
7	this._currentSearchStr = '';
8
9	this.LAYOUT = null;
10	this.VALUE_CONTAINER = null;
11
12	this.VISUAL = null;
13	this.SEARCH = null;
14
15	this.__search_current_row = null;
16
17	arParams.VISUAL.CONTROL_ID = arParams.CONTROL_ID;
18
19	this.arParams = {
20		'AJAX_PAGE': arParams.AJAX_PAGE,
21		'CONTROL_ID': arParams.CONTROL_ID,
22		'LAYOUT_ID': arParams.LAYOUT_ID,
23		'INPUT_NAME': arParams.INPUT_NAME,
24		'VISUAL': arParams.VISUAL
25	};
26
27	this.arParams.PROACTIVE = 'NONE';
28	if (!!arParams.PROACTIVE)
29		this.arParams.PROACTIVE = arParams.PROACTIVE;
30	this.arParams.PROACTIVE = this.arParams.PROACTIVE.toUpperCase();
31
32	if (!!arParams.AJAX_PARAMS)
33	{
34		this.arParams.AJAX_PARAMS = arParams.AJAX_PARAMS;
35	}
36
37	if (!!arParams.VALUE)
38	{
39		//this.arParams.VISUAL.START_TEXT = '';
40		this.arParams.VALUE = arParams.VALUE;
41	}
42
43	if (!!arParams.INPUT_NAME_SUSPICIOUS)
44	{
45		this.INPUT_SUSPICIOUS = null;
46		this.arParams.INPUT_NAME_SUSPICIOUS = arParams.INPUT_NAME_SUSPICIOUS;
47	}
48
49	this.processSearchStr = function()
50	{
51		var url = _this.arParams.AJAX_PAGE+'?MODE=SEARCH';
52
53		url += '&search=' + encodeURIComponent(_this._currentSearchStr);
54		if (_this.arParams.AJAX_PARAMS)
55		{
56			for(var param_name in _this.arParams.AJAX_PARAMS)
57				url += '&' + param_name + '=' + encodeURIComponent(_this.arParams.AJAX_PARAMS[param_name]);
58		}
59		BX.ajax.get(url, _this.ShowSearchResults);
60	};
61
62	this.ShowSearchResults = function(data)
63	{
64		if (null != _this.__search_current_row)
65			_this.__search_current_row = null;
66
67		if (null != _this.SEARCH)
68			_this.SEARCH.innerHTML = '';
69
70		var DATA = [];
71
72		if (BX.type.isNotEmptyString(data))
73		{
74			var data_test = BX.parseJSON(data);
75			if (data_test)
76			{
77				eval('DATA = ' + data);
78			}
79			else
80			{
81				if ("''" != data)
82				{
83					if ('MESSAGE' == _this.arParams.PROACTIVE || 'BXWINDOW' == _this.arParams.PROACTIVE)
84					{
85						if ('MESSAGE' == _this.arParams.PROACTIVE)
86						{
87							alert(data);	// this alert show proactive message
88						}
89						else
90						{
91							var obDialog = new BX.CDialog({
92							'content': data,
93							'draggable': true,
94							'resizable': false,
95							'buttons': [BX.CDialog.btnClose]
96						});
97							obDialog.Show();
98						}
99
100						_this.VISUAL.TEXT.value = _this.VISUAL.TEXT.value.replace(_this._currentSearchStr,'');
101						_this._currentSearchStr = '';
102					}
103				}
104			}
105		}
106
107		if (DATA.length > 0)
108		{
109			if (DATA.length == 1 && null != DATA[0].READY)
110			{
111				_this.VISUAL.SetTokenData(_this._currentSearchStr, DATA[0], false);
112				return;
113			}
114
115			if (null == _this.SEARCH)
116			{
117				if (!!_this.arParams.VISUAL.SEARCH_POSITION && 'absolute' == _this.arParams.VISUAL.SEARCH_POSITION)
118				{
119					_this.SEARCH = BX.GetDocElement().appendChild(document.createElement('DIV'));
120					_this.SEARCH.className = 'mli-search-results';
121					_this.SEARCH.style.position = 'absolute';
122					if (!!_this.arParams.VISUAL.SEARCH_ZINDEX)
123					{
124						_this.arParams.VISUAL.SEARCH_ZINDEX = parseInt(_this.arParams.VISUAL.SEARCH_ZINDEX);
125						if (!isNaN(_this.arParams.VISUAL.SEARCH_ZINDEX) && 0 < _this.arParams.VISUAL.SEARCH_ZINDEX)
126						{
127							_this.SEARCH.style.zIndex = _this.arParams.VISUAL.SEARCH_ZINDEX;
128						}
129					}
130				}
131				else
132				{
133					_this.SEARCH = _this.LAYOUT.appendChild(document.createElement('DIV'));
134					_this.SEARCH.className = 'mli-search-results';
135					_this.SEARCH.style.position = 'absolute';
136				}
137			}
138
139			var pos;
140			if (!!_this.arParams.VISUAL.SEARCH_POSITION && 'absolute' == _this.arParams.VISUAL.SEARCH_POSITION)
141			{
142				pos = BX.pos(_this.VISUAL.TEXT, false);
143			}
144			else
145			{
146				pos = BX.pos(_this.VISUAL.TEXT, true);
147			}
148			_this.SEARCH.style.top = pos.bottom + 'px';
149			_this.SEARCH.style.left = pos.left + 'px';
150			_this.SEARCH.style.width = (pos.right - pos.left - 2) + 'px';
151
152			for (var i = 0; i < DATA.length; i++)
153			{
154				var obSearchResult = _this.SEARCH.appendChild(document.createElement('DIV'));
155				obSearchResult.className = 'mli-search-result';
156				obSearchResult.appendChild(document.createTextNode(DATA[i].NAME + ' [' + DATA[i].ID + ']'));
157
158				obSearchResult.BX_ROW_DATA = DATA[i];
159				obSearchResult.onclick = _this.__search_result_click;
160				obSearchResult.onmouseover = _this.__search_result_over;
161				BX.bind(obSearchResult, "mousedown", function(event) {
162					event.stopPropagation();
163				});
164			}
165
166			_this.SEARCH.style.display = 'block';
167		}
168		else
169		{
170			_this.__hideSearch();
171		}
172	};
173
174	this.__search_result_click = function()
175	{
176		_this.VISUAL.SetTokenData(_this._currentSearchStr, this.BX_ROW_DATA);
177		_this.__hideSearch();
178	};
179
180	this.__search_result_over = function()
181	{
182		if (null != _this.__search_current_row)
183			_this.__search_current_row.className = 'mli-search-result';
184
185		_this.__search_current_row = this;
186		this.className = 'mli-search-result mli-search-current';
187	};
188
189	jsUtils.addCustomEvent('onEmpUserSelectorChangeTokenActivity', this.SetTokenInput, null, this);
190	BX.addCustomEvent('onGridClearFilter', BX.delegate(this.ClearValues, this));
191	BX.ready(function() {_this.Init(); });
192}
193
194JCMainLookupSelector.prototype.Init = function()
195{
196	if (!!this.bInit) return;
197	this.bInit = true;
198
199	var _this = this;
200
201	this.LAYOUT = document.getElementById(this.arParams.LAYOUT_ID);
202	this.VISUAL = new JCMainLookupSelectorText(this.arParams.VISUAL);
203	this.VISUAL.parent = this;
204
205	this.VISUAL.onCurrentStringExists = function(str)
206	{
207		if (_this._currentSearchStr == str)
208		{
209			if (null != _this.SEARCH && _this.SEARCH.innerHTML.length > 0)
210				_this.SEARCH.style.display = 'block';
211		}
212		else
213		{
214			if (null != _this.timerId)
215				clearTimeout(_this.timerId);
216
217			_this._currentSearchStr = str;
218			// timeout moved to keys processing inside the textarea
219			//_this.timerId = setTimeout(_this.processSearchStr, 100);
220			_this.processSearchStr();
221		}
222	};
223
224	this.VISUAL.onCurrentStringChange = function()
225	{
226		if (null != _this.timerId)
227			clearTimeout(_this.timerId);
228
229		_this.__hideSearch();
230	};
231
232	this.VISUAL.onUnidentifiedTokenFound = function(str)
233	{
234		var url = _this.arParams.AJAX_PAGE+'?MODE=SEARCH';
235
236		url += '&search=' + encodeURIComponent(str);
237		if (_this.arParams.AJAX_PARAMS)
238		{
239			for(var param_name in _this.arParams.AJAX_PARAMS)
240				url += '&' + param_name + '=' + encodeURIComponent(_this.arParams.AJAX_PARAMS[param_name]);
241		}
242		BX.ajax.get(url, function(data) {
243			if (data.length <= 0)
244				return;
245
246			var DATA = [];
247			eval('DATA = ' + data);
248			if (DATA.length == 1 && DATA[0].READY == 'Y')
249				_this.VISUAL.SetTokenData(str, DATA[0], false);
250		});
251	};
252
253	this.VISUAL.onControlKeyPressed = function(keyCode)
254	{
255		if (null != _this.SEARCH && _this.SEARCH.style.display == 'block')
256		{
257			switch (keyCode)
258			{
259				case 27: // escape key - close search div
260					_this.SEARCH.style.display = 'none';
261					return false;
262				break;
263
264				case 40: // down key - navigate down on search results
265					if (null == _this.__search_current_row)
266						_this.SEARCH.firstChild.onmouseover();
267					else if (null != _this.__search_current_row.nextSibling)
268						_this.__search_current_row.nextSibling.onmouseover();
269					return false;
270				break;
271
272				case 38: // up key - navigate up on search results
273					if (null == _this.__search_current_row)
274						_this.SEARCH.lastChild.onmouseover();
275					else if (null != _this.__search_current_row.previousSibling)
276						_this.__search_current_row.previousSibling.onmouseover();
277					return false;
278				break;
279
280				case 13: // enter key - choose current search result
281					if (null != _this.__search_current_row)
282						_this.__search_current_row.onclick();
283					return false;
284				break;
285			}
286		}
287
288		return true;
289	};
290
291	if (null != this.arParams.INPUT_NAME_SUSPICIOUS)
292	{
293		this.VISUAL.onSuspiciousTokensFound = function(arWords)
294		{
295			if (null == _this.INPUT_SUSPICIOUS)
296			{
297				_this.INPUT_SUSPICIOUS = document.createElement('INPUT');
298				_this.INPUT_SUSPICIOUS.type = 'hidden';
299				_this.INPUT_SUSPICIOUS.name = _this.arParams.INPUT_NAME_SUSPICIOUS;
300
301				_this.VALUE_CONTAINER.appendChild(_this.INPUT_SUSPICIOUS);
302			}
303
304			_this.INPUT_SUSPICIOUS.value = arWords.join(';');
305		};
306	}
307
308	this.__hideSearch = function() {if (null != _this.SEARCH) _this.SEARCH.style.display = 'none';};
309	this.__delayedHideSearch = function() {if (null != _this.SEARCH) setTimeout(_this.__hideSearch, 500);};
310
311	jsUtils.addEvent(this.VISUAL.TEXT, 'blur', this.__delayedHideSearch);
312
313	if (BX('value_container_' + this.arParams['CONTROL_ID']))
314		this.VALUE_CONTAINER = BX('value_container_' + this.arParams['CONTROL_ID']);
315	else
316		this.VALUE_CONTAINER = this.LAYOUT.appendChild(document.createElement('DIV'));
317		
318	this.VALUE_CONTAINER.style.display = 'none';
319
320	if (null != this.arParams.VALUE)
321		this.SetValue(this.arParams.VALUE);
322};
323
324// object destructor
325JCMainLookupSelector.prototype.Clear = function()
326{
327	// remove global event handler
328	jsUtils.removeCustomEvent('onEmpUserSelectorChangeTokenActivity', this.SetTokenInput);
329
330	// clear textarea object internal event handlers
331	this.VISUAL.onCurrentStringChange = null;
332	this.VISUAL.onCurrentStringExists = null;
333	this.VISUAL.onCurrentTokenExists = null;
334	this.VISUAL.onUnidentifiedTokenFound = null;
335	this.VISUAL.onControlKeyPressed = null;
336	this.VISUAL.onSuspiciousTokensFound = null;
337
338	jsUtils.removeEvent(this.VISUAL.TEXT, 'blur', this.__delayedHideSearch);
339
340	// reset and kill textarea processing object
341	this.VISUAL.Reset(false, true);
342	this.VISUAL = null;
343
344	// collect garbage
345	if (null != this.timerId)
346		clearTimeout(this.timerId);
347
348	if (null != this.SEARCH)
349	{
350		this.SEARCH.parentNode.removeChild(this.SEARCH);
351		this.SEARCH = null;
352	}
353
354	this._currentSearchStr = '';
355	BX.cleanNode(this.LAYOUT, true);
356};
357
358JCMainLookupSelector.prototype.SetTokenInput = function(arParams, arEventParam
358s)
359{
360	if (arEventParams.CONTROL_ID != this.arParams.CONTROL_ID)
361		return;
362
363	if (null == this.VALUE_CONTAINER) return;
364	if (null == arEventParams.TOKEN.DATA || null == arEventParams.TOKEN.DATA.ID) return;
365
366	if (null == arEventParams.TOKEN.INPUT)
367	{
368		arEventParams.TOKEN.INPUT = document.createElement('INPUT');
369		arEventParams.TOKEN.INPUT.type = 'hidden';
370		arEventParams.TOKEN.INPUT.name = this.arParams.INPUT_NAME + '[]';
371		arEventParams.TOKEN.INPUT.value = arEventParams.TOKEN.DATA.ID;
372	}
373
374	if (arEventParams.TOKEN.ACTIVE && null == arEventParams.TOKEN.INPUT.parentNode)
375	{
376		this.AddInput(arEventParams.TOKEN.INPUT);
377		jsUtils.onCustomEvent('onLookupInputChange', {'CONTROL_ID': this.arParams.CONTROL_ID, 'ACTION': 'add', 'DATA': arEventParams.TOKEN.DATA});
378	}
379	else if (!arEventParams.TOKEN.ACTIVE && null != arEventParams.TOKEN.INPUT.parentNode)
380	{
381		this.DeleteInput(arEventParams.TOKEN.INPUT);
382		jsUtils.onCustomEvent('onLookupInputChange', {'CONTROL_ID': this.arParams.CONTROL_ID, 'ACTION': 'remove', 'DATA': arEventParams.TOKEN.DATA});
383	}
384};
385
386
387JCMainLookupSelector.prototype.AddValue = function(arValue)
388{
389	if (typeof arValue != 'object' || null == arValue.length || null == arValue[0])
390		arValue = [arValue];
391
392	var _this = this;
393	for (var i = 0; i < arValue.length; i++)
394	{
395		if (typeof arValue[i] == 'object')
396		{
397			if (null != arValue[i].ID && null != arValue[i].NAME)
398			{
399				this.VISUAL.AddTokenData(arValue[i], false);
400			}
401		}
402		else
403		{
404			var val = parseInt(arValue[i]);
405			if (!isNaN(val))
406			{
407				var str = 'q <q@q> [' + val + ']'; // hack
408				var url = this.arParams.AJAX_PAGE+'?MODE=SEARCH';
409
410				url += '&search=' + encodeURIComponent(str);
411				if (this.arParams.AJAX_PARAMS)
412				{
413					for(var param_name in this.arParams.AJAX_PARAMS)
414						url += '&' + param_name + '=' + encodeURIComponent(this.arParams.AJAX_PARAMS[param_name]);
415				}
416				BX.ajax.get(url, function(data) {
417					if (data.length <= 0)
418						return;
419
420					var DATA = [];
421					eval('DATA = ' + data);
422					if (DATA.length == 1 && DATA[0].READY == 'Y')
423					{
424						_this.VISUAL.AddTokenData(DATA[0], false);
425					}
426				});
427			}
428		}
429	}
430};
431
432JCMainLookupSelector.prototype.SetValue = function(arValue)
433{
434	this.VISUAL.Reset(true, false);
435	this.ClearValues();
436	this.AddValue(arValue);
437};
438
439JCMainLookupSelector.prototype.ClearValues = function()
440{
441	if (this.VALUE_CONTAINER && this.VALUE_CONTAINER.childNodes)
442	{
443		var children = this.VALUE_CONTAINER.childNodes;
444		for (var i = 0; i < children.length; i++)
445		{
446			var child = children[i];
447			child.value = '';
448		}
449		this.CleanUpValues();
450	}
451};
452
453JCMainLookupSelector.prototype.AddInput = function(input)
454{
455	if (this.VALUE_CONTAINER)
456	{
457		this.VALUE_CONTAINER.appendChild(input);
458		this.CleanUpValues();
459	}
460};
461
462JCMainLookupSelector.prototype.DeleteInput = function(input)
463{
464	if (this.VALUE_CONTAINER)
465	{
466		input.value = '';
467		this.CleanUpValues();
468	}
469};
470
471JCMainLookupSelector.prototype.CleanUpValues = function(input)
472{
473	if (this.VALUE_CONTAINER && this.VALUE_CONTAINER.childNodes)
474	{
475		var i;
476		var found = false;
477		var children = this.VALUE_CONTAINER.childNodes;
478		for (i = 0; i < children.length; i++)
479		{
480			var child = children[i];
481			if (child.value.length > 0)
482				found++;
483		}
484
485		i = 0;
486		while (i < children.length)
487		{
488			var child = children[i];
489			if (child.value.length == 0)
490			{
491				if (found > 0)
492				{
493					this.VALUE_CONTAINER.removeChild(child);
494				}
495				else
496				{
497					i++;
498					found++;
499				}
500			}
501			else
502			{
503				i++;
504			}
505		}
506	}
507};
508
509/**************** Visual textarea **********************/
510function JCMainLookupSelectorText(arParams)
511{
512	var _this = this;
513
514	this.__split_reg = /([,;\n])/;
515	this.__check_reg = /^(.*?) \[\d+\]/m;
516	this.__check_suspicious = [/^[a-z0-9.\-_]+@[a-z0-9.\-]+$/i, /^(.*?)<[a-z0-9.\-_]+@[a-z0-9.\-]+>$/i, /^(.*?)\[[a-z0-9.\-_]+@[a-z0-9.\-]+\]$/i];
517
518	this.arParams = arParams;
519
520	this.__token_index = 0;
521
522	this.previousCurrentHash = '';
523	this.previousCurrentIndex = -1;
524
525	this.timerId = null;
526
527	this.isMainUiFilter = (this.arParams['MAIN_UI_FILTER'] === 'Y');
528	this.isMultiple = (this.arParams['MULTIPLE'] === 'Y');
529
530	this.TEXT = document.getElementById(this.arParams.ID);
531	this.TEXT.bx_last_position = 0;
532	this.TEXT.bx_focused = false;
533
534	this.TEXT.style.width = '95%';
535	if (this.arParams.MAX_WIDTH) this.TEXT.style.width = this.arParams.MAX_WIDTH + 'px';
536	if(this.TEXT.type.toLowerCase() == "textarea")
537		this.TEXT.style.height = this.arParams.MIN_HEIGHT + 'px';
538
539	this.__text_focus = function()
540	{
541		_this.TEXT.bx_focused = true;
542		if (_this.TEXT.value == _this.arParams.START_TEXT)
543		{
544			_this.TEXT.value = '';
545		}
546	};
547	this.__text_blur = function()
548	{
549		_this.TEXT.bx_focused = false;
550		if (_this.TEXT.value == '')
551		{
552			_this.TEXT.value = _this.arParams.START_TEXT;
553		}
554	};
555	this.__text_additional_check = function()
556	{
557		_this.TEXT.bx_focused = false;
558		_this.__process();
559	};
560
561	if (this.TEXT.value == '')
562	{
563		this.TEXT.value = this.arParams.START_TEXT;
564	}
565
566	jsUtils.addEvent(this.TEXT, 'focus', this.__text_focus);
567	jsUtils.addEvent(this.TEXT, 'blur', this.__text_blur);
568	jsUtils.addEvent(this.TEXT, 'blur', this.__text_additional_check);
569
570	if (null != this.TEXT.form)
571	{
572		jsUtils.addEvent(this.TEXT.form, 'submit', this.__text_focus);
573	}
574
575	this.arTokens = [];
576	this.arTokensMap = {};
577
578	/*** internal event handlers ****/
579	this.onCurrentStringExists = null; // caret in textarea is on the unidentified string
580	this.onCurrentStringChange = null; // caret in textarea changes to unidentified string
581	this.onCurrentTokenExists = null; // caret in textarea is on the identified token
582	this.onUnidentifiedTokenFound = null; // found a string in required format, which hasn't a token object
583	this.onControlKeyPressed = null; // arrow or escape key was pressed while textarea editing; return false to prevent default key action
584	this.onSuspiciousTokensFound = null; // found some suspicious tokens found. array of them is passing as parameter (empty one if none found)
585
586	/*** tokens parsing ****/
587	this.__ignore_key = false;
588
589	this.__pre_process = function() {_this.__process(); };
590
591	this.TEXT.onkeydown = function(e)
592	{
593		if (null == e) e = window.event;
594
595		if (null != _this.onControlKeyPressed && ((e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 27 || e.keyCode == 13))
596		{
597			if (!_this.onControlKeyPressed(e.keyCode))
598			{
599				_this.__ignore_key = true;
600				return jsUtils.PreventDefault(e);
601			}
602		}
603
604	};
605
606	this.TEXT.onclick = this.TEXT.onkeyup = function(e) {
607		if (null == e) e = window.event;
608
609		if (e.type == 'keyup' && e.keyCode >= 16 && e.keyCode <= 18) // shift, alt & ctrl keys for avoing of event doubling while copy-pasting
610			return;
611
612		if (e.type == 'keyup' && _this.__ignore_key)
613		{
614			_this.__ignore_key = false;
615			return false;
616		}
617
618		if (null != _this.timerId)
619			clearTimeout(_this.timerId);
620
621		_this.timerId = setTimeout(_this.__pre_process, 500);
622	};
623
624	if (this.TEXT.value.length > 0)
625	{
626		this.timerId = setTimeout(this.__pre_process, 200);
627	}
628
629	_this.AdjustHeight();
630}
631
632
633//This function splits string with respect to __check_reg pattern
634JCMainLookupSelectorText.prototype.__split = function (str, separator, limit) {
635
636	if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
637		return str.split(separator, limit);
638	}
639
640	var _compliantExecNpcg = /()??/.exec("")[1] === undefined;
641
642	var output = [];
643	var lastLastIndex = 0;
644	var flags = (separator.ignoreCase ? "i" : "") + (separator.multiline  ? "m" : "") + (separator.sticky     ? "y" : "");
645	separator = RegExp(separator.source, flags + "g");
646	var separator2, match, lastIndex, lastLength;
647
648	str = str + ""; // type conversion
649	if (!_compliantExecNpcg)
650		separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt
651
652	/* behavior for `limit`: if it's...
653	- `undefined`: no limit.
654	- `NaN` or zero: return an empty array.
655	- a positive number: use `Math.floor(limit)`.
656	- a negative number: no limit.
657	- other: type-convert, then use the above rules. */
658	if (limit === undefined || +limit < 0)
659	{
660		limit = Infinity;
661	}
662	else
663	{
664		limit = Math.floor(+limit);
665		if (!limit)
666			return [];
667	}
668
669	while (match = separator.exec(str))
670	{
671		lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser
672
673		if (lastIndex > lastLastIndex)
674		{
675			output.push(str.slice(lastLastIndex, match.index));
676
677			// fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
678			if (!_compliantExecNpcg && match.length > 1)
679			{
680				match[0].replace(separator2, function () {
681					for (var i = 1; i < arguments.length - 2; i++)
682						if (arguments[i] === undefined)
683							match[i] = undefined;
684				});
685			}
686
687			if (match.length > 1 && match.index < str.length)
688				Array.prototype.push.apply(output, match.slice(1));
689
690			lastLength = match[0].length;
691			lastLastIndex = lastIndex;
692
693			if (output.length >= limit)
694				break;
695
696		}
697
698		if (separator.lastIndex === match.index)
699			separator.lastIndex++; // avoid an infinite loop
700	}
701
702	if (lastLastIndex === str.length)
703	{
704		if (lastLength || !separator.test(""))
705		{
706			output.push("");
707		}
708	}
709	else
710	{
711		output.push(str.slice(lastLastIndex));
712	}
713
714	return output.length > limit ? output.slice(0, limit) : output;
715};
716
717//This function splits string with respect to __check_reg pattern
718JCMainLookupSelectorText.prototype.__parse = function(str, split_reg, check_reg, arTokens, newStr)
719{
720	var arResult = [];
721
722	var arToks = [];
723	var tok = '';
724
725	if(arTokens && arTokens.length > 0)
726	{
727		for(var j = 0; j < arTokens.length; j++)
728		{
729			if(arTokens[j])
730			{
731				tok = jsUtils.trim(arTokens[j].TOKEN);
732				if(tok.length)
733				{
734					arToks[arToks.length] = tok;
735					var start = -1;
736					while( (start = str.indexOf(tok, start+1)) > -1 )
737					{
738						arResult[arResult.length] = {
739							'start' : start,
740							'end' : start + tok.length,
741							'tok' : tok,
742							'trimmed': jsUtils.trim(tok),
743							'delim' : ''
744						};
745					}
746				}
747			}
748		}
749	}
750	if(newStr && newStr.length > 0)
751	{
752		tok = jsUtils.trim(newStr);
753		if(tok.length)
754		{
755			arToks[arToks.length] = tok;
756			start = -1;
757			while( (start = str.indexOf(tok, start+1)) > -1 )
758			{
759				var found = false;
760				for(var i =0; i < arResult.length; i++)
761				{
762					if(start >= arResult[i].start && start < arResult[i].end)
763					{
764						start = arResult[i].end;
765						found = true;
766						break;
767					}
768				}
769
770				if(!found)
771				{
772					arResult[arResult.length] = {
773						'start' : start,
774						'end' : start + tok.length,
775						'tok' : tok,
776						'trimmed': jsUtils.trim(tok),
777						'delim' : ''
778					};
779					break;
780				}
781			}
782		}
783	}
784
785	var arTmp = this.__split(str, split_reg);
786
787	tok = '';
788	var delim = '';
789	var cur_pos = 0;
790	for(i = 0; i < arTmp.length; i++)
791	{
792		tok = arTmp[i];
793
794		i++;
795		if(i < arTmp.length)
796			delim = arTmp[i];
797		else
798			delim = '';
799
800		var skip = false;
801		if(tok.length)
802		{
803			for(var ii = 0; ii < arResult.length && !skip; ii++)
804			{
805				if ( cur_pos >= arResult[ii].start && cur_pos < arResult[ii].end )
806				{
807					arResult[ii].delim = delim;
808					skip = true;
809				}
810			}
811			//if ( cur_pos >= strNew_start && cur_pos < strNew_end )
812			//	skip = true;
813		}
814
815		if(tok.length && !skip)
816		{
817			//Additional check if this is string followed known token
818			if(check_reg.test(tok) && arToks.length > 0)
819			{
820				for(j = 0; j < arToks.length; j++)
821				{
822					if(
823						tok.length > arToks[j].length
824						&& arToks[j] == tok.substr(tok.length - arToks[j].length)
825					)
826					{
827
828						var pre_tok = tok.substr(0, tok.length - arToks[j].length);
829						tok = tok.substr(pre_tok.length);
830
831						while(pre_tok && pre_tok.length > 0 && pre_tok.substr(0, 1) == ' ')
832						{
833							cur_pos++;
834							pre_tok = pre_tok.substr(1);
835						}
836
837						if(pre_tok && pre_tok.length > 0)
838						{
839							arResult[arResult.length] = {
840								'start' : cur_pos,
841								'end' : cur_pos + pre_tok.length,
842								'tok' : pre_tok,
843								'trimmed': jsUtils.trim(pre_tok),
844								'delim' : ''
845							};
846							cur_pos += pre_tok.length;
847						}
848					}
849				}
850			}
851
852			while(tok.length && tok.substr(0, 1) == ' ')
853			{
854				cur_pos++;
855				tok = tok.substr(1);
856			}
857			arResult[arResult.length] = {
858				'start' : cur_pos,
859				'end' : cur_pos + tok.length,
860				'tok' : tok,
861				'trimmed': jsUtils.trim(tok),
862				'delim' : delim
863			};
864		}
865
866		cur_pos += tok.length + delim.length;
867	}
868
869//	if(tok.length)
870//		arResult[arResult.length] = {
871//			'start' : cur_pos,
872//			'end' : cur_pos + tok.length,
873//			'tok' : tok,
874//			'delim' : ''
875//		};
876
877	return arResult;
878};
879
880JCMainLookupSelectorText.prototype.__process = function()
881{
882	this.__current_token = null;
883
884	if (
885		(
886			this.arParams.START_TEXT.length > 0
887			&& this.TEXT.value == this.arParams.START_TEXT
888		) || (
889			this.TEXT.value == ''
890		)
891	)
892	{
893		this.parent.ClearValues();
894		return;
895	}
896
897	var arToks = this.__parse(this.TEXT.value, this.__split_reg, this.__check_reg, this.arTokens);
898
899	var cur_pos = this.GetCursorPos();
900	var bCurrent;
901
902	var arSuspiciousTokens = [];
903
904	for (var i = 0; i < arToks.length; i++)
905	{
906		bCurrent = (cur_pos > arToks[i].start && cur_pos <= arToks[i].end);
907
908		var str = jsUtils.trim(arToks[i].tok);
909		if (str.length > 0)
910		{
911			if (null != this.onUnidentifiedTokenFound && this.__check_reg.test(str))
912			{
913				if (null == this.arTokensMap[this.GetHash(str)])
914					this.onUnidentifiedTokenFound(str);
915			}
916			else
917			{
918				if (bCurrent)
919				{
920					var currentHash = this.GetHash(str);
921					var currentIndex = i;
922
923					if (
924						null != this.arTokensMap[this.previousCurrentHash]
925						&& currentHash != this.previousCurrentHash
926						&& currentIndex == this.previousCurrentIndex
927					)
928						this.arTokensMap[this.previousCurrentHash] = null;
929
930					/**** external events block *****/
931					if (
932						null != this.onCurrentStringChange
933						&& null != this.previousCurrentIndex
934						&& this.previousCurrentIndex != currentIndex
935					)
936						this.onCurrentStringChange();
937
938
939					if (
940						null != this.onCurrentStringExists
941						&& null == this.arTokensMap[currentHash]
942					)
943						this.onCurrentStringExists(str);
944
945					if (
946						null != this.onCurrentTokenExists
947						&& null != this.arTokensMap[currentHash]
948					)
949						this.onCurrentTokenExists(this.arTokensMap[currentHash]);
950
951					this.previousCurrentHash = currentHash;
952					this.previousCurrentIndex = currentIndex;
953				}
954
955				if (null != this.onSuspiciousTokensFound)
956				{
957					for (var j = 0; j < this.__check_suspicious.length; j++)
958					{
959						if (this.__check_suspicious[j].test(str))
960						{
961							arSuspiciousTokens[arSuspiciousTokens.length] = str;
962							break;
963						}
964					}
965				}
966			}
967		}
968	}
969
970	if (null != this.onSuspiciousTokensFound)
971		this.onSuspiciousTokensFound(arSuspiciousTokens);
972
973	this.CheckTokens(arToks);
974	this.AdjustHeight();
975};
976
977JCMainLookupSelectorText.prototype.Reset = function(bClearText, bClearEvents)
978{
979	if (null == bClearText) bClearEvents = false;
980	if (null == bClearEvents) bClearEvents = false;
981
982	for (var i = 0; i < this.arTokens.length; i++)
983	{
984		if (null == this.arTokens[i])
985			continue;
986		this.arTokensMap[this.arTokens[i].TEXT_HASH] = null;
987		this.arTokens[i] = null;
988	}
989
990	this.arTokens = [];
991	this.arTokensMap = {};
992
993	this.__token_index = 0;
994
995	this.previousCurrentHash = '';
996	this.previousCurrentIndex = -1;
997
998	if (bClearText)
999	{
1000		this.TEXT.value = this.arParams.START_TEXT;
1001	}
1002
1003	if (bClearEvents)
1004	{
1005		this.TEXT.onkeydown = this.TEXT.onkeyup = this.TEXT.onclick = null;
1006		jsUtils.removeEvent(this.TEXT, 'focus', this.__text_focus);
1007		jsUtils.removeEvent(this.TEXT, 'blur', this.__text_blur);
1008		jsUtils.removeEvent(this.TEXT, 'blur', this.__text_additional_check);
1009		if (null != this.TEXT.form)
1010			jsUtils.removeEvent(this.TEXT.form, 'submit', this.__text_focus);
1011
1012	}
1013};
1014
1015JCMainLookupSelectorText.prototype.CheckTokens = function(arToks)
1016{
1017	if (!arToks)
1018	{
1019		arToks = this.__parse(this.TEXT.value, this.__split_reg, this.__check_reg, this.arTokens);
1020	}
1021
1022	var index = [];
1023	for (var j = 0; j < arToks.length; j++)
1024	{
1025		if (arToks[j].trimmed.length <= 0)
1026			continue;
1027		if (index[arToks[j].trimmed] !== undefined)
1028			continue;
1029		index[arToks[j].trimmed] = j;
1030	}
1031	
1032	for (var i = 0; i < this.arTokens.length; i++)
1033	{
1034		if (null == this.arTokens[i])
1035			continue;
1036
1037		var tok = jsUtils.trim(this.arTokens[i].TOKEN);
1038
1039		this.arTokens[i].SetActive(index[tok] !== undefined);
1040	}
1041};
1042
1043JCMainLookupSelectorText.prototype.AdjustHeight = function()
1044{
1045	if (this.TEXT.scrollHeight > this.TEXT.clientHeight)
1046	{
1047		var dy = this.TEXT.offsetHeight - this.TEXT.clientHeight;
1048		var newHeight = this.TEXT.scrollHeight + dy;
1049
1050		if (newHeight > this.arParams.MAX_HEIGHT)
1051			newHeight = this.arParams.MAX_HEIGHT;
1052
1053		if(this.TEXT.type.toLowerCase() == "textarea")
1054			this.TEXT.style.height = newHeight + 'px';
1055	}
1056};
1057
1058JCMainLookupSelectorText.prototype.AddTokenData = function(data, bSelect)
1059{
1060	if (this.TEXT.value == this.arParams.START_TEXT)
1061		this.TEXT.value = '';
1062
1063	// IE bug: focus on hidden input
1064	//try{this.TEXT.focus();}catch(e){};
1065	var scrollTop = this.TEXT.scrollTop;
1066
1067	if(this.TEXT.type.toLowerCase() == "textarea")
1068	{
1069		var str = jsUtils.trim(data.NAME + ' [' + data.ID + ']');
1070		if(this.TEXT.value.indexOf(str) < 0)
1071		{
1072			if (this.TEXT.value.length > 0 && this.TEXT.value.substr(this.TEXT.value.length-1, 1) != "\n")
1073				this.TEXT.value += "\n";
1074
1075			this.TEXT.value += str + "\n";
1076			this.TEXT.scrollTop = scrollTop;
1077			this.SetTokenData(str, data, bSelect);
1078		}
1079	}
1080	else
1081	{
1082		str = jsUtils.trim(data.NAME + ' [' + data.ID + ']');
1083		if (this.isMultiple && this.isMainUiFilter)
1084		{
1085			if(this.TEXT.value.indexOf(str) < 0)
1086			{
1087				this.TEXT.value += str + "\u00A0";
1088				this.SetTokenData(str, data, bSelect);
1089			}
1090		}
1091		else
1092		{
1093			if(this.TEXT.value.indexOf(str) < 0)
1094			{
1095				this.TEXT.value = str;
1096				this.SetTokenData(str, data, bSelect);
1097			}
1098		}
1099	}
1100
1101};
1102
1103JCMainLookupSelectorText.prototype.SetTokenData = function(str, data, bSelect)
1104{
1105	if (null == bSelect) bSelect = true;
1106
1107	var arToks = this.__parse(this.TEXT.value, this.__split_reg, this.__check_reg, this.arTokens, str);
1108
1109	var delim = '';
1110	for (var i = 0; i < arToks.length; i++)
1111	{
1112		if(delim == '')//take first delim
1113			delim = arToks[i].delim;
1114		if(delim.length)
1115			break;
1116	}
1117	if(delim == '')//fall to default
1118		delim = "\n";
1119
1120	for (i = 0; i < arToks.length; i++)
1121	{
1122		var s = jsUtils.trim(arToks[i].tok);
1123
1124		if (s.length <= 0)
1125			continue;
1126
1127		if (str == s)
1128		{
1129			var newText = jsUtils.trim(data.NAME + ' [' + data.ID + ']');
1130
1131			//delimiter is needed only at the end of input
1132			//at when replaced text has no delimiter just before next token
1133			var actual_delim = '';
1134			if(i == arToks.length || arToks[i].delim == '')
1135				actual_delim = delim;
1136
1137			// IE bug: focus on hidden input
1138			//try{this.TEXT.focus();}catch(e){};
1139
1140			var scrollTop = this.TEXT.scrollTop;
1141
1142			var prefix = this.TEXT.value.substr(0, arToks[i].start);
1143			var posfix = this.TEXT.value.substr(arToks[i].end);
1144
1145			this.TEXT.value = prefix + newText + actual_delim + posfix;
1146
1147			this.TEXT.scrollTop = scrollTop;
1148
1149			var obToken = new JCMainLookupSelectorToken({
1150				'CONTROL_ID': this.arParams.CONTROL_ID,
1151				'TOKEN': newText,
1152				'INDEX': this.__token_index++,
1153				'START': arToks[i].start,
1154				'FINISH': arToks[i].start + newText.length + actual_delim.length,
1155				'ACTIVE': true,
1156				'DATA': data
1157			});
1158
1159			if (null != this.arTokensMap[obToken.TEXT_HASH])
1160				this.arTokens[this.arTokensMap[obToken.TEXT_HASH].INDEX] = null;
1161
1162			var change = newText.length + actual_delim.length - arToks[i].tok.length;
1163			this.AdjustTokensPos(arToks[i].end, change);
1164
1165			this.arTokensMap[obToken.TEXT_HASH] = this.arTokens[obToken.INDEX] = obToken;
1166
1167			if (bSelect)
1168				this.SetCursorPos(obToken.FINISH);
1169
1170			if (null != this.onCurrentTokenExists)
1171				this.onCurrentStringExists(obToken);
1172
1173			this.AdjustHeight();
1174			this.__pre_process();
1175			return;
1176		}
1177	}
1178
1179	this.AddTokenData(data, bSelect);
1180};
1181
1182JCMainLookupSelectorText.prototype.AdjustTokensPos = function(start_pos, change)
1183{
1184	for (var i = 0; i < this.arTokens.length; i++)
1185	{
1186		if (null != this.arTokens[i] && this.arTokens[i].ACTIVE && this.arTokens[i].START >= start_pos)
1187		{
1188			this.arTokens[i].START += change;
1189			this.arTokens[i].FINISH += change;
1190		}
1191	}
1192};
1193
1194JCMainLookupSelectorText.prototype.SetCursorPos = function(pos)
1195{
1196	if (null != this.TEXT.selectionStart)// Gecko
1197		this.TEXT.setSelectionRange(pos, pos);
1198	else if (document.selection)//IE
1199	{
1200		try {
1201			var r = this.TEXT.createTextRange();
1202			r.collapse(true);
1203			r.moveEnd('character', pos);
1204			r.moveStart('character', pos);
1205			r.select();
1206		} catch (e) {}
1207	}
1208};
1209
1210JCMainLookupSelectorText.prototype.GetCursorPos = function()
1211{
1212	try {
1213		if (null != this.TEXT.selectionStart)// Gecko
1214			return this.TEXT.selectionStart;
1215		else if (document.selection && this.TEXT.bx_focused)//IE
1216		{
1217			if(this.TEXT.type.toLowerCase() == "textarea")
1218			{
1219				var sel = document.selection.createRange();
1220				var clone = sel.duplicate();
1221				clone.moveToElementText(this.TEXT);
1222				clone.setEndPoint('EndToEnd', sel);
1223				this.TEXT.bx_last_position = clone.text.length;
1224			}
1225			else
1226				this.TEXT.bx_last_position = 1;
1227		}
1228
1229		return this.TEXT.bx_last_position;
1230	} catch (e) {return 0; }
1231};
1232
1233JCMainLookupSelectorText.prototype.GetHash = function(text)
1234{
1235	var hash = '';
1236	for (var i = 0; i < text.length; i++)
1237	{
1238		hash += text.charCodeAt(i).toString(16);
1239	}
1240	return hash;
1241};
1242
1243/**************** Visual textarea token ******************/
1244function JCMainLookupSelectorToken(arParams)
1245{
1246	var _this = this;
1247
1248	this.CONTROL_ID = arParams.CONTROL_ID;
1249
1250	this.TOKEN = arParams.TOKEN;
1251	this.INDEX = arParams.INDEX;
1252	this.START = arParams.START;
1253	this.FINISH = arParams.FINISH;
1254
1255	this.DATA = arParams.DATA;
1256
1257	this.SetActive(null == arParams.ACTIVE ? false : arParams.ACTIVE);
1258
1259	this.HASH = this.GetHash(this.INDEX + '$$' + jsUtils.trim(this.TOKEN));
1260	this.TEXT_HASH = this.GetHash(jsUtils.trim(this.TOKEN));
1261}
1262
1263JCMainLookupSelectorToken.prototype.SetActive = function(flag)
1264{
1265	this.ACTIVE = !!flag;
1266	jsUtils.onCustomEvent('onEmpUserSelectorChangeTokenActivity', {'CONTROL_ID': this.CONTROL_ID, 'TOKEN': this});
1267};
1268
1269JCMainLookupSelectorToken.prototype.SetPos = function(start, finish)
1270{
1271	if (null == finish) finish = start+this.TOKEN.length;
1272
1273	this.START = start; this.FINISH = finish;
1274};
1275
1276JCMainLookupSelectorToken.prototype.GetHash = JCMainLookupSelectorText.prototype.GetHash;
1277

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.