Textarea limit characters and lines with Javascript - javascript

There are some similar topics to this theme, but current requirements are more specific and I couldn't combine it yet with existent solutions, so asking community to help me with. I need to build textarea js validator for rules:
4 lines of 32 characters maximum
of 128 characters maximum
a remaining characters counter is displayed under the textarea
and other that I already found how to do...
Could you please tell how I could implement this?
p.s. It should be clear Javascript solution

This is how your code should look like (see also JSFiddle):
var rows = 4;
var cols = 32;
var body = document.body;
var ta = createElem(body, 'textarea');
var div = createElem(body, 'div');
ta.rows = rows;
ta.cols = cols;
ta.addEventListener('keyup', () => {
var lines = ta.value.split('\n');
lines = lines
.slice(0, Math.min(lines.length, rows))
.map(line => line.substring(0, cols));
ta.value = lines.join('\n');
displayRemaining(cols * rows - ta.value.length + rows - 1);
});
displayRemaining(cols * rows);
function createElem(a, b){
b = document.createElement(b);
a.appendChild(b);
return b;
}
function displayRemaining(a){
div.innerHTML = 'Characters remaining: ' + a + '.';
}

Related

Counting Words Between Two Variable Strings

Total newbie + first time poster here with very little experience though I feel this problem is one I could solve with the help of some generous strangers.
I am querying a GDoc and attempting to create a function to count words between two strings for two possible end strings, for example:
Example #1
Definitive Title
*Count these words*
===============
OR
Example #2
Definitive Title
*Count these words*
Other words that are in a table
Definitive Title
*Count these other different words*
===============
In both of the above examples I looking to count the words between a pre-defined string and an end string.
If I ran the function that I am trying to create on Example #1 I am hoping it'd return 3 words. For Example #2 I'd hope that my function returns 8 words.
So far my function looks like this:
function doPost(e) {
var docUrl = e.parameter.docUrl
var text = DocumentApp.openByUrl(docUrl).getBody().getText()
var wordCount = text.split(" ").length
return ContentService.createTextOutput(wordCount.toString()).setMimeType(ContentService.MimeType.TEXT)
}
This returns a word count for the entire document. Any advice to point me in the right direction?
For more dynamic, appropriate and accurate solution, execute the following snippets before the split () function. Regular Expressions often used to provide dynamic solutions. It is a must have skill.
text = text.replace(/(^\s*)|(\s*$)/gi,""); // remove the start and end spaces of the string (like trim ())
text = text.replace(/[ ]{2,}/gi," "); // filter out one or more spaces
text = text.replace(/\n /,"\n"); // filter out news lines with spacing at beginning
wordCount = text.split(" ").length;
Here is a solution to your problem you can log the difference of characters and words or you can log the total amount of words or characters in the two sentaces. You are also going to want to put the bigger sentence on top, otherwise it will give you a negative number.
var x = "count these words";
var y = "count words";
function findCharDif(word1, word2) {
var word1length = word1.length;
var word2length = word2.length;
var difference = word1length - word2length;
var total = word1length + word2length;
console.log(difference);
console.log(total);
}
function findWordDif(sentence1, sentence2) {
var words1 = 0;
var words2 = 0;
for (var i = 0; i < sentence1.length; i++) {
if (sentence1[i] == " ") {
words1++;
} else {
continue
}
}
for (var a = 0; a < sentence2.length; a++) {
if (sentence2[a] == " ") {
words2++;
} else {
continue
}
}
var difference = (words1 + 1) - (words2 + 1); // this logs out the difference of words between the sentences
var totalWords = (words1 + 1) + (words2 + 1); // this logs out the total amount of words
console.log(difference);
console.log(totalWords);
}
findCharDif(x, y);
findWordDif(x, y);
The below code seems to have worked! Was able to sit down with someone and solve it with them:
function doPost(e) {
var docUrl = e.parameter.docUrl
/*
var text = DocumentApp.openByUrl(docUrl).getBody().getText()
var wordCount = text.split(" ").length
*/
var wordCount = countScenario2(docUrl);
return ContentService.createTextOutput(wordCount.toString()).setMimeType(ContentService.MimeType.TEXT)
}
/**
* Count the words from Start Test to a table or ====
*/
function countScenario2(docUrl) {
//var docUrl = 'https://docs.google.com/document/d/';
var doc = DocumentApp.openByUrl(docUrl);
var body = doc.getBody();
var reference = body.findText('Start Text');
var start = getIndex('Start Text', body);
var tables = body.getTables();
var count = 0;
for(var j = 1; j < tables.length ; j ++) {
var end = body.getChildIndex(tables[j]);
for (var i = start + 1; i < end; i++) {
var element = body.getChild(i);
var text = element.getText();
//if(text.length > 0) count += text.split(" ").filter(word => word !== ' ' && word !== '' && word !== ' ').length;
var match = text.match(/\b(\w+)\b/g);
count += (match) ? match.length : 0;
}
console.log(count);
var reference = body.findText('Start Text', reference);
var element = reference.getElement();
var start = body.getChildIndex(element.getParent());
}
var end = getIndex('=========================================================', body);
for (var i = start + 1; i < end; i++) {
var element = body.getChild(i);
var text = element.getText();
//if(text.length > 0) count += text.split(" ").filter(word => word !== ' ' && word !== '' && word !== ' ').length;
var match = text.match(/\b(\w+)\b/g);
count += (match) ? match.length : 0;
}
console.log(count);
return count ;
}
/**
* This will return the index of the element
*
* #param {string} keyword The text to be found
* #param {Body} body This is the body of the document
*/
function getIndex(keyword, body, previous) {
var reference = body.findText(keyword, previous);
var element = reference.getElement();
return body.getChildIndex(element.getParent());
}
/************ */
function testPost(){
var e = {parameter:{docUrl:'https://docs.google.com/document/d/'}};
var result = doPost(e);
console.log(JSON.stringify(result.getContent()));}
/**
* Count the words from Start Text to ====
*/
function countScenario1(docUrl) {
//var docUrl = 'https://docs.google.com/document/d/';
var doc = DocumentApp.openByUrl(docUrl);
var body = doc.getBody();
var start = getIndex('Start Text', body);
var end = getIndex('=========================================================', body);
var count = 0;
for (var i = start + 1; i < end; i++) {
var element = body.getChild(i);
var text = element.getText();
//if(text.length > 0) count += text.split(" ").filter(word => word !== ' ' && word !== '' && word !== ' ').length;
var match = text.match(/\b(\w+)\b/g);
count += (match) ? match.length : 0;
}
console.log(count);
return count;
}
function test(){
var docUrl = 'https://docs.google.com/document/d/';
var wordCount = countScenario2(docUrl);
console.log(wordCount);
}
As what #Rishabh K said in his answer, you should definitely want to replace trailing spaces and multiple spaces to avoid inaccurate results.
However on the other hand, I don't think it answers the OP's question. Correct me if I'm wrong but I think this is what you want:
var sample1 = `This is the start identifier
These words should be included
As well As these ones
Even this
Until it ends
now
Ending identifier
These words shouldn't be included
If any of these appears, the logic is wrong`;
var sample2 = sample1 + `
This is the start identifier
These some few words
should also be included in the result set
Ending identifier`;
var sample3 = sample2 + `
This is the start identifier
Although we have the start identifier above
These words shouldn't be included
because there is no corresponding end identifier`;
function getWordDiffBetween(source, str1, str2) {
// make sure newSource, str1 and str2 are all strings
var args = Array.prototype.slice.call(arguments);
args.forEach(function(str, idx) {
if (typeof str !== 'string') {
throw `Argument ${[idx + 1]} is not a string.`;
}
});
var startId = '<==start==>',
endId = '<==end==>';
var newSource = source.replace(new RegExp(str1, 'g'), startId) // replace the start identifier with our own
.replace(new RegExp(str2 + '|={2,}', 'g'), endId) // replace the end identifier with our own
.replace(/(^\s*)|(\s*$)/gi, "") // remove the start and end spaces of the string (like trim ())
.replace(/\s+/g, ' ') //replace all 1 or more spaces/newline/linefeed with a single space
//separate text into words which are separated by a space since we replaced all newlines with space
var words = newSource.split(' ');
// get the indexes where the start and end identifiers occured
var strOneIdx = getAllIndexes(words, startId, true);
var strTwoIdx = getAllIndexes(words, endId, true);
var results = [], // we will store our results here
i;
for (i = 0; i < strOneIdx.length; i++) {
var idxOne = strOneIdx[i]; // current index for str1
var idxTwo = strTwoIdx.find(x => x > idxOne);
//make sure that idxOne has a partner
if (idxTwo) {
var wordsInBetween = words.slice(idxOne + 1, idxTwo); //get range between idxOne and idxTwo
results = results.concat(wordsInBetween); // add the result
}
}
return results;
}
function getAllIndexes(arr, val) {
var indexes = [],
i;
for (i = 0; i < arr.length; i++) {
if (arr[i] === val) {
indexes.push(i);
}
}
return indexes;
}
var startIdentifier = 'This is the start identifier',
endIdentifier = 'Ending identifier',
wordResults = {
sample1: getWordDiffBetween(sample1, startIdentifier, endIdentifier),
sample2: getWordDiffBetween(sample2, startIdentifier, endIdentifier),
sample3: getWordDiffBetween(sample3, startIdentifier, endIdentifier) //should be equal to sample2
};
console.log(wordResults);
We have 2 functions - getWordDiffBetween and getAllIndexes. For explanation, check the comments I added in noteworthy lines.
Edit (updated snippet above):
It seems like you also want "====================" included as your end identifier. This can be done by changing the code:
.replace(new RegExp(str2, 'g'), endId) // replace the end identifier with our own
into
.replace(new RegExp(str2 + '|={2,}', 'g'), endId) // replace the end identifier with our own
which means match occurence of your <end string> or if there is 2 or more occurences of =. You can also change the number 2 in {2,} to your desired count.

