Credit card validation with parsleyJS - parsley.js

In version 1.x, it seems that there was credit cart validation in extras via data-luhn="true".
In version 2.0, is there similar method to check credit card validity ?

Nope there is currently not. There is in Parsley2 repo an https://github.com/guillaumepotier/Parsley.js/tree/master/src/extra/validator directory where special or non usual validators could fit. v1 luhn validator should be ported here if you want to use it with Parsley2 and give it to the community too.
Best

As of December 2014, support for luhn validation has been restored to Parsley.
Follow these steps to add Parsley's Luhn support:
Download luhn.js from Parsley github
Includ it in your code before including parsley.js / parsley.min.js
Add data-parsley-luhn="true" to the credit card input

Add the following code somewhere in your code:
if (typeof Parsley !== 'undefined') {
//add the custom message:
Parsley.addMessages('lang', {
luhn: 'Please provide valid credit card'
});
//set the locale:
Parsley.setLocale('lang');
//add the credit card validator:
Parsley.addValidator('luhn',
function (value) {
val = value.replace(/[ -]/g, '');
var digit, n, sum, _j, _len1, _ref2;
sum = 0;
_ref2 = val.split('').reverse();
for (n = _j = 0, _len1 = _ref2.length; _j < _len1; n = ++_j) {
digit = _ref2[n];
digit = +digit;
if (n % 2) {
digit *= 2;
if (digit < 10) {
sum += digit;
} else {
sum += digit - 9;
}
} else {
sum += digit;
}
}
return sum % 10 === 0;
}, 32);
}
provide the input's attribute: data-parsley-luhn="true"
<input id="creditCardNumber" value="" type="text" autocomplete="off" style="direction:rtl;" data-parsley-type="number" data-parsley-required="true" data-parsley-luhn="true"/>

Related

Restrict double minus in mui number textfiled

Currently, Number type inputs accepting double minus (i.e: --1). But I want only one - sign. If user press another minus then it should preventDefault().
Found a solution here. But it has some edge cases like, if user input '-123' and then go back and remove the '-' then user can't give the '-' again.
Since such values (-786-6, --712, ...) are allowed in TextField of type="number"
What I did is changing the textField proprety type from number to text, yes we'll accept strings but the user will be forced to enter a valid number.
Let's create a useState hook for the TextField input :
const [entredValue,setEntredValue] = useState('');
Then each time we verify if the entred input is a valid number before updating entredValue. this is a function made for that :
function restrict(test) {
if (test == "") return true;
if (test.length == 1) {
if (isNaN(test[0]) && test[0] !== "-") return false;
}
var found = 0;
for (let i = 0; i < test.length; i++) {
if (test[i] === ".") found++;
}
if (found > 1 || test == "-.") return false;
if (
test.length > 1 &&
isNaN(test[test.length - 1]) &&
test[test.length - 1] !== "."
)
return false;
let result = test.substr(0, test.length - 1);
if (test.length > 2 && isNaN(result)) return false;
return true;
}
Then update the state value this way :
<TextField
onChange={(event) => {
if (restrict(event.target.value)) {
setEntredValue(event.target.value);
}}}
/>
finally when you have to use entredValue:
parseFloat(entredValue); // always a valid number

How can I adjust my formula to continuously autofill the missing paramater?

