PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/components/bitrix/main.lookup.input/script.js

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