How do read the last character per string?

My code generates a string, as shown in the image. At the end of each line a number in euros is shown, let call these euros1 and euros2. The idea is that the amount ✓ icons is multiplied with euros1 and euros2 shown per line. So in the case of my example: (10,45 + 5,50) x 2 = 31,90 for the first line, and for the second line (18,24 + 9,60) x 3 = 83,52. These numbers are supposed to be combined to a total of 115,42.
However my current code produces a total of 218,95. So it takes the sum of all the euros1 and euros2 and multiplies it by the total amount of ✓ icons.
How can I calculate the sum of the euros1 + euros2 x the amount ✓ icons, per line?
I think a for loop could help me, however I am new to Javascript and I am not sure how to proceed.
var temp = g_form.getValue('mwadm_onkosten_list_refList_changes');
var count = (temp.match(/✓/g) || []).length;
var lastChar = temp[temp.length -1];
if (count != temp);
{
total = total * count;
}
I was thinking of a split solution just like #dganenco mentioned. I created a TEMP variable to reproduce your result string. And execute a function on it foreach row.
And i intentionally kept it really simple. Hope this helps.
var temp = ["✓|| €10,45 | €1,50 ", "✓|| €10,45 | €2,50 ", "✓|| €10,45 | €3,50 "];
var totalTimes = (String(temp).match(/✓/g) || []).length;
//perform function for each row
temp.forEach(CalculateRow);
//Splits the row, Gets euro1 as decimal, euro2 as decimal, calculates the amount a character is found, calculates total, prints it to console.
function CalculateRow(item, index) {
var arr = item.split('|');
var euro1 = GetValueFromArray(arr,1);
var euro2 = GetValueFromArray(arr, 2);
var times = (String(arr).match(/✓/g) || []).length;
var _total = (euro1 + euro2) * times;
console.log(_total);
}
//Takes the index value, and casts it to decimal value
function GetValueFromArray(arr, index){
var getindex = arr.length -index;
var result = arr[getindex];
result = result.replace('€', '');
return parseFloat(result, 10);
}
I've got a solution for you in case if is it possible to only split strings by | symbol, then get last 2 values and do all needed stuff using them.
const strArr = ['asd|addf|$56.60|$10.40', 'asd|addf|$5.60|$1.40'];
let sum = 0;
strArr.forEach(str => {
const splitted = str.split('|');
debugger;
const x = getNumber(splitted[splitted.length - 2]);
const y = getNumber(splitted[splitted.length - 1]);
sum += (x+y)*2;
});
function getNumber(str){
return +str.substring(1);
}
console.log(sum)
Assuming temp is the multiline string in the form of
asd|√|√| €12.34 | €15.25
zxc|√|| €18.34 | €19.25
This code should give you a sum of last two numbers multiplied by the amount of √
var temp = "asd|√|√| €12.34 | €15.25\nzxc|√|| €18.34 | €19.25"
let lines = temp.split('\n')
let linesSummaries = []
for(let line of lines){
var count = line.match(/√/g).length
var vars = line.split('|')
var x = parseFloat(vars[vars.length-1].match(/\d+\.?\d*/))
var y = parseFloat(vars[vars.length-2].match(/\d+\.?\d*/))
var lineSum = (x + y ) * count
linesSummaries.push(lineSum)
}
console.log(linesSummaries)