So i'm building a calculator/estimator that is basically just a more complicated version of this margin calculator: https://www.omnicalculator.com/finance/margin
So here's one edge case that I'm trying to fix right now.
My costs are broken into 3 parts due to outsourced data- labor, material and laborAndMaterial. LaborAndMaterial is the sum of labor and material, but it can be the only known cost factor so that's why it's broken into 3 parts.
So here's the problem. Say we know that laborAndMaterial is set to 100 and labor and material are 0
cost: {
labor: 0,
material: 0,
laborAndMaterial: 100
}
Then the user enters 50 for labor. Because laborAndMaterial is 100 and labor is now 50 we can autofill material to 50.
But what's happening right now as the user is typing "50" it autofills material to 95 as the user types the 5 in 50. Then when they enter the "0" it sets the laborAndMaterial to 145 (50 + 95). But in that example I need to adjust how I autofill material to continue to update as the user enters more numbers (labor = 5 -> 50 -> 506) (material = 95, 50, -406). As of now I basically have my formula run like:
if(key === "cogs.labor") {
if(laborAndMaterial > 0) {
params["cogs.material"] = laborAndMaterial - value // value is what was entered
}
}
But I still need to allow for the other edge cause that as labor is entered and material is known it updates the laborAndMaterial value
cost {
labor: 50,
material: 50,
laborAndMaterial: 100
}
So if someone enters 100 for labor and we know material is 50 we can autofill laborAndMaterial to 150.
So I have something like:
if(material > 0) {
params["cogs.laborAndMaterial"] = material + value // value is what was entered
}
Any ideas how I can tweak my formula to decide the autofill and continue to update that paramater while still allowing for multiple edge cases?
The margin calculator from omnicalculator is a good example as they solved the issue, but I've been scratching my head over it for some time.
I think you basically need to differentiate between which cost centers are treated as input and which are treated as output. So when you start, each piece of data you're provided is input, and the data you use to autofill the rest of the form is output.
As the user types, any information they give is then treated as input data. Given that any two values can be used to calculate the third, you can only have two of the fields be treated as input at a time.
Here's a code sample to get an idea of what I mean:
// This is a queue to hold your two input cost centers
const inputFields = [];
// Determine the odd one out that we need to calculate
function getVariableCostCenter() {
if (!inputFields.includes('labor')) {
return 'labor';
}
if (!inputFields.includes('material')) {
return 'material';
}
return 'laborAndMaterial';
}
function calculateCostCenters(costCenters) {
const variableCostCenter = getVariableCostCenter();
if (variableCostCenter === 'labor') {
return {
...costCenters,
labor: costCenters.laborAndMaterial - costCenters.material,
};
}
if (variableCostCenter === 'material') {
return {
...costCenters,
material: costCenters.laborAndMaterial - costCenters.labor,
};
}
return {
...costCenters,
laborAndMaterial: costCenters.labor + costCenters.material,
};
}
function initializeCostCenters(costCenters) {
// First, we determine which field(s) are known up front
if (costCenters.labor > 0) {
inputFields.push('labor');
}
if (costCenters.material > 0) {
inputFields.push('material');
}
if (costCenters.laborAndMaterial > 0 && inputFields.length < 2) {
inputFields.push('laborAndMaterial');
}
// ...then do whatever you normally do to populate the form
}
function updateCostCenters(previousCostCenters) {
// Update the input fields so that the user's input
// is always treated as one of the two input fields
if (!inputFields.includes(key)) {
inputFields.shift();
inputFields.push(field);
}
const costCenters = calculateCostCenters({
...previousCostCenters,
[key]: value,
});
params['cogs.labor'] = costCenters.labor;
params['cogs.material'] = costCenters.material;
params['cogs.laborAndMaterial'] = costCenters.laborAndMaterial;
}
Pretty roughly it might look like below.
Note that I remembering last touched fields, which are became "fixed", because we can not recalculate values circularly.
Also, note that I use direct value update, while in some frameworks/libs it might generate change/input event, so you would want to set values silently.
setup = {
labor: {
value: 0
},
material: {
value: 0
},
laborAndMaterial: {
value: 100
}
};
// the number which we are treat as "fixed", may be changed later
let prevFixed = 'labor';
let fixed = 'labor';
const calculateTheRest = () => {
if (!setup.material.touched && !setup.laborAndMaterial.touched ||
!setup.labor.touched && !setup.laborAndMaterial.touched ||
!setup.labor.touched && !setup.material.touched) {
return false; // two unknowns, can't recalculate
}
if (!setup.labor.touched || fixed !== 'labor' && prevFixed !== 'labor') {
setup.labor.value = setup.laborAndMaterial.value - setup.material.value;
} else if (!setup.material.touched || fixed !== 'material' && prevFixed !== 'material') {
setup.material.value = setup.laborAndMaterial.value - setup.labor.value;
} else {
setup.laborAndMaterial.value = setup.material.value + setup.labor.value;
}
}
const $els = {};
Object.keys(setup).forEach(key => $els[key] = document.querySelector('#' + key))
const onInputChanged = (e) => {
const key = e.target.id;
setup[key].value = +e.target.value;
setup[key].touched = true;
if (fixed !== key) {
prevFixed = fixed;
fixed = key;
}
calculateTheRest();
Object.keys(setup).forEach(key => $els[key].value = setup[key].value);
}
Object.keys(setup).forEach(key => {
$els[key].value = setup[key].value; // initial set
setup[key].touched = setup[key].value !== 0; // 0 on initial setup are the numbers that not set
$els[key].addEventListener('input', onInputChanged);
})
<p><label>labor: <input id="labor" type="number"/></label></p>
<p><label>material: <input id="material" type="number"/></label></p>
<p><label> laborAndMaterial: <input id="laborAndMaterial" type="number" /></label></p>
I think you need to implement a condition on your laborAndMaterial field.
Check the condition: -
if(labor > 0 && material > 0){
let laborAndMaterial = labor + material;
}
And after that set the laborAndMaterial variable value into field,
I think it will may help you.

