PageRenderTime 46ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/bitrix/js/main/masked_input.js

https://bitbucket.org/ayerspro/uchef
JavaScript | 832 lines | 701 code | 118 blank | 13 comment | 142 complexity | e8f30e3192fd294a50fc69d4926a1774 MD5 | raw file
Possible License(s): Apache-2.0
  1. ;(function()
  2. {
  3. if (BX.MaskedInput)
  4. return;
  5. var defaultDefinitions = {
  6. "cypher": {
  7. "char": "9",
  8. "rule": "[0-9]"
  9. },
  10. "hexrgb": {
  11. "char": "h",
  12. "rule": "[A-Fa-f0-9]"
  13. },
  14. "lang_en": {
  15. "char": "a",
  16. "rule": "[a-zA-Z]"
  17. }
  18. };
  19. /**
  20. * @param {object} params
  21. * @constructor
  22. */
  23. BX.MaskedInput = function (params)
  24. {
  25. this.mask = params.mask || '';
  26. this.placeholder = (params.placeholder || '_').substring(0, 1);
  27. this.definitions = this.prepareDefinitions(params.definitions || ['cypher', 'lang_en']);
  28. this.stopChangeEvent = false;
  29. this.initInput(params);
  30. };
  31. BX.MaskedInput.prototype.initInput = function (params)
  32. {
  33. if (params.input.tagName == 'INPUT')
  34. {
  35. this.input = new BX.MaskedInputElement({node: params.input});
  36. }
  37. else
  38. {
  39. this.input = new BX.MaskedTextElement({node: params.input});
  40. }
  41. this.dataInput = params.dataInput || null;
  42. this.isDataInputClean = params.isDataInputClean || false;
  43. this.isHoldOverInputValueInit = params.isHoldOverInputValueInit || false;
  44. this.enableCheckingValue = params.enableCheckingValue || false;
  45. this.onDataInputChange = params.onDataInputChange || null;
  46. this.onDataInputInitValue = params.onDataInputInitValue || null;
  47. BX.addCustomEvent(this.input, 'change', BX.proxy(this.onInputChange, this));
  48. BX.addCustomEvent(this.input, 'paste', BX.proxy(this.onInputPaste, this));
  49. BX.addCustomEvent(this.input, 'delete', BX.proxy(this.onInputDelete, this));
  50. BX.bind(this.input.node, 'focus', BX.proxy(function ()
  51. {
  52. if (this.getFirstEmptyPosition() === null)
  53. {
  54. return;
  55. }
  56. setTimeout(BX.proxy(function ()
  57. {
  58. this.moveCaret(this.getFirstEmptyPosition());
  59. }, this), 50);
  60. }, this));
  61. if (!this.isHoldOverInputValueInit && !this.test(this.input.val()))
  62. {
  63. this.input.val(this.getMaskedPlaceholder());
  64. }
  65. if (this.dataInput)
  66. {
  67. var dataInputVal = this.dataInput.value;
  68. if (BX.type.isFunction(this.onDataInputInitValue))
  69. {
  70. dataInputVal = this.onDataInputInitValue.apply(this, [dataInputVal]);
  71. }
  72. if (BX.type.isString(dataInputVal) && dataInputVal.length > 0)
  73. {
  74. this.setValue(dataInputVal);
  75. }
  76. BX.addCustomEvent(this, 'change', BX.proxy(function ()
  77. {
  78. var inputVal = '';
  79. if (BX.type.isFunction(this.onDataInputChange))
  80. {
  81. inputVal = this.onDataInputChange.apply(this, [this.getValueClean(), this.getValue()]);
  82. }
  83. else if (this.isDataInputClean)
  84. {
  85. inputVal = this.getValueClean();
  86. }
  87. else
  88. {
  89. inputVal = this.getValue();
  90. }
  91. if (!BX.type.isString(inputVal))
  92. {
  93. inputVal = '';
  94. }
  95. if (this.enableCheckingValue && !this.checkValue())
  96. {
  97. inputVal = '';
  98. }
  99. this.dataInput.value = inputVal;
  100. }, this));
  101. }
  102. };
  103. BX.MaskedInput.prototype.setMask = function (mask)
  104. {
  105. if (this.mask == mask)
  106. {
  107. return;
  108. }
  109. var val = this.getValueClean();
  110. this.mask = mask;
  111. this.setChangeEventFiring(false);
  112. this.setValue(val);
  113. this.setChangeEventFiring(true);
  114. };
  115. BX.MaskedInput.prototype.getMask = function ()
  116. {
  117. return this.mask;
  118. };
  119. BX.MaskedInput.prototype.setValue = function (val)
  120. {
  121. if (!this.mask)
  122. {
  123. this.input.val(val);
  124. this.fireChangeEvent();
  125. return;
  126. }
  127. var lastCaretPosition = this.input.getSelectionStart();
  128. this.moveCaret(0);
  129. this.input.val(this.mask);
  130. var j = 0;
  131. for (var i = 0; i < this.mask.length; i++)
  132. {
  133. if (!this.isMaskCharReplaceable(i))
  134. {
  135. this.replaceChar(i, this.mask.charAt(i));
  136. continue;
  137. }
  138. while (true)
  139. {
  140. var char = val.charAt(j);
  141. if (!char)
  142. {
  143. char = this.placeholder;
  144. break;
  145. }
  146. if (char == this.placeholder)
  147. {
  148. break;
  149. }
  150. if (this.testChar(i, char))
  151. {
  152. break;
  153. }
  154. j++;
  155. }
  156. this.replaceChar(i, char);
  157. j++;
  158. }
  159. this.moveCaret(lastCaretPosition);
  160. //this.moveCaret(this.getFirstEmptyPosition());
  161. this.fireChangeEvent();
  162. };
  163. BX.MaskedInput.prototype.getValue = function ()
  164. {
  165. return this.input.val();
  166. };
  167. BX.MaskedInput.prototype.getFirstEmptyPosition = function ()
  168. {
  169. var val = this.getValue();
  170. for (var i = 0; i < val.length; i++)
  171. {
  172. if (!this.isMaskCharReplaceable(i))
  173. {
  174. continue;
  175. }
  176. var char = val.charAt(i);
  177. if (char == this.placeholder)
  178. {
  179. return i;
  180. }
  181. }
  182. return null;
  183. };
  184. BX.MaskedInput.prototype.getValueClean = function ()
  185. {
  186. var returnValue = '';
  187. var val = this.getValue();
  188. if (!this.mask)
  189. {
  190. return val;
  191. }
  192. for (var i = 0; i < val.length; i++)
  193. {
  194. if (!this.isMaskCharReplaceable(i))
  195. {
  196. continue;
  197. }
  198. var char = val.charAt(i);
  199. returnValue += char == this.placeholder ? '' : char;
  200. }
  201. return returnValue;
  202. };
  203. BX.MaskedInput.prototype.checkValue = function ()
  204. {
  205. var val = this.getValue();
  206. if (!this.mask)
  207. {
  208. return val;
  209. }
  210. for (var i = 0; i < val.length; i++)
  211. {
  212. if (!this.isMaskCharReplaceable(i))
  213. {
  214. continue;
  215. }
  216. if (val.charAt(i) == this.placeholder)
  217. {
  218. return false;
  219. }
  220. }
  221. return true;
  222. };
  223. BX.MaskedInput.prototype.onInputDelete = function (directionLeft)
  224. {
  225. if (this.deleteSelection())
  226. {
  227. this.fireChangeEvent();
  228. return;
  229. }
  230. var pos = this.input.getSelectionStart();
  231. if (directionLeft)
  232. {
  233. if (pos === this.mask.length)
  234. {
  235. this.replaceChar(pos - 1, this.placeholder);
  236. }
  237. else
  238. {
  239. this.shift(pos, pos - 1);
  240. }
  241. this.moveCaret(pos - 1);
  242. }
  243. else
  244. {
  245. if (pos === this.mask.length - 1)
  246. {
  247. this.replaceChar(pos, this.placeholder);
  248. }
  249. else if (pos < this.mask.length - 1)
  250. {
  251. this.shift(pos + 1, pos);
  252. }
  253. this.moveCaret(pos);
  254. }
  255. this.fireChangeEvent();
  256. };
  257. BX.MaskedInput.prototype.onInputPaste = function (pastedData)
  258. {
  259. this.deleteSelection();
  260. for (var i = 0; i < pastedData.length; i++)
  261. {
  262. this.setCharOnCaret(pastedData.charAt(i));
  263. }
  264. this.fireChangeEvent();
  265. };
  266. BX.MaskedInput.prototype.onInputChange = function (char)
  267. {
  268. if (this.input.val() == '' && this.mask)
  269. {
  270. this.input.val(this.getMaskedPlaceholder());
  271. this.input.setCaretPosition(0);
  272. }
  273. this.deleteSelection();
  274. this.setCharOnCaret(char);
  275. this.fireChangeEvent();
  276. };
  277. BX.MaskedInput.prototype.moveCaret = function (pos)
  278. {
  279. if (pos > this.mask.length)
  280. {
  281. pos = this.mask.length;
  282. }
  283. else if (pos < 0)
  284. {
  285. pos = 0;
  286. }
  287. else if (pos === null)
  288. {
  289. return this.input.getSelectionStart();
  290. }
  291. this.input.setCaretPosition(pos);
  292. return pos;
  293. };
  294. BX.MaskedInput.prototype.findClosestAllowPosition = function (pos, char, directionLeft)
  295. {
  296. if (typeof pos === 'undefined')
  297. {
  298. pos = 0;
  299. }
  300. if (typeof char === 'undefined')
  301. {
  302. char = null;
  303. }
  304. if (typeof directionLeft === 'undefined')
  305. {
  306. directionLeft = false;
  307. }
  308. while (true)
  309. {
  310. if (!directionLeft && pos >= this.mask.length)
  311. {
  312. return null;
  313. }
  314. else if (directionLeft && pos <= 0)
  315. {
  316. return 0;
  317. }
  318. if (this.isMaskCharReplaceable(pos))
  319. {
  320. break;
  321. }
  322. if (!directionLeft)
  323. {
  324. pos++;
  325. }
  326. else
  327. {
  328. pos--;
  329. }
  330. }
  331. if (!this.isMaskCharReplaceable(pos))
  332. {
  333. return null;
  334. }
  335. if (char && !this.testChar(pos, char))
  336. {
  337. return null;
  338. }
  339. return pos;
  340. };
  341. BX.MaskedInput.prototype.setCharOnCaret = function (char)
  342. {
  343. var pos = this.input.getSelectionStart();
  344. pos = this.findClosestAllowPosition(pos, char);
  345. if (pos === null)
  346. {
  347. return;
  348. }
  349. this.shift(pos, pos + 1);
  350. pos = this.replaceChar(pos, char);
  351. if (pos === null)
  352. {
  353. return;
  354. }
  355. pos = this.findClosestAllowPosition(pos + 1);
  356. this.moveCaret(pos);
  357. if (BX.browser.IsAndroid() && BX.browser.DetectAndroidVersion() < 7)
  358. {
  359. var _this = this;
  360. setTimeout(function ()
  361. {
  362. _this.moveCaret(pos);
  363. }, 50);
  364. }
  365. /*
  366. var pos = this.getFirstEmptyPosition();
  367. if(pos)
  368. {
  369. this.moveCaret(pos);
  370. }
  371. */
  372. };
  373. BX.MaskedInput.prototype.shift = function (start, target)
  374. {
  375. var i, char = null;
  376. var buffer = [];
  377. for (i = start; i < this.mask.length; i++)
  378. {
  379. if (!this.isMaskCharReplaceable(i)) continue;
  380. var val = this.input.val();
  381. buffer.push(val.charAt(i));
  382. this.replaceChar(i, this.placeholder);
  383. }
  384. buffer.reverse();
  385. for (i = target; i < this.mask.length; i++)
  386. {
  387. if (!this.isMaskCharReplaceable(i)) continue;
  388. if (buffer.length > 0)
  389. {
  390. char = buffer.pop();
  391. }
  392. else
  393. {
  394. char = this.placeholder;
  395. }
  396. this.replaceChar(i, char);
  397. }
  398. };
  399. BX.MaskedInput.prototype.deleteSelection = function ()
  400. {
  401. var posStart = this.input.getSelectionStart();
  402. var posEnd = this.input.getSelectionEnd();
  403. if (posStart == posEnd)
  404. {
  405. return false;
  406. }
  407. // delete
  408. for (var i = posStart; i < posEnd; i++)
  409. {
  410. if (!this.isMaskCharReplaceable(i))
  411. {
  412. continue;
  413. }
  414. this.replaceChar(i, this.placeholder);
  415. }
  416. this.shift(posEnd, posStart);
  417. this.moveCaret(posStart);
  418. return true;
  419. };
  420. BX.MaskedInput.prototype.setChangeEventFiring = function (start)
  421. {
  422. this.stopChangeEvent = !start;
  423. };
  424. BX.MaskedInput.prototype.fireChangeEvent = function ()
  425. {
  426. if (!this.stopChangeEvent)
  427. {
  428. BX.onCustomEvent(this, 'change', [this.getValueClean(), this.getValue()]);
  429. }
  430. };
  431. BX.MaskedInput.prototype.replaceChar = function (pos, char)
  432. {
  433. if (isNaN(pos))
  434. {
  435. return null;
  436. }
  437. var val = this.input.val();
  438. var valTml = val.substring(0, pos) + char;
  439. valTml += (pos >= val.length) ? '' : val.substring((pos + 1));
  440. val = valTml;
  441. this.input.val(val);
  442. return pos;
  443. };
  444. BX.MaskedInput.prototype.isMaskCharReplaceable = function (pos)
  445. {
  446. var char = this.mask.charAt(pos);
  447. if (!char)
  448. {
  449. return false;
  450. }
  451. return !!this.definitions[char];
  452. };
  453. BX.MaskedInput.prototype.getMaskedPlaceholder = function ()
  454. {
  455. var val = '';
  456. for (var i = 0; i < this.mask.length; i++)
  457. {
  458. var char = this.mask[i];
  459. if (this.definitions[char])
  460. {
  461. char = this.placeholder;
  462. }
  463. val += char;
  464. }
  465. return val;
  466. };
  467. BX.MaskedInput.prototype.prepareDefinitions = function (definitions)
  468. {
  469. var result = {};
  470. definitions.forEach(function (definition)
  471. {
  472. if (BX.type.isString(definition) && defaultDefinitions[definition])
  473. {
  474. definition = defaultDefinitions[definition];
  475. }
  476. if (BX.type.isPlainObject(definition))
  477. {
  478. var def = {
  479. "rule": definition.rule,
  480. "isFunction": false
  481. };
  482. if (BX.type.isFunction(definition.rule))
  483. {
  484. def.isFunction = true;
  485. }
  486. else
  487. {
  488. def.regexp = new RegExp(definition.rule);
  489. }
  490. result[definition.char] = def;
  491. }
  492. }, this);
  493. return result;
  494. };
  495. BX.MaskedInput.prototype.test = function (string)
  496. {
  497. for (var i = 0; i < string.length; i++)
  498. {
  499. var r = this.testChar(i, string[i]);
  500. if (!r)
  501. {
  502. return false;
  503. }
  504. }
  505. return true;
  506. };
  507. BX.MaskedInput.prototype.testChar = function (pos, char)
  508. {
  509. var maskChar = this.mask[pos];
  510. if (!this.definitions[maskChar])
  511. {
  512. return char === maskChar;
  513. }
  514. var isSuccess = true;
  515. if (this.definitions[maskChar].isFunction)
  516. {
  517. isSuccess = !!this.definitions[maskChar].func.apply(this, [char]);
  518. }
  519. else
  520. {
  521. isSuccess = this.definitions[maskChar].regexp.test(char);
  522. }
  523. return isSuccess;
  524. };
  525. BX.MaskedInputElement = function (params)
  526. {
  527. this.node = params.node;
  528. this.skipTextInputEvent = false;
  529. BX.bind(this.node, 'paste', BX.proxy(this.onChange, this));
  530. BX.bind(this.node, 'keypress', BX.proxy(this.onChange, this));
  531. if (BX.browser.IsAndroid() && BX.browser.DetectAndroidVersion() < 7)
  532. {
  533. BX.bind(this.node, 'textInput', BX.proxy(this.onAndroidInput, this));
  534. BX.bind(this.node, 'keydown', BX.proxy(this.onAndroidInput, this));
  535. }
  536. else
  537. {
  538. BX.bind(this.node, 'keydown', BX.proxy(this.onChange, this));
  539. }
  540. };
  541. BX.MaskedInputElement.prototype.val = function (value)
  542. {
  543. if (typeof value != 'undefined')
  544. {
  545. this.node.value = value;
  546. }
  547. return this.node.value;
  548. };
  549. BX.MaskedInputElement.prototype.onAndroidInput = function (e)
  550. {
  551. var kc = 0;
  552. if (e.type == 'keydown')
  553. {
  554. kc = (typeof e.which == "number") ? e.which : e.keyCode;
  555. if (kc != 8)
  556. {
  557. if (e.key == "Unidentified") // if keydown has wrong key, use textInput with right key
  558. {
  559. this.skipTextInputEvent = false;
  560. BX.PreventDefault(e);
  561. return false;
  562. }
  563. else
  564. {
  565. this.skipTextInputEvent = true;
  566. }
  567. }
  568. }
  569. else
  570. {
  571. if (this.skipTextInputEvent)
  572. {
  573. BX.PreventDefault(e);
  574. return false;
  575. }
  576. kc = e.data.toUpperCase().charCodeAt(0);
  577. }
  578. var eventObject = {
  579. keyCode: kc,
  580. which: kc,
  581. type: 'keydown'
  582. };
  583. return this.onChange(eventObject, e);
  584. };
  585. BX.MaskedInputElement.prototype.onChange = function (e, eReal)
  586. {
  587. var isCatch = true;
  588. var kc = (typeof e.which == "number") ? e.which : e.keyCode;
  589. if (kc <= 0)
  590. {
  591. return;
  592. }
  593. switch (e.type)
  594. {
  595. case 'keydown':
  596. isCatch = (
  597. (!e.ctrlKey && !e.altKey && !e.metaKey)
  598. &&
  599. (
  600. kc
  601. &&
  602. (
  603. (kc > 46 && kc <= 90) // chars
  604. ||
  605. kc > 145
  606. ||
  607. kc === 13 // Carriage return
  608. ||
  609. kc === 8 // [backspace] key
  610. ||
  611. kc === 46 // [Del] key
  612. ||
  613. (BX.browser.IsIOS() && kc === 127) // iOS [delete] key
  614. )
  615. )
  616. );
  617. break;
  618. case 'keypress':
  619. break;
  620. }
  621. if (!isCatch)
  622. {
  623. return;
  624. }
  625. BX.PreventDefault(eReal || e);
  626. if (e.type == 'paste')
  627. {
  628. var clipboardData = e.clipboardData || window.clipboardData;
  629. var pastedData = clipboardData.getData('Text');
  630. BX.onCustomEvent(this, 'paste', [pastedData]);
  631. }
  632. else if (kc === 8 || kc === 46 || (BX.browser.IsIOS() && kc === 127))
  633. {
  634. var directionLeft = kc === 8;
  635. BX.onCustomEvent(this, 'delete', [directionLeft]);
  636. }
  637. else
  638. {
  639. var char = String.fromCharCode(kc);
  640. BX.onCustomEvent(this, 'change', [char]);
  641. }
  642. };
  643. BX.MaskedInputElement.prototype.setCaretPosition = function (pos)
  644. {
  645. this.node.setSelectionRange(pos, pos);
  646. };
  647. BX.MaskedInputElement.prototype.getSelectionStart = function ()
  648. {
  649. if (this.node.selectionStart)
  650. {
  651. return this.node.selectionStart;
  652. }
  653. else if (this.node.createTextRange)
  654. {
  655. var range = this.node.createTextRange().duplicate();
  656. range.moveEnd('character', this.node.value.length);
  657. if (range.text == '')
  658. {
  659. return this.node.value.length;
  660. }
  661. else
  662. {
  663. return this.node.value.lastIndexOf(range.text);
  664. }
  665. }
  666. else
  667. {
  668. return 0;
  669. }
  670. };
  671. BX.MaskedInputElement.prototype.getSelectionEnd = function ()
  672. {
  673. if (this.node.selectionEnd)
  674. {
  675. return this.node.selectionEnd;
  676. }
  677. else if (this.node.createTextRange)
  678. {
  679. var range = this.node.createTextRange().duplicate();
  680. range.moveStart('character', -this.node.value.length);
  681. return range.text.length;
  682. }
  683. else
  684. {
  685. return 0;
  686. }
  687. };
  688. BX.MaskedTextElement = function (params)
  689. {
  690. this.node = params.node;
  691. };
  692. BX.MaskedTextElement.prototype.val = function (value)
  693. {
  694. if (typeof value != 'undefined')
  695. {
  696. this.node.innerText = value;
  697. }
  698. return this.node.innerText;
  699. };
  700. BX.MaskedTextElement.prototype.onChange = function (e)
  701. {
  702. };
  703. BX.MaskedTextElement.prototype.setCaretPosition = function (pos)
  704. {
  705. };
  706. BX.MaskedTextElement.prototype.getSelectionStart = function ()
  707. {
  708. return 0;
  709. };
  710. BX.MaskedTextElement.prototype.getSelectionEnd = function ()
  711. {
  712. return 0;
  713. };
  714. })();