I have a data file that I need to extract data from. It has to be submitted via html input. I want to make a DataReader class to do so. The class needs to be constructed with the file and extract its attributes from the file, so
class DataReader() {
constructor(file) {
this.file = file;
}
extractData(){...}
}
var data = new DataReader(file);
console.log(data.attr0, data.attr1, data.attr3, ...);
I keep getting scope issues and I don't know how to make the class take a file when instantiated so it separates it's attributes.
This is what I have
class DataReader {
setFile(file) {
this.file = file;
}
getFile() {
return this.file;
}
extractData() {
var self = this; // this == DataReader
var objXMLhttp = new XMLHttpRequest();
objXMLhttp.open("GET","diabetes400.txt",true);
objXMLhttp.send();
objXMLhttp.onreadystatechange = function() {
if (objXMLhttp.readyState==4 && objXMLhttp.status==200) {
var arrContents = objXMLhttp.responseText.split("\n"); // gotcha!
self.setFile(arrContents);
console.log(arrContents);
}
}
}
}
var data = new DataReader();
data.extractData();
console.log(data.file);
If I log only data, i can see an object with file inside it, but file prints undefined
I think you need a FileReader.
Look at this project: http://bgrins.github.io/filereader.js/.
You can read a file an get the contents of it.
Look this example:
var inputUpload = document.getElementById('file-input');
FileReaderJS.setupInput(inputUpload , {
on: {
load: function(event, file){
var textBase64 = event.srcElement.result
var decodedText = atob(e.srcElement.result.split("base64,")[1]);
}
}
});
Related
I want to write a function in javascript that reads text file and if it contains any javascript function in that text file then instead of reading it just as text, my function will read it as javascript.
Like:
const toBeRead = () => true; //This function is in text file
Above function is in text file and I want to read it as javascript. Currently I am reading this as a string but wanted to take output from it which is true.
Editing for better demonstration. Here is my function after using eval()
function readSingleFile(evt) {
var f = evt.target.files[0];
if (f) {
var r = new FileReader();
r.onload = function (e) {
var contents = e.target.result;
var ct = r.result;
var words = ct.split("\n");
let array = [];
words.map((word) => {
if (word.substring(0, 6) === "const ") {
if (word.includes("=>")) {
if (word.includes("=")) {
alert(word); //It shows const toBeRead=()=>true;
alert(eval(word)); // It shows undefined
array.push(word);
}
}
}
return array;
});
alert(array);
};
r.readAsText(f);
} else {
alert("Failed to load file");
}
}
Actually I am creating a rule for coding convention like if functions return boolean then it should start with "is/has". Like const isFuncTrue=()=>true; Don't have rule defined in Eslint!
Two methods
eval(jsstr) - not recommended
script:
Example in browser - will not run if no DOM
const str = `const toBeRead = () => true; //This function is in text file`
const scr = document.createElement("script")
scr.textContent = str;
document.body.appendChild(scr)
console.log(toBeRead())
I have 2 soy.js and lib-dialogs.js files.
I need to make lib-dialogs pass the value of the lineCount variable to soy.js.
I was able to do this with localStorage but because it saves in a cookie it does not update the values correctly.
In lib-dialogs there is a function called BlocklyDialogs.congratulations that calls the necessary data.
FIle:lib-dialogs.js
BlocklyDialogs.congratulations = function() {
// Add the user's code.
if (BlocklyGames.workspace) {
var linesText = document.getElementById('dialogLinesText');
linesText.textContent = '';
// Line produces warning when compiling Puzzle since there is no JavaScript
// generator. But this function is never called in Puzzle, so no matter.
var code = Blockly.JavaScript.workspaceToCode(BlocklyGames.workspace);
code = BlocklyInterface.stripCode(code);
var noComments = code.replace(/\/\/[^\n]*/g, ''); // Inline comments.
noComments = noComments.replace(/\/\*.*\*\//g, ''); /* Block comments. */
noComments = noComments.replace(/[ \t]+\n/g, '\n'); // Trailing spaces.
noComments = noComments.replace(/\n+/g, '\n'); // Blank lines.
noComments = noComments.trim();
var lineCount = noComments.split('\n').length;
var pre = document.getElementById('containerCode');
pre.textContent = code;
if (typeof prettyPrintOne == 'function') {
code = pre.innerHTML;
code = prettyPrintOne(code, 'js');
pre.innerHTML = code;
}
if (lineCount == 1) {
var text = BlocklyGames.getMsg('Games_linesOfCode1');
} else {
var text = BlocklyGames.getMsg('Games_linesOfCode2')
.replace('%1', String(lineCount));
}
linesText.appendChild(document.createTextNode(text));
}
FIle:soy.js
example "var count = BlocklyDialogs.congratulations(lineCount);"
In soy.js I need to receive the values of lineCount. I've already managed to do this using localStorage but I needed to do something more direct.
In testing I verified that the problem is in the lineCount variable because it is not passing a value to any variable even within the file itself.
I created a variable outside the blocklyDialogs.congratulations function and entered a value of 5.
I called the variable in the soy.js file and got it normally.
I need to make the lineCount pass its value.
You can use event driven programming, pub-sub model.
class EventManager {
constructor(...listeners) {
this.listeners = listeners;
}
register(fn) {
const id = this.listeners.push(fn);
return () => {
this.listeners.splice(id - 1, 1);
};
}
emit(data) {
this.listeners.forEach(fn => fn(data));
}
}
const pushEvents = new EventManager();
// JS 1
const unsubscribe1 = pushEvents.register(x => {
console.log("event:", x);
});
pushEvents.register(x => {
console.log("event:", x);
});
// JS 2
pushEvents.emit("Tets data");
//Output
// event: Tets data
// event: Tets data
unsubscribe1();
pushEvents.emit("Tets data2");
//Output
// event: Tets data2
.as-console-row {color: red!important}
I have a function prototype that loads data from a path. The trick is that I need to change the path afterward. I tried call, apply, bind and even assign but as I am a novice I did not find the solution.
Here a sample of my code :
Chat.prototype.loadMessages = function() {
this.messagesRef = this.database;
var setMessage = function(data) {
var val = data.val();
this.displayMessage(data.key, val.name, val.text);
}.bind(this);
};
var chat = new Chat
function setPath (newpath) {
chat.loadMessages.messageRef = newpath; // I guess, it is where I'm wrong...
chat.loadMessages(); // It should load messages from the new path in my chat container.
}
As I said I also tried :
chat.loadMessages.call(newpath);
or
var setPath = function(newpath) {
chat.loadMessages(newpath);
}.bind(chat);
setPath();
chat.loadMessages();
But the chat container continues to disclose messages from the old path...
This looks a bit convoluted. Just pass messagesRef as a parameter and make it default to this.database:
Chat.prototype.loadMessages = function(messagesRef = this.database) {
// do whatever is needed with messagesRef
};
chat = new Chat();
chat.loadMessages(); // load from the default location
chat.loadMessages('foobar'); // load from this specific location
It looks like you are creating a function with loadMessages, which is fine but you need to pass in a value to set the new path. Is this more of what you were thinking?
Chat.prototype.loadMessages = function (newPath) {
this.messagesRef = newPath || this.database; // if newPath is empty than default to this.database
var setMessage = function(data) {
var val = data.val();
this.displayMessage(data.key, val.name, val.text);
};
var chat = new Chat
function setPath (newpath) {
chat.loadMessages(newpath);
}
I'm using an object with information for a bookmark tagging system that needs to persist across Chrome sessions, so I'm trying to save it to local storage and update it whenever a new bookmark is created.
When I create a new bookmark, I fire a function to see if there are now any other bookmarks with the same tag as the new bookmark. This organizes bookmarks into "tag groups" that function kind of like dynamic folders.
When I set the storage object, the object being stored has all the data I'd expect. However, as soon as I get the same object out of storage, one of the nested objects mysteriously turns to null. See console output: the top object is just before the set call in function updateStorage. The bottom is what I get back when I "get" that object from storage. Notice the tagGroups bookmarks are now null. The bookmarks themselves are still there, it's only in the tag group object that they disappear. I've spent a full day and night messing around with this trying to get it to work.
Here is the model code. I included everything for context, but the most relevant pieces are the createNewBookmark, updatePrimaryTreeWithTagGroups, and updateStorage methods.
UPDATE: I've edited the code to make all the changes to the bookmarks tree before setting/getting anything from storage, then making a final call to update storage with the resulting object. I'm literally storing one thing, one time, and getting back another whenever I try to retrieve.
function PrimaryBookmarksTree(){
chrome.storage.sync.get(null, this.findOrCreate.bind(this));
}
PrimaryBookmarksTree.prototype.findOrCreate = function(result){
if (result.bookmarksTree != undefined){
this.bookmarks = result.bookmarksTree.bookmarks;
this.title = result.bookmarksTree.title;
this.tagGroups = result.bookmarksTree.tagGroups;
console.log(this);
} else {
this.bookmarks = [];
this.title = "Marinade Bookmarks";
this.tagGroups = [];
chrome.storage.sync.set({"bookmarksTree": this}, function(){console.log("New tree created!")});
console.log(this);
}
}
function Bookmark(name, tags, url){
this.name = name;
this.tags = tags;
this.url = url;
this.dateCreated = this.date();
}
function TagGroup(tag){
this.bookmarks = [];
this.tag = tag;
}
//called by controller when user tags a new bookmark via the extension
PrimaryBookmarksTree.prototype.createNewBookmark = function(name, tags, url){
var newBookmark = new Bookmark(name, tags, url);
this.bookmarks.push(newBookmark);
this.tagGroups = this.updatePrimaryTreeWithTagGroups();
this.updateStorage(this);
}
PrimaryBookmarksTree.prototype.updatePrimaryTreeWithTagGroups = function(){
var tagsForGrouping = this.getTagsWithMultipleBookmarks(this.bookmarks);
for(j=0;j<tagsForGrouping.length;j++){
this.tagGroups.push(this.buildTagGroup(tagsForGrouping[j]));
}
return this.tagGroups;
}
PrimaryBookmarksTree.prototype.getTagsWithMultipleBookmarks = function(bookmarks){
var tagsToCheck = this.pluck(bookmarks, "tags");
var tagCounts = tagsToCheck.reduce(function (obj, curr){
if (typeof obj[curr] == 'undefined') {
obj[curr] = 1;
} else {
obj[curr] += 1;
}
return obj;
}, {});
var tagGroups = this.filter(tagCounts, function(x){return x > 1});
return tagGroups;
}
PrimaryBookmarksTree.prototype.buildTagGroup = function(tag){
tagGroup = new TagGroup(tag);
for(i=0;i<this.bookmarks.length;i++){
if(this.bookmarks[i].tags[0] == tag){
tagGroup.bookmarks.push(this.bookmarks[i]);
}
}
if (tagGroup.bookmarks.length != 0){
return tagGroup;
}
}
PrimaryBookmarksTree.prototype.updateStorage = function(updatedTree){
console.log(JSON.stringify(updatedTree));
chrome.storage.sync.set({"bookmarksTree": updatedTree}, function(){console.log("final storage complete")});
}
You are always setting this.tagGroups to undefined when you retrieve your data from the sync storage:
PrimaryBookmarksTree.prototype.findOrCreate = function(result){
if (result.bookmarksTree != undefined){
this.bookmarks = result.bookmarksTree.bookmarks;
this.title = result.bookmarksTree.title;
this.tagGroups = result.tagGroups; // should be result.bookmarksTree.tagGroups
console.log(this);
}
}
This is probably a noob JavaScript question, but I'm looking to know if my solution to a problem I am having is 'correct'
I have created the following sample application that recreates my error:
Firstly in index.js
var processor = require('./fileProcessor/processor.js');
var container = {
source: "source.txt",
destination: "destination.txt"
};
new processor().process(container);
I create my container object which has the name of the source file and the name of the destination file. This is passed into the process function of the processor:
var fileProcessor = require('./fileProcessor.js');
module.exports = function Processor() {
this.process = function(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, this.process);
} else {
file.write(container, this.process);
}
}
};
};
As you can see this calls the read and write functions passing in the container and the process function as the callback, the fileProcessor looks like this:
var fs = require('fs');
module.exports = function() {
this.read = function(container, callback) {
fs.readFile(container.source, function (err, data) {
if(err) throw err;
container.body = data;
callback(container);
});
};
this.write = function(container, callback) {
fs.writeFile(container.destination, container.body, function(err) {
if(err) {
return console.log(err);
}
container.finished = true;
callback(container);
});
};
};
In simple terms the processor calls file.read, which reads the file and calls back into the process function, which then calls the write function. However at the end of the write function an error is thrown:
callback(container);
^
TypeError: object is not a function
Obviously when passing in this.process to file.write(container, this.process); the this isn't the this I intend it to be!
If I update my processor by adding a processFunction variable:
var fileProcessor = require('./fileProcessor.js');
module.exports = function Processor() {
var processFunction = function(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, processFunction);
} else {
file.write(container, processFunction);
}
}
};
this.process = function(container) {
processFunction(container);
};
};
Everything works fine. Is this a good way to do this or is there a better solution?
I think this is a fine way to do it. There is one possible modification that you might make. Since you are creating a new name in your scope just for the purpose of recursing, you could just name your function and refer to it by its name inside of the function.
module.exports = function Processor() {
this.process = function processFunction(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, processFunction);
} else {
file.write(container, processFunction);
}
}
};
};
Then you can avoid creating a name (processFunction) that will be visible outside the function.
Take a look here for reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function#Named_function_expression