I'm trying to randomize 5 selections from a list of people

This might be less difficult than I'm making it out to be, but I'm trying to make a Discord.JS bot command, that will take however many arguments I have. For example: !randomize 1,2,3,4,5,6,7,8,9,10
And the bot would respond with something like: "I have chosen: 4,2,7,3,9!" Any help?
Current attempt: Not exactly sure what I'm doing.
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}`
`bot.on('message', async msg => {
if(msg.content === "!add") {
//message.member.user.tag
var msgArray = msg.content.split(" ");
var args = msgArray.slice(1);
var user = args[1];
//if(!args[1]) return msg.channel.send("Please specify an argument!");
if(nameList.includes(user)) {
msg.reply("You're already on the list.")
} else {
nameList.push(args[1]);
msg.channel.send(`${args[1]} has been added to the list!\n Current List:` + nameList);
}
}
if(msg.content === "!bonus") {
if(nameList.length === 0) {
msg.reply("Either the list is empty, or I'm not in the mood!");
} else {
shuffleArray(nameList);
var chosenOne = nameList.pop();
nameList = [];
msg.reply(chosenOne + ' has been chosen! Good luck!');
}
}
if(msg.content === "!list") {
if(nameList.length === 0) {
msg.channel.send("Either the list is empty, or I'm not in the mood!");
} else {
msg.channel.send('The current list:' + nameList);
}
});```
Here's some simple steps to select 5 random elements from an array...
Construct an array of possible selections. In this example I've used names for the first 10 letters of the alphabet. In your code, it'll be the command arguments or predefined nameList.
Make a new array to hold the elements picked.
At some point before #3, you should check to make sure the pool the user has provided is large enough to make 5 selections (Array.length).
Use a for loop to execute the next code multiple times.
Generate a random number representing the index of a selected element (Math.random(), Math.floor()/double NOT bitwise operator).
Push the selection into the array.
Remove the chosen element from the original pool (Array.splice()).
Return the results.
const pool = ['Albert', 'Bob', 'Charlie', 'David', 'Edward', 'Francis', 'George', 'Horacio', 'Ivan', 'Jim'];
const selected = [];
for (let i = 0; i < 5; i++) {
const num = ~~(Math.random() * pool.length);
selected.push(pool[num]);
pool.splice(num, 1);
}
console.log(`I have chosen: ${selected.join(', ')}`);
Take this example and manipulate it within your code to suit your purpose.

Parsley.js Customer Validator Pass Variable to message

window.Parsley.addValidator('passwordValidChars', {
requirementType: ['integer', 'integer'],
validateString: function(value, min, max) {
var invalidChars = [];
for (var i = 0; i < value.length; i++) {
if(value.charCodeAt(i) < min || value.charCodeAt(i) > max) {
invalidChars.push(value.charAt(i));
}
}
return (invalidChars.length === 0)
},
messages: {
en: 'These characters are not allowed %s'
}
});
I have this custom validator which validates the password entered between specific char codes e.g [33, 126].
If the user entered any invalid characters I collect them to an array invalidChars and then I want to pass the invalidChars array to the error message but how can I do that? The only values I can pass to the message are the min and max.
There's no super easy way, but you can return a dynamic error message. It's not well documented, but instead of returning false return $.Deferred().reject('your dynamic error message').

AngularJS number input formatted view

I want to use a formatted number input to show thousand seperator dots to user when he types big numbers. Here is the directive code that I used: http://jsfiddle.net/LCZfd/3/
When I use input type="text" it works, but when I want to use input type="number" it's weirdly cleaning by something when user typing big numbers.
What is problem about input[number]?
As written in the comments, input type="number" doesn't support anything but digits, a decimal separator (usually , or . depending on the locale) and - or e. You may still enter whatever you want, but the browser will discard any unknown / incorrect character.
This leaves you with 2 options:
Use type="text" and pattern validation like pattern="[0-9]+([\.,][0-9]+)*" to limit what the user may enter while automatically formatting the value as you do in your example.
Put an overlay on top of the input field that renders the numbers how you want and still allows the user to use the custom type="number" input controls, like demonstrated here.
The latter solution uses an additional <label> tag that contains the current value and is hidden via CSS when you focus the input field.
All these years later, there still isn't an HTML5 solution out of the box for this.
I am using <input type="tel"> or <input type="text"> ("tel" brings up a numeric keyboard in Android and iOS, which in some cases is a bonus.)
Then I needed a directive to:
filter out non-numeric characters
add thousand-separator commas as the user types
use $parsers and keyup to set elem.val() and $formatters to set the display...
...while behind the scenes, assign ng-model a floating point number
The directive example below does this, and it accepts negatives and floating point numbers unless you specify you want only positive or integers.
It's not the full solution I would like, but I think it bridges the gap.
HTML
<input type="text" ng-model="someNumber" number-input />
JAVASCRIPT
myApp.directive('numberInput', function($filter) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
ngModelCtrl.$formatters.push(function(modelValue) {
return setDisplayNumber(modelValue, true);
});
// it's best to change the displayed text using elem.val() rather than
// ngModelCtrl.$setViewValue because the latter will re-trigger the parser
// and not necessarily in the correct order with the changed value last.
// see http://radify.io/blog/understanding-ngmodelcontroller-by-example-part-1/
// for an explanation of how ngModelCtrl works.
ngModelCtrl.$parsers.push(function(viewValue) {
setDisplayNumber(viewValue);
return setModelNumber(viewValue);
});
// occasionally the parser chain doesn't run (when the user repeatedly
// types the same non-numeric character)
// for these cases, clean up again half a second later using "keyup"
// (the parser runs much sooner than keyup, so it's better UX to also do it within parser
// to give the feeling that the comma is added as they type)
elem.bind('keyup focus', function() {
setDisplayNumber(elem.val());
});
function setDisplayNumber(val, formatter) {
var valStr, displayValue;
if (typeof val === 'undefined') {
return 0;
}
valStr = val.toString();
displayValue = valStr.replace(/,/g, '').replace(/[A-Za-z]/g, '');
displayValue = parseFloat(displayValue);
displayValue = (!isNaN(displayValue)) ? displayValue.toString() : '';
// handle leading character -/0
if (valStr.length === 1 && valStr[0] === '-') {
displayValue = valStr[0];
} else if (valStr.length === 1 && valStr[0] === '0') {
displayValue = '';
} else {
displayValue = $filter('number')(displayValue);
}
// handle decimal
if (!attrs.integer) {
if (displayValue.indexOf('.') === -1) {
if (valStr.slice(-1) === '.') {
displayValue += '.';
} else if (valStr.slice(-2) === '.0') {
displayValue += '.0';
} else if (valStr.slice(-3) === '.00') {
displayValue += '.00';
}
} // handle last character 0 after decimal and another number
else {
if (valStr.slice(-1) === '0') {
displayValue += '0';
}
}
}
if (attrs.positive && displayValue[0] === '-') {
displayValue = displayValue.substring(1);
}
if (typeof formatter !== 'undefined') {
return (displayValue === '') ? 0 : displayValue;
} else {
elem.val((displayValue === '0') ? '' : displayValue);
}
}
function setModelNumber(val) {
var modelNum = val.toString().replace(/,/g, '').replace(/[A-Za-z]/g, '');
modelNum = parseFloat(modelNum);
modelNum = (!isNaN(modelNum)) ? modelNum : 0;
if (modelNum.toString().indexOf('.') !== -1) {
modelNum = Math.round((modelNum + 0.00001) * 100) / 100;
}
if (attrs.positive) {
modelNum = Math.abs(modelNum);
}
return modelNum;
}
}
};
});
https://jsfiddle.net/benlk/4dto9738/
You need to add the step attribute to your number input.
<input type="number" step="0.01" />
This will allow floating points.
http://jsfiddle.net/LCZfd/1/
Also, I'd recommend reviewing the bug thread on number inputs in Firefox. You may want to consider not using this input type, as it was just finally supported in this release of FF.
https://bugzilla.mozilla.org/show_bug.cgi?id=344616
http://caniuse.com/input-number
You cannot use values with , because type=number only takes numbers, adding a comma makes it a string.
See http://jsfiddle.net/LCZfd/5
You're better off making your own controls if you want commas. One with a true value (the number) and a display value (the string).
you can try this, I modified the directive I saw here...
How do I restrict an input to only accept numbers? ...
here's the modified directive I made... This directive uses the keyup event to modify the input on the fly...
.directive('numericOnly', function($filter) {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
element.bind('keyup', function (inputValue, e) {
var strinput = modelCtrl.$$rawModelValue;
//filter user input
var transformedInput = strinput ? strinput.replace(/[^,\d.-]/g,'') : null;
//remove trailing 0
if(transformedInput.charAt(0) <= '0'){
transformedInput = null;
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}else{
var decimalSplit = transformedInput.split(".")
var intPart = decimalSplit[0];
var decPart = decimalSplit[1];
//remove previously formated number
intPart = intPart.replace(/,/g, "");
//split whole number into array of 3 digits
if(intPart.length > 3){
var intDiv = Math.floor(intPart.length / 3);
var strfraction = [];
var i = intDiv,
j = 3;
while(intDiv > 0){
strfraction[intDiv] = intPart.slice(intPart.length-j,intPart.length - (j - 3));
j=j+3;
intDiv--;
}
var k = j-3;
if((intPart.length-k) > 0){
strfraction[0] = intPart.slice(0,intPart.length-k);
}
}
//join arrays
if(strfraction == undefined){ return;}
var currencyformat = strfraction.join(',');
//check for leading comma
if(currencyformat.charAt(0)==','){
currencyformat = currencyformat.slice(1);
}
if(decPart == undefined){
modelCtrl.$setViewValue(currencyformat);
modelCtrl.$render();
return;
}else{
currencyformat = currencyformat + "." + decPart.slice(0,2);
modelCtrl.$setViewValue(currencyformat);
modelCtrl.$render();
}
}
});
}
};
you use it like this ...
<input type="text" ng-model="amountallocated" id="amountallocated" numeric-only />

Resources