UNPKG

45 kBJavaScriptView Raw
1/*!
2 * react-maskedinput 3.2.1 - https://github.com/insin/react-maskedinput
3 * MIT Licensed
4 */
5(function webpackUniversalModuleDefinition(root, factory) {
6 if(typeof exports === 'object' && typeof module === 'object')
7 module.exports = factory(require("react"));
8 else if(typeof define === 'function' && define.amd)
9 define(["react"], factory);
10 else if(typeof exports === 'object')
11 exports["MaskedInput"] = factory(require("react"));
12 else
13 root["MaskedInput"] = factory(root["React"]);
14})(this, function(__WEBPACK_EXTERNAL_MODULE_12__) {
15return /******/ (function(modules) { // webpackBootstrap
16/******/ // The module cache
17/******/ var installedModules = {};
18
19/******/ // The require function
20/******/ function __webpack_require__(moduleId) {
21
22/******/ // Check if module is in cache
23/******/ if(installedModules[moduleId])
24/******/ return installedModules[moduleId].exports;
25
26/******/ // Create a new module (and put it into the cache)
27/******/ var module = installedModules[moduleId] = {
28/******/ exports: {},
29/******/ id: moduleId,
30/******/ loaded: false
31/******/ };
32
33/******/ // Execute the module function
34/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
35
36/******/ // Flag the module as loaded
37/******/ module.loaded = true;
38
39/******/ // Return the exports of the module
40/******/ return module.exports;
41/******/ }
42
43
44/******/ // expose the modules object (__webpack_modules__)
45/******/ __webpack_require__.m = modules;
46
47/******/ // expose the module cache
48/******/ __webpack_require__.c = installedModules;
49
50/******/ // __webpack_public_path__
51/******/ __webpack_require__.p = "";
52
53/******/ // Load entry module and return exports
54/******/ return __webpack_require__(0);
55/******/ })
56/************************************************************************/
57/******/ ([
58/* 0 */
59/***/ function(module, exports, __webpack_require__) {
60
61 'use strict';
62
63 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
64
65 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
66
67 var React = __webpack_require__(12);
68
69 var _require = __webpack_require__(9);
70
71 var getSelection = _require.getSelection;
72 var setSelection = _require.setSelection;
73
74 var InputMask = __webpack_require__(7);
75
76 var KEYCODE_Z = 90;
77 var KEYCODE_Y = 89;
78
79 function isUndo(e) {
80 return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z);
81 }
82
83 function isRedo(e) {
84 return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y);
85 }
86
87 var MaskedInput = React.createClass({
88 displayName: 'MaskedInput',
89
90 propTypes: {
91 mask: React.PropTypes.string.isRequired,
92
93 formatCharacters: React.PropTypes.object,
94 placeholderChar: React.PropTypes.string
95 },
96
97 getDefaultProps: function getDefaultProps() {
98 return {
99 value: ''
100 };
101 },
102
103 componentWillMount: function componentWillMount() {
104 var options = {
105 pattern: this.props.mask,
106 value: this.props.value,
107 formatCharacters: this.props.formatCharacters
108 };
109 if (this.props.placeholderChar) {
110 options.placeholderChar = this.props.placeholderChar;
111 }
112 this.mask = new InputMask(options);
113 },
114
115 componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
116 if (this.props.value !== nextProps.value) {
117 this.mask.setValue(nextProps.value);
118 }
119 if (this.props.mask !== nextProps.mask) {
120 this.mask.setPattern(nextProps.mask, { value: this.mask.getRawValue() });
121 }
122 },
123
124 componentWillUpdate: function componentWillUpdate(nextProps, nextState) {
125 if (nextProps.mask !== this.props.mask) {
126 this._updatePattern(nextProps);
127 }
128 },
129
130 componentDidUpdate: function componentDidUpdate(prevProps) {
131 if (prevProps.mask !== this.props.mask && this.mask.selection.start) {
132 this._updateInputSelection();
133 }
134 },
135
136 _updatePattern: function _updatePattern(props) {
137 this.mask.setPattern(props.mask, {
138 value: this.mask.getRawValue(),
139 selection: getSelection(this.input)
140 });
141 },
142
143 _updateMaskSelection: function _updateMaskSelection() {
144 this.mask.selection = getSelection(this.input);
145 },
146
147 _updateInputSelection: function _updateInputSelection() {
148 setSelection(this.input, this.mask.selection);
149 },
150
151 _onChange: function _onChange(e) {
152 // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)
153
154 var maskValue = this.mask.getValue();
155 if (e.target.value !== maskValue) {
156 // Cut or delete operations will have shortened the value
157 if (e.target.value.length < maskValue.length) {
158 var sizeDiff = maskValue.length - e.target.value.length;
159 this._updateMaskSelection();
160 this.mask.selection.end = this.mask.selection.start + sizeDiff;
161 this.mask.backspace();
162 }
163 var value = this._getDisplayValue();
164 e.target.value = value;
165 if (value) {
166 this._updateInputSelection();
167 }
168 }
169 if (this.props.onChange) {
170 this.props.onChange(e);
171 }
172 },
173
174 _onKeyDown: function _onKeyDown(e) {
175 // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
176
177 if (isUndo(e)) {
178 e.preventDefault();
179 if (this.mask.undo()) {
180 e.target.value = this._getDisplayValue();
181 this._updateInputSelection();
182 if (this.props.onChange) {
183 this.props.onChange(e);
184 }
185 }
186 return;
187 } else if (isRedo(e)) {
188 e.preventDefault();
189 if (this.mask.redo()) {
190 e.target.value = this._getDisplayValue();
191 this._updateInputSelection();
192 if (this.props.onChange) {
193 this.props.onChange(e);
194 }
195 }
196 return;
197 }
198
199 if (e.key === 'Backspace') {
200 e.preventDefault();
201 this._updateMaskSelection();
202 if (this.mask.backspace()) {
203 var value = this._getDisplayValue();
204 e.target.value = value;
205 if (value) {
206 this._updateInputSelection();
207 }
208 if (this.props.onChange) {
209 this.props.onChange(e);
210 }
211 }
212 }
213 },
214
215 _onKeyPress: function _onKeyPress(e) {
216 // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
217
218 // Ignore modified key presses
219 // Ignore enter key to allow form submission
220 if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') {
221 return;
222 }
223
224 e.preventDefault();
225 this._updateMaskSelection();
226 if (this.mask.input(e.key)) {
227 e.target.value = this.mask.getValue();
228 this._updateInputSelection();
229 if (this.props.onChange) {
230 this.props.onChange(e);
231 }
232 }
233 },
234
235 _onPaste: function _onPaste(e) {
236 // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)
237
238 e.preventDefault();
239 this._updateMaskSelection();
240 // getData value needed for IE also works in FF & Chrome
241 if (this.mask.paste(e.clipboardData.getData('Text'))) {
242 e.target.value = this.mask.getValue();
243 // Timeout needed for IE
244 setTimeout(this._updateInputSelection, 0);
245 if (this.props.onChange) {
246 this.props.onChange(e);
247 }
248 }
249 },
250
251 _getDisplayValue: function _getDisplayValue() {
252 var value = this.mask.getValue();
253 return value === this.mask.emptyValue ? '' : value;
254 },
255
256 focus: function focus() {
257 this.input.focus();
258 },
259
260 blur: function blur() {
261 this.input.blur();
262 },
263
264 render: function render() {
265 var _this = this;
266
267 var _props = this.props;
268 var mask = _props.mask;
269 var formatCharacters = _props.formatCharacters;
270 var size = _props.size;
271 var placeholder = _props.placeholder;
272 var placeholderChar = _props.placeholderChar;
273
274 var props = _objectWithoutProperties(_props, ['mask', 'formatCharacters', 'size', 'placeholder', 'placeholderChar']);
275
276 var patternLength = this.mask.pattern.length;
277 return React.createElement('input', _extends({}, props, {
278 ref: function (r) {
279 return _this.input = r;
280 },
281 maxLength: patternLength,
282 onChange: this._onChange,
283 onKeyDown: this._onKeyDown,
284 onKeyPress: this._onKeyPress,
285 onPaste: this._onPaste,
286 placeholder: placeholder || this.mask.emptyValue,
287 size: size || patternLength,
288 value: this._getDisplayValue()
289 }));
290 }
291 });
292
293 module.exports = MaskedInput;
294
295/***/ },
296/* 1 */
297/***/ function(module, exports) {
298
299 /**
300 * Copyright (c) 2013-present, Facebook, Inc.
301 * All rights reserved.
302 *
303 * This source code is licensed under the BSD-style license found in the
304 * LICENSE file in the root directory of this source tree. An additional grant
305 * of patent rights can be found in the PATENTS file in the same directory.
306 *
307 */
308
309 'use strict';
310
311 var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
312
313 /**
314 * Simple, lightweight module assisting with the detection and context of
315 * Worker. Helps avoid circular dependencies and allows code to reason about
316 * whether or not they are in a Worker, even if they never include the main
317 * `ReactWorker` dependency.
318 */
319 var ExecutionEnvironment = {
320
321 canUseDOM: canUseDOM,
322
323 canUseWorkers: typeof Worker !== 'undefined',
324
325 canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),
326
327 canUseViewport: canUseDOM && !!window.screen,
328
329 isInWorker: !canUseDOM // For now, this is true - might change in the future.
330
331 };
332
333 module.exports = ExecutionEnvironment;
334
335/***/ },
336/* 2 */
337/***/ function(module, exports, __webpack_require__) {
338
339 'use strict';
340
341 /**
342 * Copyright (c) 2013-present, Facebook, Inc.
343 * All rights reserved.
344 *
345 * This source code is licensed under the BSD-style license found in the
346 * LICENSE file in the root directory of this source tree. An additional grant
347 * of patent rights can be found in the PATENTS file in the same directory.
348 *
349 *
350 */
351
352 var isTextNode = __webpack_require__(6);
353
354 /*eslint-disable no-bitwise */
355
356 /**
357 * Checks if a given DOM node contains or is another DOM node.
358 */
359 function containsNode(outerNode, innerNode) {
360 if (!outerNode || !innerNode) {
361 return false;
362 } else if (outerNode === innerNode) {
363 return true;
364 } else if (isTextNode(outerNode)) {
365 return false;
366 } else if (isTextNode(innerNode)) {
367 return containsNode(outerNode, innerNode.parentNode);
368 } else if ('contains' in outerNode) {
369 return outerNode.contains(innerNode);
370 } else if (outerNode.compareDocumentPosition) {
371 return !!(outerNode.compareDocumentPosition(innerNode) & 16);
372 } else {
373 return false;
374 }
375 }
376
377 module.exports = containsNode;
378
379/***/ },
380/* 3 */
381/***/ function(module, exports) {
382
383 /**
384 * Copyright (c) 2013-present, Facebook, Inc.
385 * All rights reserved.
386 *
387 * This source code is licensed under the BSD-style license found in the
388 * LICENSE file in the root directory of this source tree. An additional grant
389 * of patent rights can be found in the PATENTS file in the same directory.
390 *
391 */
392
393 'use strict';
394
395 /**
396 * @param {DOMElement} node input/textarea to focus
397 */
398
399 function focusNode(node) {
400 // IE8 can throw "Can't move focus to the control because it is invisible,
401 // not enabled, or of a type that does not accept the focus." for all kinds of
402 // reasons that are too expensive and fragile to test.
403 try {
404 node.focus();
405 } catch (e) {}
406 }
407
408 module.exports = focusNode;
409
410/***/ },
411/* 4 */
412/***/ function(module, exports) {
413
414 'use strict';
415
416 /**
417 * Copyright (c) 2013-present, Facebook, Inc.
418 * All rights reserved.
419 *
420 * This source code is licensed under the BSD-style license found in the
421 * LICENSE file in the root directory of this source tree. An additional grant
422 * of patent rights can be found in the PATENTS file in the same directory.
423 *
424 * @typechecks
425 */
426
427 /* eslint-disable fb-www/typeof-undefined */
428
429 /**
430 * Same as document.activeElement but wraps in a try-catch block. In IE it is
431 * not safe to call document.activeElement if there is nothing focused.
432 *
433 * The activeElement will be null only if the document or document body is not
434 * yet defined.
435 */
436 function getActiveElement() /*?DOMElement*/{
437 if (typeof document === 'undefined') {
438 return null;
439 }
440 try {
441 return document.activeElement || document.body;
442 } catch (e) {
443 return document.body;
444 }
445 }
446
447 module.exports = getActiveElement;
448
449/***/ },
450/* 5 */
451/***/ function(module, exports) {
452
453 'use strict';
454
455 /**
456 * Copyright (c) 2013-present, Facebook, Inc.
457 * All rights reserved.
458 *
459 * This source code is licensed under the BSD-style license found in the
460 * LICENSE file in the root directory of this source tree. An additional grant
461 * of patent rights can be found in the PATENTS file in the same directory.
462 *
463 * @typechecks
464 */
465
466 /**
467 * @param {*} object The object to check.
468 * @return {boolean} Whether or not the object is a DOM node.
469 */
470 function isNode(object) {
471 return !!(object && (typeof Node === 'function' ? object instanceof Node : typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'));
472 }
473
474 module.exports = isNode;
475
476/***/ },
477/* 6 */
478/***/ function(module, exports, __webpack_require__) {
479
480 'use strict';
481
482 /**
483 * Copyright (c) 2013-present, Facebook, Inc.
484 * All rights reserved.
485 *
486 * This source code is licensed under the BSD-style license found in the
487 * LICENSE file in the root directory of this source tree. An additional grant
488 * of patent rights can be found in the PATENTS file in the same directory.
489 *
490 * @typechecks
491 */
492
493 var isNode = __webpack_require__(5);
494
495 /**
496 * @param {*} object The object to check.
497 * @return {boolean} Whether or not the object is a DOM text node.
498 */
499 function isTextNode(object) {
500 return isNode(object) && object.nodeType == 3;
501 }
502
503 module.exports = isTextNode;
504
505/***/ },
506/* 7 */
507/***/ function(module, exports) {
508
509 'use strict'
510
511 function extend(dest, src) {
512 if (src) {
513 var props = Object.keys(src)
514 for (var i = 0, l = props.length; i < l ; i++) {
515 dest[props[i]] = src[props[i]]
516 }
517 }
518 return dest
519 }
520
521 function copy(obj) {
522 return extend({}, obj)
523 }
524
525 /**
526 * Merge an object defining format characters into the defaults.
527 * Passing null/undefined for en existing format character removes it.
528 * Passing a definition for an existing format character overrides it.
529 * @param {?Object} formatCharacters.
530 */
531 function mergeFormatCharacters(formatCharacters) {
532 var merged = copy(DEFAULT_FORMAT_CHARACTERS)
533 if (formatCharacters) {
534 var chars = Object.keys(formatCharacters)
535 for (var i = 0, l = chars.length; i < l ; i++) {
536 var char = chars[i]
537 if (formatCharacters[char] == null) {
538 delete merged[char]
539 }
540 else {
541 merged[char] = formatCharacters[char]
542 }
543 }
544 }
545 return merged
546 }
547
548 var ESCAPE_CHAR = '\\'
549
550 var DIGIT_RE = /^\d$/
551 var LETTER_RE = /^[A-Za-z]$/
552 var ALPHANNUMERIC_RE = /^[\dA-Za-z]$/
553
554 var DEFAULT_PLACEHOLDER_CHAR = '_'
555 var DEFAULT_FORMAT_CHARACTERS = {
556 '*': {
557 validate: function(char) { return ALPHANNUMERIC_RE.test(char) }
558 },
559 '1': {
560 validate: function(char) { return DIGIT_RE.test(char) }
561 },
562 'a': {
563 validate: function(char) { return LETTER_RE.test(char) }
564 },
565 'A': {
566 validate: function(char) { return LETTER_RE.test(char) },
567 transform: function(char) { return char.toUpperCase() }
568 },
569 '#': {
570 validate: function(char) { return ALPHANNUMERIC_RE.test(char) },
571 transform: function(char) { return char.toUpperCase() }
572 }
573 }
574
575 /**
576 * @param {string} source
577 * @patam {?Object} formatCharacters
578 */
579 function Pattern(source, formatCharacters, placeholderChar) {
580 if (!(this instanceof Pattern)) {
581 return new Pattern(source, formatCharacters, placeholderChar)
582 }
583
584 /** Placeholder character */
585 this.placeholderChar = placeholderChar || DEFAULT_PLACEHOLDER_CHAR
586 /** Format character definitions. */
587 this.formatCharacters = formatCharacters || DEFAULT_FORMAT_CHARACTERS
588 /** Pattern definition string with escape characters. */
589 this.source = source
590 /** Pattern characters after escape characters have been processed. */
591 this.pattern = []
592 /** Length of the pattern after escape characters have been processed. */
593 this.length = 0
594 /** Index of the first editable character. */
595 this.firstEditableIndex = null
596 /** Index of the last editable character. */
597 this.lastEditableIndex = null
598
599 /** Lookup for indices of editable characters in the pattern. */
600 this._editableIndices = {}
601
602 this._parse()
603 }
604
605 Pattern.prototype._parse = function parse() {
606 var sourceChars = this.source.split('')
607 var patternIndex = 0
608 var pattern = []
609
610 for (var i = 0, l = sourceChars.length; i < l; i++) {
611 var char = sourceChars[i]
612 if (char === ESCAPE_CHAR) {
613 if (i === l - 1) {
614 throw new Error('InputMask: pattern ends with a raw ' + ESCAPE_CHAR)
615 }
616 char = sourceChars[++i]
617 }
618 else if (char in this.formatCharacters) {
619 if (this.firstEditableIndex === null) {
620 this.firstEditableIndex = patternIndex
621 }
622 this.lastEditableIndex = patternIndex
623 this._editableIndices[patternIndex] = true
624 }
625
626 pattern.push(char)
627 patternIndex++
628 }
629
630 if (this.firstEditableIndex === null) {
631 throw new Error(
632 'InputMask: pattern "' + this.source + '" does not contain any editable characters.'
633 )
634 }
635
636 this.pattern = pattern
637 this.length = pattern.length
638 }
639
640 /**
641 * @param {Array<string>} value
642 * @return {Array<string>}
643 */
644 Pattern.prototype.formatValue = function format(value) {
645 var valueBuffer = new Array(this.length)
646 var valueIndex = 0
647
648 for (var i = 0, l = this.length; i < l ; i++) {
649 if (this.isEditableIndex(i)) {
650 valueBuffer[i] = (value.length > valueIndex && this.isValidAtIndex(value[valueIndex], i)
651 ? this.transform(value[valueIndex], i)
652 : this.placeholderChar)
653 valueIndex++
654 }
655 else {
656 valueBuffer[i] = this.pattern[i]
657 // Also allow the value to contain static values from the pattern by
658 // advancing its index.
659 if (value.length > valueIndex && value[valueIndex] === this.pattern[i]) {
660 valueIndex++
661 }
662 }
663 }
664
665 return valueBuffer
666 }
667
668 /**
669 * @param {number} index
670 * @return {boolean}
671 */
672 Pattern.prototype.isEditableIndex = function isEditableIndex(index) {
673 return !!this._editableIndices[index]
674 }
675
676 /**
677 * @param {string} char
678 * @param {number} index
679 * @return {boolean}
680 */
681 Pattern.prototype.isValidAtIndex = function isValidAtIndex(char, index) {
682 return this.formatCharacters[this.pattern[index]].validate(char)
683 }
684
685 Pattern.prototype.transform = function transform(char, index) {
686 var format = this.formatCharacters[this.pattern[index]]
687 return typeof format.transform == 'function' ? format.transform(char) : char
688 }
689
690 function InputMask(options) {
691 if (!(this instanceof InputMask)) { return new InputMask(options) }
692
693 options = extend({
694 formatCharacters: null,
695 pattern: null,
696 placeholderChar: DEFAULT_PLACEHOLDER_CHAR,
697 selection: {start: 0, end: 0},
698 value: ''
699 }, options)
700
701 if (options.pattern == null) {
702 throw new Error('InputMask: you must provide a pattern.')
703 }
704
705 if (options.placeholderChar.length !== 1) {
706 throw new Error('InputMask: placeholderChar should be a single character.')
707 }
708
709 this.placeholderChar = options.placeholderChar
710 this.formatCharacters = mergeFormatCharacters(options.formatCharacters)
711 this.setPattern(options.pattern, {
712 value: options.value,
713 selection: options.selection
714 })
715 }
716
717 // Editing
718
719 /**
720 * Applies a single character of input based on the current selection.
721 * @param {string} char
722 * @return {boolean} true if a change has been made to value or selection as a
723 * result of the input, false otherwise.
724 */
725 InputMask.prototype.input = function input(char) {
726 // Ignore additional input if the cursor's at the end of the pattern
727 if (this.selection.start === this.selection.end &&
728 this.selection.start === this.pattern.length) {
729 return false
730 }
731
732 var selectionBefore = copy(this.selection)
733 var valueBefore = this.getValue()
734
735 var inputIndex = this.selection.start
736
737 // If the cursor or selection is prior to the first editable character, make
738 // sure any input given is applied to it.
739 if (inputIndex < this.pattern.firstEditableIndex) {
740 inputIndex = this.pattern.firstEditableIndex
741 }
742
743 // Bail out or add the character to input
744 if (this.pattern.isEditableIndex(inputIndex)) {
745 if (!this.pattern.isValidAtIndex(char, inputIndex)) {
746 return false
747 }
748 this.value[inputIndex] = this.pattern.transform(char, inputIndex)
749 }
750
751 // If multiple characters were selected, blank the remainder out based on the
752 // pattern.
753 var end = this.selection.end - 1
754 while (end > inputIndex) {
755 if (this.pattern.isEditableIndex(end)) {
756 this.value[end] = this.placeholderChar
757 }
758 end--
759 }
760
761 // Advance the cursor to the next character
762 this.selection.start = this.selection.end = inputIndex + 1
763
764 // Skip over any subsequent static characters
765 while (this.pattern.length > this.selection.start &&
766 !this.pattern.isEditableIndex(this.selection.start)) {
767 this.selection.start++
768 this.selection.end++
769 }
770
771 // History
772 if (this._historyIndex != null) {
773 // Took more input after undoing, so blow any subsequent history away
774 console.log('splice(', this._historyIndex, this._history.length - this._historyIndex, ')')
775 this._history.splice(this._historyIndex, this._history.length - this._historyIndex)
776 this._historyIndex = null
777 }
778 if (this._lastOp !== 'input' ||
779 selectionBefore.start !== selectionBefore.end ||
780 this._lastSelection !== null && selectionBefore.start !== this._lastSelection.start) {
781 this._history.push({value: valueBefore, selection: selectionBefore, lastOp: this._lastOp})
782 }
783 this._lastOp = 'input'
784 this._lastSelection = copy(this.selection)
785
786 return true
787 }
788
789 /**
790 * Attempts to delete from the value based on the current cursor position or
791 * selection.
792 * @return {boolean} true if the value or selection changed as the result of
793 * backspacing, false otherwise.
794 */
795 InputMask.prototype.backspace = function backspace() {
796 // If the cursor is at the start there's nothing to do
797 if (this.selection.start === 0 && this.selection.end === 0) {
798 return false
799 }
800
801 var selectionBefore = copy(this.selection)
802 var valueBefore = this.getValue()
803
804 // No range selected - work on the character preceding the cursor
805 if (this.selection.start === this.selection.end) {
806 if (this.pattern.isEditableIndex(this.selection.start - 1)) {
807 this.value[this.selection.start - 1] = this.placeholderChar
808 }
809 this.selection.start--
810 this.selection.end--
811 }
812 // Range selected - delete characters and leave the cursor at the start of the selection
813 else {
814 var end = this.selection.end - 1
815 while (end >= this.selection.start) {
816 if (this.pattern.isEditableIndex(end)) {
817 this.value[end] = this.placeholderChar
818 }
819 end--
820 }
821 this.selection.end = this.selection.start
822 }
823
824 // History
825 if (this._historyIndex != null) {
826 // Took more input after undoing, so blow any subsequent history away
827 this._history.splice(this._historyIndex, this._history.length - this._historyIndex)
828 }
829 if (this._lastOp !== 'backspace' ||
830 selectionBefore.start !== selectionBefore.end ||
831 this._lastSelection !== null && selectionBefore.start !== this._lastSelection.start) {
832 this._history.push({value: valueBefore, selection: selectionBefore, lastOp: this._lastOp})
833 }
834 this._lastOp = 'backspace'
835 this._lastSelection = copy(this.selection)
836
837 return true
838 }
839
840 /**
841 * Attempts to paste a string of input at the current cursor position or over
842 * the top of the current selection.
843 * Invalid content at any position will cause the paste to be rejected, and it
844 * may contain static parts of the mask's pattern.
845 * @param {string} input
846 * @return {boolean} true if the paste was successful, false otherwise.
847 */
848 InputMask.prototype.paste = function paste(input) {
849 // This is necessary because we're just calling input() with each character
850 // and rolling back if any were invalid, rather than checking up-front.
851 var initialState = {
852 value: this.value.slice(),
853 selection: copy(this.selection),
854 _lastOp: this._lastOp,
855 _history: this._history.slice(),
856 _historyIndex: this._historyIndex,
857 _lastSelection: copy(this._lastSelection)
858 }
859
860 // If there are static characters at the start of the pattern and the cursor
861 // or selection is within them, the static characters must match for a valid
862 // paste.
863 if (this.selection.start < this.pattern.firstEditableIndex) {
864 for (var i = 0, l = this.pattern.firstEditableIndex - this.selection.start; i < l; i++) {
865 if (input.charAt(i) !== this.pattern.pattern[i]) {
866 return false
867 }
868 }
869
870 // Continue as if the selection and input started from the editable part of
871 // the pattern.
872 input = input.substring(this.pattern.firstEditableIndex - this.selection.start)
873 this.selection.start = this.pattern.firstEditableIndex
874 }
875
876 for (i = 0, l = input.length;
877 i < l && this.selection.start <= this.pattern.lastEditableIndex;
878 i++) {
879 var valid = this.input(input.charAt(i))
880 // Allow static parts of the pattern to appear in pasted input - they will
881 // already have been stepped over by input(), so verify that the value
882 // deemed invalid by input() was the expected static character.
883 if (!valid) {
884 if (this.selection.start > 0) {
885 // XXX This only allows for one static character to be skipped
886 var patternIndex = this.selection.start - 1
887 if (!this.pattern.isEditableIndex(patternIndex) &&
888 input.charAt(i) === this.pattern.pattern[patternIndex]) {
889 continue
890 }
891 }
892 extend(this, initialState)
893 return false
894 }
895 }
896
897 return true
898 }
899
900 // History
901
902 InputMask.prototype.undo = function undo() {
903 // If there is no history, or nothing more on the history stack, we can't undo
904 if (this._history.length === 0 || this._historyIndex === 0) {
905 return false
906 }
907
908 var historyItem
909 if (this._historyIndex == null) {
910 // Not currently undoing, set up the initial history index
911 this._historyIndex = this._history.length - 1
912 historyItem = this._history[this._historyIndex]
913 // Add a new history entry if anything has changed since the last one, so we
914 // can redo back to the initial state we started undoing from.
915 var value = this.getValue()
916 if (historyItem.value !== value ||
917 historyItem.selection.start !== this.selection.start ||
918 historyItem.selection.end !== this.selection.end) {
919 this._history.push({value: value, selection: copy(this.selection), lastOp: this._lastOp, startUndo: true})
920 }
921 }
922 else {
923 historyItem = this._history[--this._historyIndex]
924 }
925
926 this.value = historyItem.value.split('')
927 this.selection = historyItem.selection
928 this._lastOp = historyItem.lastOp
929 return true
930 }
931
932 InputMask.prototype.redo = function redo() {
933 if (this._history.length === 0 || this._historyIndex == null) {
934 return false
935 }
936 var historyItem = this._history[++this._historyIndex]
937 // If this is the last history item, we're done redoing
938 if (this._historyIndex === this._history.length - 1) {
939 this._historyIndex = null
940 // If the last history item was only added to start undoing, remove it
941 if (historyItem.startUndo) {
942 this._history.pop()
943 }
944 }
945 this.value = historyItem.value.split('')
946 this.selection = historyItem.selection
947 this._lastOp = historyItem.lastOp
948 return true
949 }
950
951 // Getters & setters
952
953 InputMask.prototype.setPattern = function setPattern(pattern, options) {
954 options = extend({
955 selection: {start: 0, end: 0},
956 value: ''
957 }, options)
958 this.pattern = new Pattern(pattern, this.formatCharacters, this.placeholderChar)
959 this.setValue(options.value)
960 this.emptyValue = this.pattern.formatValue([]).join('')
961 this.selection = options.selection
962 this._resetHistory()
963 }
964
965 InputMask.prototype.setSelection = function setSelection(selection) {
966 this.selection = copy(selection)
967 if (this.selection.start === this.selection.end) {
968 if (this.selection.start < this.pattern.firstEditableIndex) {
969 this.selection.start = this.selection.end = this.pattern.firstEditableIndex
970 return true
971 }
972 if (this.selection.end > this.pattern.lastEditableIndex + 1) {
973 this.selection.start = this.selection.end = this.pattern.lastEditableIndex + 1
974 return true
975 }
976 }
977 return false
978 }
979
980 InputMask.prototype.setValue = function setValue(value) {
981 if (value == null) {
982 value = ''
983 }
984 this.value = this.pattern.formatValue(value.split(''))
985 }
986
987 InputMask.prototype.getValue = function getValue() {
988 return this.value.join('')
989 }
990
991 InputMask.prototype.getRawValue = function getRawValue() {
992 var rawValue = []
993 for (var i = 0; i < this.value.length; i++) {
994 if (this.pattern._editableIndices[i] === true) {
995 rawValue.push(this.value[i])
996 }
997 }
998 return rawValue.join('')
999 }
1000
1001 InputMask.prototype._resetHistory = function _resetHistory() {
1002 this._history = []
1003 this._historyIndex = null
1004 this._lastOp = null
1005 this._lastSelection = copy(this.selection)
1006 }
1007
1008 InputMask.Pattern = Pattern
1009
1010 module.exports = InputMask
1011
1012
1013/***/ },
1014/* 8 */
1015/***/ function(module, exports, __webpack_require__) {
1016
1017 /**
1018 * Copyright 2013-present, Facebook, Inc.
1019 * All rights reserved.
1020 *
1021 * This source code is licensed under the BSD-style license found in the
1022 * LICENSE file in the root directory of this source tree. An additional grant
1023 * of patent rights can be found in the PATENTS file in the same directory.
1024 *
1025 * @providesModule ReactDOMSelection
1026 */
1027
1028 'use strict';
1029
1030 var ExecutionEnvironment = __webpack_require__(1);
1031
1032 var getNodeForCharacterOffset = __webpack_require__(10);
1033 var getTextContentAccessor = __webpack_require__(11);
1034
1035 /**
1036 * While `isCollapsed` is available on the Selection object and `collapsed`
1037 * is available on the Range object, IE11 sometimes gets them wrong.
1038 * If the anchor/focus nodes and offsets are the same, the range is collapsed.
1039 */
1040 function isCollapsed(anchorNode, anchorOffset, focusNode, focusOffset) {
1041 return anchorNode === focusNode && anchorOffset === focusOffset;
1042 }
1043
1044 /**
1045 * Get the appropriate anchor and focus node/offset pairs for IE.
1046 *
1047 * The catch here is that IE's selection API doesn't provide information
1048 * about whether the selection is forward or backward, so we have to
1049 * behave as though it's always forward.
1050 *
1051 * IE text differs from modern selection in that it behaves as though
1052 * block elements end with a new line. This means character offsets will
1053 * differ between the two APIs.
1054 *
1055 * @param {DOMElement} node
1056 * @return {object}
1057 */
1058 function getIEOffsets(node) {
1059 var selection = document.selection;
1060 var selectedRange = selection.createRange();
1061 var selectedLength = selectedRange.text.length;
1062
1063 // Duplicate selection so we can move range without breaking user selection.
1064 var fromStart = selectedRange.duplicate();
1065 fromStart.moveToElementText(node);
1066 fromStart.setEndPoint('EndToStart', selectedRange);
1067
1068 var startOffset = fromStart.text.length;
1069 var endOffset = startOffset + selectedLength;
1070
1071 return {
1072 start: startOffset,
1073 end: endOffset
1074 };
1075 }
1076
1077 /**
1078 * @param {DOMElement} node
1079 * @return {?object}
1080 */
1081 function getModernOffsets(node) {
1082 var selection = window.getSelection && window.getSelection();
1083
1084 if (!selection || selection.rangeCount === 0) {
1085 return null;
1086 }
1087
1088 var anchorNode = selection.anchorNode;
1089 var anchorOffset = selection.anchorOffset;
1090 var focusNode = selection.focusNode;
1091 var focusOffset = selection.focusOffset;
1092
1093 var currentRange = selection.getRangeAt(0);
1094
1095 // In Firefox, range.startContainer and range.endContainer can be "anonymous
1096 // divs", e.g. the up/down buttons on an <input type="number">. Anonymous
1097 // divs do not seem to expose properties, triggering a "Permission denied
1098 // error" if any of its properties are accessed. The only seemingly possible
1099 // way to avoid erroring is to access a property that typically works for
1100 // non-anonymous divs and catch any error that may otherwise arise. See
1101 // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
1102 try {
1103 /* eslint-disable no-unused-expressions */
1104 currentRange.startContainer.nodeType;
1105 currentRange.endContainer.nodeType;
1106 /* eslint-enable no-unused-expressions */
1107 } catch (e) {
1108 return null;
1109 }
1110
1111 // If the node and offset values are the same, the selection is collapsed.
1112 // `Selection.isCollapsed` is available natively, but IE sometimes gets
1113 // this value wrong.
1114 var isSelectionCollapsed = isCollapsed(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);
1115
1116 var rangeLength = isSelectionCollapsed ? 0 : currentRange.toString().length;
1117
1118 var tempRange = currentRange.cloneRange();
1119 tempRange.selectNodeContents(node);
1120 tempRange.setEnd(currentRange.startContainer, currentRange.startOffset);
1121
1122 var isTempRangeCollapsed = isCollapsed(tempRange.startContainer, tempRange.startOffset, tempRange.endContainer, tempRange.endOffset);
1123
1124 var start = isTempRangeCollapsed ? 0 : tempRange.toString().length;
1125 var end = start + rangeLength;
1126
1127 // Detect whether the selection is backward.
1128 var detectionRange = document.createRange();
1129 detectionRange.setStart(anchorNode, anchorOffset);
1130 detectionRange.setEnd(focusNode, focusOffset);
1131 var isBackward = detectionRange.collapsed;
1132
1133 return {
1134 start: isBackward ? end : start,
1135 end: isBackward ? start : end
1136 };
1137 }
1138
1139 /**
1140 * @param {DOMElement|DOMTextNode} node
1141 * @param {object} offsets
1142 */
1143 function setIEOffsets(node, offsets) {
1144 var range = document.selection.createRange().duplicate();
1145 var start, end;
1146
1147 if (offsets.end === undefined) {
1148 start = offsets.start;
1149 end = start;
1150 } else if (offsets.start > offsets.end) {
1151 start = offsets.end;
1152 end = offsets.start;
1153 } else {
1154 start = offsets.start;
1155 end = offsets.end;
1156 }
1157
1158 range.moveToElementText(node);
1159 range.moveStart('character', start);
1160 range.setEndPoint('EndToStart', range);
1161 range.moveEnd('character', end - start);
1162 range.select();
1163 }
1164
1165 /**
1166 * In modern non-IE browsers, we can support both forward and backward
1167 * selections.
1168 *
1169 * Note: IE10+ supports the Selection object, but it does not support
1170 * the `extend` method, which means that even in modern IE, it's not possible
1171 * to programmatically create a backward selection. Thus, for all IE
1172 * versions, we use the old IE API to create our selections.
1173 *
1174 * @param {DOMElement|DOMTextNode} node
1175 * @param {object} offsets
1176 */
1177 function setModernOffsets(node, offsets) {
1178 if (!window.getSelection) {
1179 return;
1180 }
1181
1182 var selection = window.getSelection();
1183 var length = node[getTextContentAccessor()].length;
1184 var start = Math.min(offsets.start, length);
1185 var end = offsets.end === undefined ? start : Math.min(offsets.end, length);
1186
1187 // IE 11 uses modern selection, but doesn't support the extend method.
1188 // Flip backward selections, so we can set with a single range.
1189 if (!selection.extend && start > end) {
1190 var temp = end;
1191 end = start;
1192 start = temp;
1193 }
1194
1195 var startMarker = getNodeForCharacterOffset(node, start);
1196 var endMarker = getNodeForCharacterOffset(node, end);
1197
1198 if (startMarker && endMarker) {
1199 var range = document.createRange();
1200 range.setStart(startMarker.node, startMarker.offset);
1201 selection.removeAllRanges();
1202
1203 if (start > end) {
1204 selection.addRange(range);
1205 selection.extend(endMarker.node, endMarker.offset);
1206 } else {
1207 range.setEnd(endMarker.node, endMarker.offset);
1208 selection.addRange(range);
1209 }
1210 }
1211 }
1212
1213 var useIEOffsets = ExecutionEnvironment.canUseDOM && 'selection' in document && !('getSelection' in window);
1214
1215 var ReactDOMSelection = {
1216 /**
1217 * @param {DOMElement} node
1218 */
1219 getOffsets: useIEOffsets ? getIEOffsets : getModernOffsets,
1220
1221 /**
1222 * @param {DOMElement|DOMTextNode} node
1223 * @param {object} offsets
1224 */
1225 setOffsets: useIEOffsets ? setIEOffsets : setModernOffsets
1226 };
1227
1228 module.exports = ReactDOMSelection;
1229
1230/***/ },
1231/* 9 */
1232/***/ function(module, exports, __webpack_require__) {
1233
1234 /**
1235 * Copyright 2013-present, Facebook, Inc.
1236 * All rights reserved.
1237 *
1238 * This source code is licensed under the BSD-style license found in the
1239 * LICENSE file in the root directory of this source tree. An additional grant
1240 * of patent rights can be found in the PATENTS file in the same directory.
1241 *
1242 * @providesModule ReactInputSelection
1243 */
1244
1245 'use strict';
1246
1247 var ReactDOMSelection = __webpack_require__(8);
1248
1249 var containsNode = __webpack_require__(2);
1250 var focusNode = __webpack_require__(3);
1251 var getActiveElement = __webpack_require__(4);
1252
1253 function isInDocument(node) {
1254 return containsNode(document.documentElement, node);
1255 }
1256
1257 /**
1258 * @ReactInputSelection: React input selection module. Based on Selection.js,
1259 * but modified to be suitable for react and has a couple of bug fixes (doesn't
1260 * assume buttons have range selections allowed).
1261 * Input selection module for React.
1262 */
1263 var ReactInputSelection = {
1264
1265 hasSelectionCapabilities: function (elem) {
1266 var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
1267 return nodeName && (nodeName === 'input' && elem.type === 'text' || nodeName === 'textarea' || elem.contentEditable === 'true');
1268 },
1269
1270 getSelectionInformation: function () {
1271 var focusedElem = getActiveElement();
1272 return {
1273 focusedElem: focusedElem,
1274 selectionRange: ReactInputSelection.hasSelectionCapabilities(focusedElem) ? ReactInputSelection.getSelection(focusedElem) : null
1275 };
1276 },
1277
1278 /**
1279 * @restoreSelection: If any selection information was potentially lost,
1280 * restore it. This is useful when performing operations that could remove dom
1281 * nodes and place them back in, resulting in focus being lost.
1282 */
1283 restoreSelection: function (priorSelectionInformation) {
1284 var curFocusedElem = getActiveElement();
1285 var priorFocusedElem = priorSelectionInformation.focusedElem;
1286 var priorSelectionRange = priorSelectionInformation.selectionRange;
1287 if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
1288 if (ReactInputSelection.hasSelectionCapabilities(priorFocusedElem)) {
1289 ReactInputSelection.setSelection(priorFocusedElem, priorSelectionRange);
1290 }
1291 focusNode(priorFocusedElem);
1292 }
1293 },
1294
1295 /**
1296 * @getSelection: Gets the selection bounds of a focused textarea, input or
1297 * contentEditable node.
1298 * -@input: Look up selection bounds of this input
1299 * -@return {start: selectionStart, end: selectionEnd}
1300 */
1301 getSelection: function (input) {
1302 var selection;
1303
1304 if ('selectionStart' in input) {
1305 // Modern browser with input or textarea.
1306 selection = {
1307 start: input.selectionStart,
1308 end: input.selectionEnd
1309 };
1310 } else if (document.selection && input.nodeName && input.nodeName.toLowerCase() === 'input') {
1311 // IE8 input.
1312 var range = document.selection.createRange();
1313 // There can only be one selection per document in IE, so it must
1314 // be in our element.
1315 if (range.parentElement() === input) {
1316 selection = {
1317 start: -range.moveStart('character', -input.value.length),
1318 end: -range.moveEnd('character', -input.value.length)
1319 };
1320 }
1321 } else {
1322 // Content editable or old IE textarea.
1323 selection = ReactDOMSelection.getOffsets(input);
1324 }
1325
1326 return selection || { start: 0, end: 0 };
1327 },
1328
1329 /**
1330 * @setSelection: Sets the selection bounds of a textarea or input and focuses
1331 * the input.
1332 * -@input Set selection bounds of this input or textarea
1333 * -@offsets Object of same form that is returned from get*
1334 */
1335 setSelection: function (input, offsets) {
1336 var start = offsets.start;
1337 var end = offsets.end;
1338 if (end === undefined) {
1339 end = start;
1340 }
1341
1342 if ('selectionStart' in input) {
1343 input.selectionStart = start;
1344 input.selectionEnd = Math.min(end, input.value.length);
1345 } else if (document.selection && input.nodeName && input.nodeName.toLowerCase() === 'input') {
1346 var range = input.createTextRange();
1347 range.collapse(true);
1348 range.moveStart('character', start);
1349 range.moveEnd('character', end - start);
1350 range.select();
1351 } else {
1352 ReactDOMSelection.setOffsets(input, offsets);
1353 }
1354 }
1355 };
1356
1357 module.exports = ReactInputSelection;
1358
1359/***/ },
1360/* 10 */
1361/***/ function(module, exports) {
1362
1363 /**
1364 * Copyright 2013-present, Facebook, Inc.
1365 * All rights reserved.
1366 *
1367 * This source code is licensed under the BSD-style license found in the
1368 * LICENSE file in the root directory of this source tree. An additional grant
1369 * of patent rights can be found in the PATENTS file in the same directory.
1370 *
1371 * @providesModule getNodeForCharacterOffset
1372 */
1373
1374 'use strict';
1375
1376 /**
1377 * Given any node return the first leaf node without children.
1378 *
1379 * @param {DOMElement|DOMTextNode} node
1380 * @return {DOMElement|DOMTextNode}
1381 */
1382
1383 function getLeafNode(node) {
1384 while (node && node.firstChild) {
1385 node = node.firstChild;
1386 }
1387 return node;
1388 }
1389
1390 /**
1391 * Get the next sibling within a container. This will walk up the
1392 * DOM if a node's siblings have been exhausted.
1393 *
1394 * @param {DOMElement|DOMTextNode} node
1395 * @return {?DOMElement|DOMTextNode}
1396 */
1397 function getSiblingNode(node) {
1398 while (node) {
1399 if (node.nextSibling) {
1400 return node.nextSibling;
1401 }
1402 node = node.parentNode;
1403 }
1404 }
1405
1406 /**
1407 * Get object describing the nodes which contain characters at offset.
1408 *
1409 * @param {DOMElement|DOMTextNode} root
1410 * @param {number} offset
1411 * @return {?object}
1412 */
1413 function getNodeForCharacterOffset(root, offset) {
1414 var node = getLeafNode(root);
1415 var nodeStart = 0;
1416 var nodeEnd = 0;
1417
1418 while (node) {
1419 if (node.nodeType === 3) {
1420 nodeEnd = nodeStart + node.textContent.length;
1421
1422 if (nodeStart <= offset && nodeEnd >= offset) {
1423 return {
1424 node: node,
1425 offset: offset - nodeStart
1426 };
1427 }
1428
1429 nodeStart = nodeEnd;
1430 }
1431
1432 node = getLeafNode(getSiblingNode(node));
1433 }
1434 }
1435
1436 module.exports = getNodeForCharacterOffset;
1437
1438/***/ },
1439/* 11 */
1440/***/ function(module, exports, __webpack_require__) {
1441
1442 /**
1443 * Copyright 2013-present, Facebook, Inc.
1444 * All rights reserved.
1445 *
1446 * This source code is licensed under the BSD-style license found in the
1447 * LICENSE file in the root directory of this source tree. An additional grant
1448 * of patent rights can be found in the PATENTS file in the same directory.
1449 *
1450 * @providesModule getTextContentAccessor
1451 */
1452
1453 'use strict';
1454
1455 var ExecutionEnvironment = __webpack_require__(1);
1456
1457 var contentKey = null;
1458
1459 /**
1460 * Gets the key used to access text content on a DOM node.
1461 *
1462 * @return {?string} Key used to access text content.
1463 * @internal
1464 */
1465 function getTextContentAccessor() {
1466 if (!contentKey && ExecutionEnvironment.canUseDOM) {
1467 // Prefer textContent to innerText because many browsers support both but
1468 // SVG <text> elements don't support innerText even when <div> does.
1469 contentKey = 'textContent' in document.documentElement ? 'textContent' : 'innerText';
1470 }
1471 return contentKey;
1472 }
1473
1474 module.exports = getTextContentAccessor;
1475
1476/***/ },
1477/* 12 */
1478/***/ function(module, exports) {
1479
1480 module.exports = __WEBPACK_EXTERNAL_MODULE_12__;
1481
1482/***/ }
1483/******/ ])
1484});
1485;
\No newline at end of file