Find String in a Txt File, Delete Entire Line - javascript

I'm currently working with node.js to create an IRC bot. The bot allows users to add song links to a database. Each time someone submits a song, it is added to a new line of "shuffle.txt" as such:
user1,The Beatles,Yesterday,(youtube link)
user2,The Rolling Stones,Angie,(youtube link)
user1,The Bealtes,Yellow Sumbarine,(youtube link)
Notice that user1 mistyped some information in their latest addition. I'm trying to make an UNDO command so that a user can delete their most recently entered line. I plan on doing this by finding the latest occurrence of their name in shuffle.txt and deleting the entire line that it's found on. Here's my message listener:
bot.addListener('message', function(from, to, message) {
if (message.indexOf(config.prefix) == 0) {
message = message.slice(1);
var token = message.split(" ");
if (token[0] == 'undo') {
//find and delete
}
}
});
the user entering the command is stored as from
I'm assuming I'll have to do something along the lines of this:
var songList = fs.readFileSync('shuffle.txt', 'utf8');
var position = songList.indexOf(from);
if (position != -1) { //if 'from' found
//find LAST occurrence of 'from'
//get length from here to next occurrence of '\n'
//substr(length + 1)
fs.writeFile('shuffle.txt', songList, function(err) {
if (err) {
console.log (err);
}
}
I'm new to JavaScript and this is my first time using node.js so I can use any help I can get! Thanks everyone.
EDIT: I should also point out that I don't need help with the command recognition. I only need help with the finding/deleting portion. Cheers!

Edit2: edited with a new solution.
You could try this:
fs.readFile('shuffle.txt', function read(err, data) {
if (err) {
throw err;
}
lastIndex = function(){
for (var i = data_array.length - 1; i > -1; i--)
if (data_array[i].match('user1'))
return i;
}()
delete data_array[lastIndex];
});
Split file into lines, find the last line with a simple loop going backwards, remove the line using delete, then patch it back together.
demo
Also, you should not use readFileSync in node, as blocking node can be dangerous since it's single threaded.

This seems to work ok. It's self contained so you can just paste into a HTML file to see it run.
Basically keep running a regular expression to match the whole line starting with the username passed. The whole line is returned ("gm" part of regular expression tells it to match a line at a time) as a string.
Then you just to a replace of that returned line (as string) within the data.
Note that this assumes that line is unique in the text file. If you think people might have entered the same line a few times (but only want to remove the last one) then the 'non lazy' option whcih I've left uncommented is best. I haven't tested what happens if the line is first in the data or last.
<html>
<head>
<script type="text/javascript">
var sData="user1,The Beatles,Yesterday,(youtube link)\nuser2,The Rolling Stones,Angie,(youtube link)\nuser1,The Bealtes,Yellow Sumbarine,(youtube link)\nuser3,The Rolling Stones,Angie,(youtube link)";
function replaceLastEntry(sText) {
sNewData=sData;
var re=new RegExp("^"+sText+".*$","gm"); // matches whole lines starting with sText
var lastIndex=-1;
var i=0;
var sMatch=re.exec(sData)!= null;
while (sMatch!=null)
{
i++
lastIndex=re.lastIndex;
lastMatch=sMatch.toString(); // make note of last successful match - gives full line
sMatch=re.exec(sData);
}
// at this point lastMatch contains the whole line which needs to be removed.
// lastIndex is the string position of the character after the LAST string matched (ie the end of the matched line)
// sNewData = sData.replace(lastMatch,""); // Lazy way - assumes the line is unique within the file
// non-lazy way : locate the text by position returned and remove it
sNewData = sData.substr(0, lastIndex-lastMatch.length-1) + sData.substr(lastIndex);
document.getElementById("Data").innerHTML=sData
document.getElementById("NewData").innerHTML=sNewData
document.getElementById("LastMatch").innerHTML=lastMatch
}
</script>
</head>
<body onload="replaceLastEntry('user1','newvalue')">
Data: <pre id="Data"></pre>
New Data:<pre id="NewData"></pre>
Last Match: <pre id="LastMatch"></pre>
</body>
</html>
hope this helps !

Related

How to select a sentence in a generic HTML file with Javascript only

I need to select a complete sentence in a HTML text in which I cannot write any placeholder.
When the user selects a piece of text, I want the selection to extend from point to point. The problem I'm facing is that if I extend my sentence over the "tag limit" I'm not able to get it with the standard methods.
This is what I'm trying to do:
Attach a selection changed method:
document.addEventListener('selectionchange', TextSelected);
Extend get my "range"
function ExpandSelection()
{
//get the next "." (end of the sentence)
var last = window.getSelection().focusOffset;
while(window.getSelection().focusNode.textContent.charAt(last) != '.')
{
last++;
}
//get the previous "." (beginning of the sentence" !!! NOT WORKING
var first = window.getSelection().anchorOffset;
while(window.getSelection().focusNode.textContent.charAt(first) != '.')
{
first--;
}
first++;
return { start: first, end : last }
}
My problem is, right now, that my selection is working in "moving forward" but when I try to move backward I'm not able to get the previous characters.
Does anybody has another way to do it?
Summarizing: if somebody is selecting something (also a single letter) I want the selection to expand to the complete sentence.

get the new added characters to an input by js

I know this seems a quite easy target. I have an input[type=text], and I want to detect the new added character(s) in it. The normal way is:
$selector.keypress(function(e) {
//do sth here
var newchar = String.fromCharCode(e.which);
});
But the above method not working properly for some browsers on android devices. Typing the android virtual keyboard will not fire the keypress.
Then I found the following method is better:
$selector.on('input', function(e){
//do sth here
});
It works fine for android devices, and also, it can detect cut/paste.
Now the question is, is there a way to know the new added character(s) to the input? Do I need to do the complicated string comparison during inputing each time, i.e. compare the previous string and the new string in the input box? I said it's complicated because you may not always type in char(s) at the end, you may insert some char(s) in the middle of the previous string. Think about this, the previous string in the input box is "abc", the new string after pasting is "abcxabc", how can we know the new pasted string is "abcx", or "xabc"?
The method from keypress is quite simple:
String.fromCharCode(e.which);
So, is there similar way to do this by the on('input') method?
After reading Yeldar Kurmangaliyev's answer, I dived into this issue for a while, and find this is really more complicated than my previous expectation. The key point here is that there's a way to get the cursor position by calling: selectionEnd.
As Yeldar Kurmangaliyev mentioned, his answer can't cover the situation:
it is not working is when you select text and paste another text with
replacing the original one.
Based on his answer, I modified the getInputedString function as following:
function getInputedString(prev, curr, selEnd) {
if (selEnd === 0) {
return "";
}
//note: substr(start,length) and substring(start,end) are different
var preLen = prev.length;
var curLen = curr.length;
var index = (preLen > selEnd) ? selEnd : preLen;
var subStrPrev;
var subStrCurr;
for(i=index; i > 0; i--){
subStrPrev = prev.substr(0, i);
subStrCurr = curr.substr(0, i);
if (subStrCurr === subStrPrev) {
var subInterval = selEnd - i;
var interval = curLen - preLen;
if (interval>subInterval) {
return curr.substring(i, selEnd+(interval-subInterval));
}
else{
return curr.substring(i, selEnd);
}
}
}
return curr.substring(0, selEnd);
}
The code is quite self explanation. The core idea is, no matter what character(s) were added(type or paste), the new content MUST be ended at the cursor position.
There's also one issue for my code, e.g. when the prev is abcabc|, you select them all, and paste abc, the return value from my code will be "". Actually, I think it's reasonable, because for my scenario, I think this is just the same with delete the abc from previous abcabc|.
Also, I changed the on('input') event to on('keyup'), the reason is, for some android browsers, the this.selectionEnd will not work in a same way, e.g., the previous text is abc|, now I paste de and the current string will be abcde|, but depending on different browsers, the this.selectionEnd inside on('input') may be 3, or 5. i.e. some browsers will report the cursor position before adding the input, some will report the cursor position after adding the input.
Eventually, I found on('keyup') worked in the same way for all the browsers I tested.
The whole demo is as following:
DEMO ON JSFIDDLE
Working on the cross-browser compatibility is always difficult, especially when you need to consider the touch screen ones. Hope this can help someone, and have fun.
Important notes:
when a user types in a character, the cursor stands after it
when a user pastes the text, the cursor is also located after the pasted text
Assuming this, we can try to suggest the inputed \ pasted string.
For example, when we have a string abc and it becomes abcx|abc (| is a cursor) - we know that actually he pasted "abcx", but not "xabc".
How do this algorithmically? Lets assume that we have the previous input abc and the current input: abcx|abc (cursor is after x).
The new one is of length 7, while the previous one is of length 4. It means that a user inputed 4 characters. Just return these four characters :)
The only case when it is not working is when you select text and paste another text with replacing the original one. I am sure you will come up with a solution for it yoruself :)
Here is the working snippet:
function getInputedString(prev, curr, selEnd) {
if (prev.length > curr.length) {
console.log("User has removed \ cut character(s)");
return "";
}
var lengthOfPasted = curr.length - prev.length;
if (curr.substr(0, selEnd - lengthOfPasted) + curr.substr(selEnd) === prev)
{
return curr.substr(selEnd - lengthOfPasted, lengthOfPasted);
} else {
console.log("The user has replaced a selection :(");
return "n\\a";
}
}
var prevText = "";
$("input").on('input', function() {
var lastInput = getInputedString(prevText, this.value, this.selectionEnd);
prevText = this.value;
$("#result").text("Last input: " + lastInput);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<div id="result">Start inputing...</div>

How to get token before dot('.') at current cursor postion in javascript for autocompletion in codemirror editor?

function (editor, currentPos) {
return editor.getTokenAt(currentPos);
}
I am designing a code editor for a custom language. So, I want do autocompletion based on mode state. I want to get token before . like when we press CTRL + space during hello. Then, my method should give me hello so I can suggest some properties related to it. How can I do this?
The javascript-hint addon does something like this, continuing to get the previous token in a loop until it finds something that is not a property. You probably want code that looks similar.
I have a similar issue, and i ends using a chunk of code to get the current line and then split'em to get the last token (and the . separator):
autocomplete(cm) {
let full_line = cm.getValue();
let end = cm.getCursor().ch;
full_line = full_line.substr(0, end);
let start = full_line.lastIndexOf(" ");
if (start === -1) {
start = 0;
}
let currentWord = full_line.substr(start, end - start);
console.log(currentWord);
[...]
You can see the complete code in my ember-plugin: https://github.com/hugoruscitti/ember-cli-jsconsole/blob/master/tests/dummy/app/controllers/application.js#L5

How to extract every line in a file and split at blank space using javascript?

Currently I have this in htmlcss.txt
a.html 1.css
b.html 2.css
c.html 3.css
d.html 4.css
e.html 5.css
How do I extract every line in this file and split at the blank space so that my array consists of
[a.html, 1.css]---->first iteration
[b.html, 2.css]---->second iteration...and so on
For use in this code:
var i = 0;
for ( i=0; i<5; i++ ) { //there are 5 lines in my file
var arr; //array of length 2
//how to create this array so that it contains the aforementioned data?
var n=arr[0];
var m=arr[1];
//Some tasks with m and n
}
I have searched a lot and this is probably a stupid question, but I have spent quite a bit of time already with no luck yet. Any suggestions?
To read a file you may use NodeJS' readFile() that will put it into a string.
Note that readFile() operates asyncronously. So it doesn't stop, read the file then proceed. Instead it start reading the file the proceeds with the next statement. In order to process the file you must place this functionality inside a callback that's provided as parameter to readFile()
See https://docs.nodejitsu.com/articles/file-system/how-to-read-files-in-nodejs
Then you may use split() method that applies to strings to do subsequent tasks.
First split the file by line feeds \n (assuming unix linefeeds)
Then split by spaces.
fs = require('fs')
fs.readFile('htmlcss.txt', 'utf8', function (err,str) {
var lines, // array: 1 item for every line
count, // how many lines
i,
arr, // array: 1 item for every space-separated item
m,
n;
if (err) {
return console.log(err);
}
lines = str.split("\n");
count = lines.lenght; // are you sure the lines are 5 ? Let's get it at runtime.
for ( i=0; i<count; i++ ) { //there are 5 lines in my file
arr = lines[i].split(" ");
n=arr[0];
m=arr[1];
// Some tasks with m and n
}
});
I've put variable decalaration at the beginning for better readability.

javascript extract parts of a sentence

Hi I'm not that experienced in JavaScript, and I'm trying to parse commands given as full sentences. It has to be able to match the sentence to a function that will answer, and select sections that it will pass as arguments. I know that's not too clear so here's an example:
sentnce = "Show me a gift of a car"
commands = {
gif: {
pattern: "*gif of a [target]*"
action: gifMe(target)
This scenario should result in the call gifMe("a car") or even better, gimme could be called with a object containing all of the arguments specified, there may be more than just target. I have no idea how to go about coding this or searching for a method to do this. Thanks in advance.
It's not relevant (I think) but I'm using jquery and Coffeescript.
I think this is the code you are looking for. See the comment in the code for some more information about how it works.
var sentence = "Show me a gift of a car";
// specify commands
var commands = {
gif: {
pattern: /gift of a (.*).?/,
action: call
}
// you can add more commands here
}
// iterate over all commands
for(var key in commands)
{
// get settings for command
var settings = commands[key];
// apply command regex to sentence
var result = sentence.match( settings.pattern );
// if there is a match trigger the action with the word as argument
if( result && result.length > 1 )
settings.action.call(this, result[1]);
}
function call(value)
{
alert(value);
}

Categories

Resources