'use strict';
var React = require('react'); // eslint-disable-line no-unused-vars
var CreateReactClass = require('create-react-class');
var NumeralFormatter = require('./shortcuts/NumeralFormatter');
var DateFormatter = require('./shortcuts/DateFormatter');
var TimeFormatter = require('./shortcuts/TimeFormatter');
var PhoneFormatter = require('./shortcuts/PhoneFormatter');
var CreditCardDetector = require('./shortcuts/CreditCardDetector');
var Util = require('./utils/Util');
var DefaultProperties = require('./common/DefaultProperties');
var cleaveReactClass = CreateReactClass({
componentDidMount: function () {
this.init();
},
componentDidUpdate: function (prevProps) {
var owner = this,
phoneRegionCode = (owner.props.options || {}).phoneRegionCode,
newValue = owner.props.value,
pps = owner.properties;
owner.updateRegisteredEvents(owner.props);
if (prevProps.value !== newValue && newValue !== undefined && newValue !== null) {
newValue = newValue.toString();
if (newValue !== owner.properties.result) {
owner.properties.initValue = newValue;
owner.onInput(newValue, true);
}
}
// update phone region code
const prevPhoneRegionCode = (prevProps.options || {}).phoneRegionCode;
if (prevPhoneRegionCode !== phoneRegionCode && phoneRegionCode && phoneRegionCode !== owner.properties.phoneRegionCode) {
owner.properties.phoneRegionCode = phoneRegionCode;
owner.initPhoneFormatter();
owner.onInput(owner.properties.result);
}
Util.setSelection(owner.element, owner.state.cursorPosition, pps.document);
},
updateRegisteredEvents: function (props) {
var owner = this,
{ onKeyDown, onChange, onFocus, onBlur, onInit } = owner.registeredEvents;
if (props.onInit && props.onInit !== onInit) owner.registeredEvents.onInit = props.onInit;
if (props.onChange && props.onChange !== onChange) owner.registeredEvents.onChange = props.onChange;
if (props.onFocus && props.onFocus !== onFocus) owner.registeredEvents.onFocus = props.onFocus;
if (props.onBlur && props.onBlur !== onBlur) owner.registeredEvents.onBlur = props.onBlur;
if (props.onKeyDown && props.onKeyDown !== onKeyDown) owner.registeredEvents.onKeyDown = props.onKeyDown;
},
getInitialState: function () {
var owner = this,
{ value, options, onKeyDown, onChange, onFocus, onBlur, onInit } = owner.props;
owner.registeredEvents = {
onInit: onInit || Util.noop,
onChange: onChange || Util.noop,
onFocus: onFocus || Util.noop,
onBlur: onBlur || Util.noop,
onKeyDown: onKeyDown || Util.noop
};
if (!options) {
options = {};
}
options.initValue = value;
owner.properties = DefaultProperties.assign({}, options);
return {
value: owner.properties.result,
cursorPosition: 0
};
},
init: function () {
var owner = this,
pps = owner.properties;
// so no need for this lib at all
if (!pps.numeral && !pps.phone && !pps.creditCard && !pps.time && !pps.date && (pps.blocksLength === 0 && !pps.prefix)) {
owner.onInput(pps.initValue);
owner.registeredEvents.onInit(owner);
return;
}
pps.maxLength = Util.getMaxLength(pps.blocks);
owner.isAndroid = Util.isAndroid();
owner.initPhoneFormatter();
owner.initDateFormatter();
owner.initTimeFormatter();
owner.initNumeralFormatter();
// avoid touch input field if value is null
// otherwise Firefox will add red box-shadow for
if (pps.initValue || (pps.prefix && !pps.noImmediatePrefix)) {
owner.onInput(pps.initValue);
}
owner.registeredEvents.onInit(owner);
},
initNumeralFormatter: function () {
var owner = this,
pps = owner.properties;
if (!pps.numeral) {
return;
}
pps.numeralFormatter = new NumeralFormatter(
pps.numeralDecimalMark,
pps.numeralIntegerScale,
pps.numeralDecimalScale,
pps.numeralThousandsGroupStyle,
pps.numeralPositiveOnly,
pps.stripLeadingZeroes,
pps.prefix,
pps.signBeforePrefix,
pps.tailPrefix,
pps.delimiter
);
},
initTimeFormatter: function () {
var owner = this,
pps = owner.properties;
if (!pps.time) {
return;
}
pps.timeFormatter = new TimeFormatter(pps.timePattern, pps.timeFormat);
pps.blocks = pps.timeFormatter.getBlocks();
pps.blocksLength = pps.blocks.length;
pps.maxLength = Util.getMaxLength(pps.blocks);
},
initDateFormatter: function () {
var owner = this,
pps = owner.properties;
if (!pps.date) {
return;
}
pps.dateFormatter = new DateFormatter(pps.datePattern, pps.dateMin, pps.dateMax);
pps.blocks = pps.dateFormatter.getBlocks();
pps.blocksLength = pps.blocks.length;
pps.maxLength = Util.getMaxLength(pps.blocks);
},
initPhoneFormatter: function () {
var owner = this,
pps = owner.properties;
if (!pps.phone) {
return;
}
// Cleave.AsYouTypeFormatter should be provided by
// external google closure lib
try {
pps.phoneFormatter = new PhoneFormatter(
new pps.root.Cleave.AsYouTypeFormatter(pps.phoneRegionCode),
pps.delimiter
);
} catch (ex) {
throw new Error('Please include phone-type-formatter.{country}.js lib');
}
},
setRawValue: function (value) {
var owner = this,
pps = owner.properties;
value = value !== undefined && value !== null ? value.toString() : '';
if (pps.numeral) {
value = value.replace('.', pps.numeralDecimalMark);
}
pps.postDelimiterBackspace = false;
owner.onChange({
target: {value: value},
// Methods to better resemble a SyntheticEvent
stopPropagation: Util.noop,
preventDefault: Util.noop,
persist: Util.noop
});
},
getRawValue: function () {
var owner = this, pps = owner.properties,
rawValue = pps.result;
if (pps.rawValueTrimPrefix) {
rawValue = Util.getPrefixStrippedValue(rawValue, pps.prefix, pps.prefixLength, pps.result, pps.delimiter, pps.delimiters, pps.noImmediatePrefix, pps.tailPrefix, pps.signBeforePrefix);
}
if (pps.numeral) {
rawValue = pps.numeralFormatter ? pps.numeralFormatter.getRawValue(rawValue) : '';
} else {
rawValue = Util.stripDelimiters(rawValue, pps.delimiter, pps.delimiters);
}
return rawValue;
},
getISOFormatDate: function () {
var owner = this,
pps = owner.properties;
return pps.date ? pps.dateFormatter.getISOFormatDate() : '';
},
getISOFormatTime: function () {
var owner = this,
pps = owner.properties;
return pps.time ? pps.timeFormatter.getISOFormatTime() : '';
},
onInit: function (owner) {
return owner;
},
onKeyDown: function (event) {
var owner = this,
pps = owner.properties,
charCode = event.which || event.keyCode;
owner.lastInputValue = pps.result;
owner.isBackward = charCode === 8;
owner.registeredEvents.onKeyDown(event);
},
onFocus: function (event) {
var owner = this, pps = owner.properties;
if (pps.prefix && pps.noImmediatePrefix && !event.target.value) {
owner.onInput(pps.prefix);
}
event.target.rawValue = owner.getRawValue();
event.target.value = pps.result;
owner.registeredEvents.onFocus(event);
Util.fixPrefixCursor(owner.element, pps.prefix, pps.delimiter, pps.delimiters);
},
onBlur: function (event) {
var owner = this, pps = owner.properties;
event.target.rawValue = owner.getRawValue();
event.target.value = pps.result;
owner.registeredEvents.onBlur(event);
},
onChange: function (event) {
var owner = this, pps = owner.properties;
owner.isBackward = owner.isBackward || event.inputType === 'deleteContentBackward';
// hit backspace when last character is delimiter
var postDelimiter = Util.getPostDelimiter(owner.lastInputValue, pps.delimiter, pps.delimiters);
if (owner.isBackward && postDelimiter) {
pps.postDelimiterBackspace = postDelimiter;
} else {
pps.postDelimiterBackspace = false;
}
owner.onInput(event.target.value);
event.target.rawValue = owner.getRawValue();
event.target.value = pps.result;
owner.registeredEvents.onChange(event);
},
onInput: function (value, fromProps) {
var owner = this, pps = owner.properties;
// case 1: delete one more character "4"
// 1234*| -> hit backspace -> 123|
// case 2: last character is not delimiter which is:
// 12|34* -> hit backspace -> 1|34*
var postDelimiterAfter = Util.getPostDelimiter(value, pps.delimiter, pps.delimiters);
if (!fromProps && !pps.numeral && pps.postDelimiterBackspace && !postDelimiterAfter) {
value = Util.headStr(value, value.length - pps.postDelimiterBackspace.length);
}
// phone formatter
if (pps.phone) {
if (pps.prefix && (!pps.noImmediatePrefix || value.length)) {
pps.result = pps.prefix + pps.phoneFormatter.format(value).slice(pps.prefix.length);
} else {
pps.result = pps.phoneFormatter.format(value);
}
owner.updateValueState();
return;
}
// numeral formatter
if (pps.numeral) {
// Do not show prefix when noImmediatePrefix is specified
// This mostly because we need to show user the native input placeholder
if (pps.prefix && pps.noImmediatePrefix && value.length === 0) {
pps.result = '';
} else {
pps.result = pps.numeralFormatter.format(value);
}
owner.updateValueState();
return;
}
// date
if (pps.date) {
value = pps.dateFormatter.getValidatedDate(value);
}
// time
if (pps.time) {
value = pps.timeFormatter.getValidatedTime(value);
}
// strip delimiters
value = Util.stripDelimiters(value, pps.delimiter, pps.delimiters);
// strip prefix
value = Util.getPrefixStrippedValue(value, pps.prefix, pps.prefixLength, pps.result, pps.delimiter, pps.delimiters, pps.noImmediatePrefix, pps.tailPrefix, pps.signBeforePrefix);
// strip non-numeric characters
value = pps.numericOnly ? Util.strip(value, /[^\d]/g) : value;
// convert case
value = pps.uppercase ? value.toUpperCase() : value;
value = pps.lowercase ? value.toLowerCase() : value;
// prevent from showing prefix when no immediate option enabled with empty input value
if (pps.prefix) {
if (pps.tailPrefix) {
value = value + pps.prefix;
} else {
value = pps.prefix + value;
}
// no blocks specified, no need to do formatting
if (pps.blocksLength === 0) {
pps.result = value;
owner.updateValueState();
return;
}
}
// update credit card props
if (pps.creditCard) {
owner.updateCreditCardPropsByValue(value);
}
// strip over length characters
value = pps.maxLength > 0 ? Util.headStr(value, pps.maxLength) : value;
// apply blocks
pps.result = Util.getFormattedValue(
value,
pps.blocks, pps.blocksLength,
pps.delimiter, pps.delimiters, pps.delimiterLazyShow
);
owner.updateValueState();
},
updateCreditCardPropsByValue: function (value) {
var owner = this, pps = owner.properties,
creditCardInfo;
// At least one of the first 4 characters has changed
if (Util.headStr(pps.result, 4) === Util.headStr(value, 4)) {
return;
}
creditCardInfo = CreditCardDetector.getInfo(value, pps.creditCardStrictMode);
pps.blocks = creditCardInfo.blocks;
pps.blocksLength = pps.blocks.length;
pps.maxLength = Util.getMaxLength(pps.blocks);
// credit card type changed
if (pps.creditCardType !== creditCardInfo.type) {
pps.creditCardType = creditCardInfo.type;
pps.onCreditCardTypeChanged.call(owner, pps.creditCardType);
}
},
updateValueState: function () {
var owner = this,
pps = owner.properties;
if (!owner.element) {
owner.setState({ value: pps.result });
return;
}
var endPos = owner.element.selectionEnd;
var oldValue = owner.element.value;
var newValue = pps.result;
owner.lastInputValue = newValue;
endPos = Util.getNextCursorPosition(endPos, oldValue, newValue, pps.delimiter, pps.delimiters);
if (owner.isAndroid) {
window.setTimeout(function () {
owner.setState({ value: newValue, cursorPosition: endPos });
}, 1);
return;
}
owner.setState({ value: newValue, cursorPosition: endPos });
},
render: function () {
var owner = this;
// eslint-disable-next-line
var { value, options, onKeyDown, onFocus, onBlur, onChange, onInit, htmlRef, ...propsToTransfer } = owner.props;
return (
);
}
});
module.exports = cleaveReactClass;