Create Dynamic columns from big text data Javascript

I get the text dynamically, sometimes the text is short sometime the text long.
The Designer separate the text to tow columns.
How can I seperate the text to tow columns.
I use angular but simple javascript is ok.
Considering that you are using bootstrap column layout, you can divide the string content into two equal parts (space as a breaking point) and then you can construct a html string like this:
function splitInHalf(s){
var middle = Math.floor(s.length / 2);
var before = s.lastIndexOf(' ', middle);
var after = s.indexOf(' ', middle + 1);
if (middle - before < after - middle) {
middle = before;
} else {
middle = after;
}
var s1 = s.substr(0, middle);
var s2 = s.substr(middle + 1);
return [s1, s2];
}
var x = <you big or small string of content>;
var splitX = splitInHalf[x];
var _html = '<div class="col-lg-6">'+splitX[0]+'</div><div class="col-lg-6">'+splitX[1]+'</div>'
// Now set this _html as your html

textarea - how to count wrapped lines/rows

I need some function That will count rows ( i know that on stackoverflow there are more than hundreds of these questions) but in my case i need to count them even when there is no end of line (mean "/n") because typical function is
textarea.value.substr(0, textarea.selectionStart).split("\n").length;.
It means that if user overflows max length of the row but he doesn't use "enter" and the text is on "new line".
Well, i dont know how to describe it better, so there is a example on fiddle https://jsfiddle.net/fNPvf/12872/
try to write long sentence with no spaces, enters etc.. and you will see where the problem is
what i really don't want is css rule nowrap, overflow-x etc..
Here you go.
/** #type {HTMLTextAreaElement} */
var _buffer;
/**
* Returns the number of lines in a textarea, including wrapped lines.
*
* __NOTE__:
* [textarea] should have an integer line height to avoid rounding errors.
*/
function countLines(textarea) {
if (_buffer == null) {
_buffer = document.createElement('textarea');
_buffer.style.border = 'none';
_buffer.style.height = '0';
_buffer.style.overflow = 'hidden';
_buffer.style.padding = '0';
_buffer.style.position = 'absolute';
_buffer.style.left = '0';
_buffer.style.top = '0';
_buffer.style.zIndex = '-1';
document.body.appendChild(_buffer);
}
var cs = window.getComputedStyle(textarea);
var pl = parseInt(cs.paddingLeft);
var pr = parseInt(cs.paddingRight);
var lh = parseInt(cs.lineHeight);
// [cs.lineHeight] may return 'normal', which means line height = font size.
if (isNaN(lh)) lh = parseInt(cs.fontSize);
// Copy content width.
_buffer.style.width = (textarea.clientWidth - pl - pr) + 'px';
// Copy text properties.
_buffer.style.font = cs.font;
_buffer.style.letterSpacing = cs.letterSpacing;
_buffer.style.whiteSpace = cs.whiteSpace;
_buffer.style.wordBreak = cs.wordBreak;
_buffer.style.wordSpacing = cs.wordSpacing;
_buffer.style.wordWrap = cs.wordWrap;
// Copy value.
_buffer.value = textarea.value;
var result = Math.floor(_buffer.scrollHeight / lh);
if (result == 0) result = 1;
return result;
}
Demo here
If I'm understanding the problem correctly, you need to count two things.
The number of hard line breaks "\n".
The number of lines that wrap past X characters.
Here's some pseudo code:
var lineLengthLimit = 40;
var lineCounter = 0;
foreach(lines as line){
lineCounter+=Math.floor(line.length/lineLengthLimit);
}
lineCounter += lines.length;
Another option might be what this guy suggested: https://stackoverflow.com/a/3697249/1207539 but it seems a bit sketchy.

(P)RNG - Array of Random Numbers Created with a Seed

I want to create an array of random/pseudo-random numbers using a seed. I want the very same array to be created when the same seed is used and I want to have little or no visible pattern in the array. I'm working in JavaScript.
This is the random function I'm using, which I'm quite happy with (sorry, I forgot who the original author is):
function random(seed) {
if (!seed) seed = new Date().getTime();
seed = (seed*9301+49297) % 233280;
return seed/(233280.0);
}
This is the array generation:
var superSeed = random();
var nRandom = 100;
var randomArray = new Array();
for (var i = 0 ; i < nRandom ; i++){
randomArray.push(random((superSeed*10)+ (i)));
}
Somehow the pattern seems to be quite similar, no matter how often I run it. This question seems to be similar, but since it's about matrixes, I don't understand what's being said.
Thanks!
Having worked on similar things before I think we can use a fairly simple series, which takes two initial values and then you can get a lot more.
var a1,b1;
function InitSequence(v1, v2) {
a1 = Math.pow(v1, 5) / Math.pow(v1, 3);
b1 = Math.pow(v2, 8);
lastrand = (a1 + b1) & 0x7fffffff;
}
function SequenceNext() {
var alast = a1;
var nextVal = (a1 + b1) & 0x7fffffff;
b1 = alast;
a1 = nextVal;
return nextVal;
}
Then use it like this:
InitSequence(75, 21);
for (var i = 0; i < 99; i++) {
v = SequenceNext();
}
I tested it like this:
var used = new Array();
InitSequence(75, 21); // any two values will do.
// fill 10k into array.
for (var i = 0; i < 9999; i++) {
v = SequenceNext();
if (undefined != used[v]) {
used[v]++;
} else used[v] = 1;
//document.write(i+": "+v+"<br>");
}
// see if there any duplicates.
var tdup = 0;
for (xx in used) {
ndup = used[xx];
if (ndup > 1) {
document.write("duplicated " + xx + " :" + ndup + "<br>");
tdup += ndup;
}
}
document.write("Total dups " + tdup + "<br>");
This is using the Fibonacci series, which in mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation
. I'm starting with different values - (v1^5) / (v1^3) and v2 ^ 8; otherwise it would only ever be identical.
I like the "Super 7" PRNG. It is simple, fast (although the other answer with the fib. sequence is fast as well), and has the interesting property:
In the entire range -- albeit of a meager of 32k -- there are no duplicates using the "Super 7" PRNG.
Multiple 7's can be joined to increase the number of bits and/or provide multiple seeds. This non-duplication property can exposed or folded.
(The sequence of a PRNG is always the same given a starting seed: it's the distribution and cycle lengths that are interesting -- it is these properties that may make them ideal in different cases where "true randomness" isn't desired).
Happy coding.
Maybe you should try this
function s_random() {
s_random.m = 71402523; s_random.a = 409647; s_random.c = 1508892;
s_random.seed = (s_random.seed*s_random.a + s_random.c) % s_random.m;
return s_random.seed / s_random.m;
}
/*
generate IV
s_random.seed = Math.floor((new Date).getTime()/10000);
*/
s_random.seed = 130324232;
var CheckRandom = 4999999;
var PrintSamples = 100;
var used = new Array();
for (var i = 0; i < CheckRandom; i++) {
v = (Math.ceil(Math.sqrt(s_random())* 1000000000) * 8);
if (undefined != used[v]) {
used[v]++;
} else used[v] = 1;
if ( i< PrintSamples) document.write(i+": "+v+"");
}
/* see if there are any duplicates. */
var tdup = 0;
for (xx in used) {
ndup = used[xx];
if (ndup > 1) {
if (ndup < PrintSamples) document.write("duplicated " + xx + " :" + ndup + "");
tdup += ndup;
}
}
document.write("Total generated " + CheckRandom + "");
document.write("Total duplicates " + tdup + "");
Just got 5 million seeded, repeatable random numbers and no duplicates. Tested several times on Mac OS X with Safari.
Cheers,
Karl

Categories

Resources