Load text from a .txt file and display in jsPsychHtmlButtonResponse - javascript

I want to use jsPsychHtmlButtonResponse to display a .txt-file from a local folder on my computer.
The reproducible code below does not work, while a very similar adaptation works for the display of images with jsPsychImageButtonResponse.
Does not work:
var text_display = {
type: jsPsychHtmlButtonResponse,
stimulus: text_files/test.txt,
choices: ["Ready"],
};
Works:
var image_display = {
type: jsPsychImageButtonResponse,
stimulus: images_files/test.png,
choices: ["Ready"],
};
Do you have any suggestions how to handle this problem?

Text files behave differently from image files in this context. While browsers can load an image file directly from a path, text files need to be loaded using more generic methods. This question has answers that demonstrate how text files are loaded. You could use fetch() to load in all the text files and assign them to variables that you then use in your experiment.
Alternatively, if you want something that is as close as possible to this experience without getting into fetch() calls, then you could put the text from your text file inside a JavaScript file and assign it to a variable.
var test_txt = `Contents from text file`;
Suppose that this has the filename test_txt.js. You can then load this in your HTML doc like you load other scripts (e.g., jspsych.js).
<head>
<script src="test_txt.js"></script>
</head>
Then you can use the test_txt variable in your code.
var text_display = {
type: jsPsychHtmlButtonResponse,
stimulus: test_txt,
choices: ["Ready"],
};

Related

Pass a file of a database as parameter to be opened with a html viewer

Premise
I am not much expert in this, but I have superficial familiarity with HTML, CSS, vanilla JS.
I am creating for let say for myself an application in HTML 5, CSS 3, vanilla JS ECMA script 6, so with NO use of frameworks as jQuery or else: just vanilla JS, and I also don't care about older browser that are not HTML 5 ES6 CSS 3 compliant, like IE.
The application runs as simple HTML/CSS/JS file on the local computer only: so no remote server is involved but it is a portable app I store and use on my computer or on a pen drive or I send to other people as my family to show things to them.
I know about limits imposed to HTML/JS to open files on its own and about the need for user interaction by the use of input type="file" html element... but nevertheless might be there is another way I am not familiar with yet, if I'm lucky...
Situation
I have an already working html file, let's call it Manager.html. It contains a table that is populated with a list of files, listed as:
File 1.db
File 2.db
File 3.db
and much much more of them...
Also, each file is a URL, for instance:
file 1.db
If I have to, I can change their extension to *.json, *.csv, *.tsv, *.db, *.txt, or any other that could work as long as it is a data related extension for files containing text only, no problem at all for that.
I have an already working html file that acts as a viewer let's call it Viewer.html: it loads data from those other db files by using at the moment a canonical input type="file". It loads the content of the chosen database and displays it into the table handled by Manager.html and the relative js that handles the loading process and the process to populate the table.
The db file used is a custom kind of "csv"-like-file (coma separated value format file) customized by myself: it contains basically text only content and uses normal \CR\LF to separate records and the pipe symbol "|" (no double quotes) instead of commas, for separating fields. For instance:
text of field 1|text of field 2|text of field 3|text of field n
text of field 1|text of field 2|text of field 3|text of field n
text of field 1|text of field 2|text of field 3|text of field n
and more records ...
The content of the db files is utf8 text and it is not limited in quantity, so:
a db file could be of any size: from few Bytes or KB and few records or even hundreds or thousands of KB and hundreds or even thousands of records: so can be present any number of records.
a record could be any length, with a fixed number of fields, corresponding to the number of fields of the target html table in Viewer.html
a field can contain text of any length as well
At the moment everything works well per se with the input type="file" implementation, but I want to implement a different feature and improve my user experience because at the moment I have to:
open Viewer.html
here I have to click on the input type="file" control to open the "open file window"
from the "open file window" I have to select the File n.db file I wish to load in Viewer.html to open it and populate the table into Viewer.html.
All this is of course super tedious.
So what I want is to be able to:
Of course directly from Manager.html, that holds the table with the main list of all File n.db I want just to:
click on the url of the File n.db file I want to open, listed inside the table in the file Manager.html.
And with that just one click I want the javascript to:
open the Viewer.html
pass to Viewer.html as parameter the File n.db file to be processed
Viewer.html opens it by its own, process it, and shows its content into its table.
In other words I'm looking for a function that can do something similar to:
Pseudo code:
open(Viewer.html, File n.db)
will also work as well something similar to:
<database src="MyDBsubDir/MyDBfile.db"></database>
Would be great if it works with standard db files as csv, tsv, json, db, txt, and so on, containing just Unicode text in it.
It is relevant/Crucial
The use of the Manager.html to list all File n.db files to click on for opening.
The use of just one common Viewer.html, so it will be easier to maintain and in case of need could be updated just once for debugging or to implement new features in case of need.
Handle an unlimited number of File n.db files (or with other extensions)
Questions
Is it possible for the user (typically myself or family or friends) which is showing Manager.html click on a link of the file and pass its href value as parameter to the other file Viewer.html to be processed and shown here?
If Yes, how do I implement a function that does something like that in vanilla JS?
Basically the function activated on mouse click on the link will have to get the text content of the file n.db file under the href attribute of the same clicked link, and will "inject" / "fuse" on the fly such a content with the Viewer.html itself that will provide the correct formatting of it as html table so can be shown on the browser as a normal html page instead of just text.
Notice that
As already said: the solution I'm looking for, if any, must be compatible with only HTML 5, ES6 compliant browsers (so I really don't care about IE and similar others, which are dead for me). I repeat: all has to work on a "local" machine (Windows, Linux, MAC, Android... and more), no server of any kind has to be involved.
The ideal solution would be a fetch() like function if it worked on local files, but unfortunately it doesn't, as far as I know.
Also it doesn't work either to compile by JavaScript a input type=file with the file because of course it is not allowed for security reasons.
Ideal behavior
IMHO the best way to go to solve this limit once and for all without putting in jeopardy the security of the local system, would be to implement in all browsers a standard behavior that asks to the user the authorization to access to app directory and its sub-dirs, similarly as when the browsers ask the user for authorization to use the microphone. This will allow the user to decide if a local app is authorized to access to its own directory. This authorization has to be a per-session authorization: so it is given every time the app is opened into the browsers.
There are alternatives.
Key factor #1 is : "If I have to, I can change their extension to *.json, or any other that could work, no problem at all for that.". This matters because you are open to using a file format that browsers are still happy to open locally.
Key factor #2 is : "Of course directly from Manager.html, that holds the table with the main list of all File n.db". This matters because browsers are not and should not be allowed to read your local drive without your intervention, but a list or urls gets us pretty far.
The first solution, and the simplest one from a technical perspective, is to save your database files as html files that already look the way you want them to. You just open them when you click on the link. You basically don't have a viewer anymore since your files are directly viewable.
If the thought of adding markup all throughout your data does not appeal, another possible solution is to define your data as a plain object, and have a little javascript function to render it on the page. Again, no viewer needed since the files render themselves.
Here is a rough example of a database.html
<head>
<style>
.hidden-frame {
display: none;
}
</style>
<script>
// Your plain object data is here
const data = [
['text of field 1', 'text of field 2', 'text of field 3', 'text of field n'],
['text of field 1', 'text of field 2', 'text of field 3', 'text of field n'],
['text of field 1', 'text of field 2', 'text of field 3', 'text of field n']
];
</script>
</head>
<body>
This is the data...
<div id="viewer"></div>
<script>
//Your vanilla javascript render function is here
document.getElementById('viewer').innerHTML = data.map(r => `<div>${r.join(', ')}</div>`).join('');
</script>
</body>
If the idea of having a render function in your database is also problematic, you could leverage an iframe and get the data from the database through that and render it in the main html file.
Here is a rough example. The following manager.html has just one hardcoded "db file example" named filedb_example.html instead of a list because I am lazy. Your actual list can be as fancy as you want with the user experience there. You don't need a framework for that. Also, here again, no separate viewer.
<head>
<style>
.hidden-frame {
display: none;
}
</style>
<script>
// This is the event handler that receives the data from the loaded db file
const eh = (e) => {
// The render function is in the main html file now
document.getElementById('viewer').innerHTML = e.data.map(r => `<div>${r.join(', ')}</div>`).join('');
}
// We register the handler to receive messages from the db files when they load
window.addEventListener('message', eh);
</script>
</head>
<body>
This is the manager, and bellow is the data I loaded from the db file.
<div id="viewer">Nothing loaded yet!</div>
<iframe class="hidden-frame" src="file:///c:/work/js/filedb_example.html" id="loader"></iframe>
</body>
Your html pseudo databases now look something like this filedb_example.html :
<head>
<script>
// This is where the actual data is set.
const data = [
['text of field 1', 'text of field 2', 'text of field 3', 'text of field n'],
['text of field 1', 'text of field 2', 'text of field 3', 'text of field n'],
['text of field 1', 'text of field 2', 'text of field 3', 'text of field n']
];
// As soon as it is loaded, it sends the data to the top frame
window.top.postMessage(data, "*");
</script>
</head>
<body>
This is a db file and is not meant to be loaded directly :P
</body>
If you want to keep your Manager.html and Viewer.html separate, you can create a dedicated Viewer.html and simply use a query parameter to pass to it the file you want to view like, but you will probably need to url-encode your file path and use front slashes. Example link file:///Viewer.html?file_db=c%3A%2Fwork%2Fjs%2Ffiledb_example.html
A rough viewer would be something like this :
<head>
<style>
.hidden-frame {
display: none;
}
</style>
<script>
// A function to extract the query parameters
const params = new Proxy(new URLSearchParams(window.location.search), {
get: (usp, p) => usp.get(p),
});
// The same rendered
const eh = (e) => {
document.getElementById('viewer').innerHTML = e.data.map(r => `<div>${r.join(', ')}</div>`).join('');
}
window.addEventListener('message', eh);
</script>
</head>
<body>
This is the viewer, and below the data from the file I loaded.
<div id="viewer"></div>
<iframe class="hidden-frame" id="loader"></iframe>
<script>
// Now that the viewer is loaded, we load the database file
document.getElementById('loader').src = `file:///${params.file_db}`;
</script>
</body>
So there you go, options.
You can check the FileSystemDirectoryEntry API in javascript it allows your app to access a local directory here is a link to the mozilla documentation page
https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry
Here is an example on how to use this there are a lot of drawbacks as this is stil experimental features and chrome does not let you access the api if you open the file from the file explorer instead of serving it, to make this work you will have to launch chrome with this flag --allow-file-access-from-files or serve the file with something like live server
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="explorer">
</div>
<button id="open">Sync</button>
<div id="content"></div>
<script>
const $ = (s, e = document) => e.querySelector(s);
const e = (e) => document.createElement(e);
const getDb = async () => {
const dir = await window.showDirectoryPicker();
let it = dir.entries();
let entry = await it.next();
while (entry.done === false) {
const [fileName, fileHandle] = entry.value;
const fileInAppDir = await rootAppDir.getFileHandle(fileName, { "create": true });
const fileData = await fileHandle.getFile();
const writable = await fileInAppDir.createWritable();
await writable.write(fileData);
await writable.close();
$('#explorer').appendChild(e('div')).innerText = fileName;
entry = await it.next();
}
}
const getAppFiles = async () => {
for await (const handle of rootAppDir.values()) {
$('#explorer').appendChild(e('div')).innerText = handle.name;
}
}
const showFileContents = async (fileName) => {
const fileHandle = await rootAppDir.getFileHandle(fileName);
const file = await fileHandle.getFile();
const reader = new FileReader();
reader.onload = function (event) {
$('#content').innerText = event.target.result;
};
reader.readAsText(file);
}
let rootAppDir;
const main = async () => {
rootAppDir = await navigator.storage.getDirectory();
await getAppFiles();
$('#explorer').addEventListener('click', async (e) => {
const fileName = e.target.innerText;
await showFileContents(fileName);
});
$('#open').addEventListener('click', getDb);
}
main();
</script>
</body>
</html>

How do I save an array to a file and manipulate it from within my code?

This is in p5.js which includes most javascript functions!
I am trying to make a save-file for my game. By this I mean: the user presses the save button in my game. It updates an array that is saved on a file included in the game package, the player keeps playing. How would I do something like this (creating files that can be accessed by my code and changed).
var SM = {
//save files
sf1: [1,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
sf2: [1,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
sf3: [1,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
};
One more thing (FOR PROCESSING CODERS FROM HERE ON): I tried to use processing functions like saveStrings(); and loadStrings(); but I couldn't get saveStrings() to save to a specific location nor could I properly load a txt file. Here is the code I used for that:
var result;
function preload() {
result = loadStrings('assets/nouns.txt');
}
function setup() {
background(200);
var ind = floor(random(result.length));
text(result[ind], 10, 10, 80, 80);
}
I had a folder called assets within the sketch folder and assets had a txt file called nouns with strings in it (downloaded from saveStrings then manually moved) but the sketch wont go past the loading screen?
If you are running it from a browser, you can't save or load a file how you want, period. Saving and loading files in browser JavaScript involves user interaction, and they get to pick the file and where it saves.
If you want to save it locally, instead of trying to write it to a file, you should write and read it from localStorage, which you can then do just fine.
// save
localStorage.setItem('saveData', data);
// load
const data = localStorage.getItem('saveData');
If it is somehow a game run directly on the client (out of the browser), like written in Node.js, then you'd want to use the fs functions.
To expand a bit, if you have your save data as an object:
const saveData = {
state: [1,2,3],
name: 'player'
};
Then to save it, you would simply call:
localStorage.setItem('saveData', JSON.stringify(data));
You'll want to stringify it when you save it to make it work properly. To read it back, you can then just read it back with getItem():
const data = JSON.parse(localStorage.getItem('saveData') || '{}');
(That extra || '{}' bit will handle if it hasn't been saved before and give you an empty object.)
It's actually much easier than trying to write a JavaScript file that you would then read in. Even if you were writing a file, you'd probably want to write it as JSON, not JavaScript.
In order to save strings into a file in Javascript, I would recommand you this previous StackOverflow question, which provides a link to a very clear and easy-to-use library to manage files in Javascript.

set file attribute filesystemobject javascript

I have created a file as part of a script on a network drive and i am trying to make it hidden so that if the script is run again it should be able to see the file and act on the information contained within it but i am having trouble doing this. what i have so far is:
function doesRegisterExist(oFs, Date, newFolder) {
dbEcho("doesRegisterExist() triggered");
sExpectedRegisterFile = newFolder+"\\Register.txt"
if(oFs.FileExists(sExpectedRegisterFile)==false){
newFile = oFs.OpenTextFile(sExpectedRegisterFile,8,true)
newFile.close()
newReg = oFs.GetFile(sExpectedRegisterFile)
dbEcho(newReg.Attributes)
newReg.Attributes = newReg.Attributes+2
}
}
Windows Script Host does not actually produce an error here and the script runs throgh to competion. the only guides i have found online i have been attempting to translate from VBscript with limited success.
variables passed to this function are roughly declared as such
var oFs = new ActiveXObject("Scripting.FileSystemObject")
var Date = "29-12-2017"
var newFolder = "\\\\File-Server\\path\\to\\folder"
I know ActiveX is a dirty word to a lot of people and i should be shot for even thinking about using it but it really is a perfect fit for what i am trying to do.
Please help.
sExpectedRegisterFolder resolves to \\\\File-Server\\path\\to\\folder\\Register which is a folder and not a file.
I get an Error: file not found when I wrap the code into a try/catch block.
I tested the code on a text file as well, and there it works.
So you're either using the wrong method if you want to set the folder to hidden.
Or you forgot to include the path to the text if you want to change a file to hidden.
( Edit: Or if Register is the name of the file, add the filetype .txt ? )
If you change GetFile to GetFolder as described in https://msdn.microsoft.com/en-us/library/6tkce7xa(v=vs.84).aspx
the folder will get hidden correctly.

How to read the contents of a file inside an epub using javascript

I need to read, inside a page of an epub3 book, the contents of one of the file of that same epub, being some data to process with a javascript function.
Unfortunately, Javascript prevents from loading local files for security reasons. (e.g the File API only allows loading uploaded user files).
But for me in the context of an epub3, it makes sense and I didn't find any information in the IDPF EPUB3 documentation related to this topic.
Any idea ?
OK. Let's clarify:
I have an epub3 with the following structure:
<root>
META-INF
...
OEBPS
pages
page.xhtml
data
data.xml
In page.xhtml, I want to write in Javascript something like:
<script type="text/javascript">
//pseudo code
var indata = readAsText("../data/data.xml"); // how to write that ???
var outdata = myfunction(indata);
</script>
Found the solution for ages, and realized that it had never been answered:
So answering to my own question ;-)
<script type="text/javascript">
/* Load the file using HTTP GET */
$.get( "../data/data.xml", function( indata ) {
var outdata = myfunction(indata);
}, 'text');
</script>

Failed To Load Resource Error

I am having trouble viewing 3 different background-image .jpg files through one thumbnailFilePath in javascript. The HTML and CSS coding does recognize all of the files correctly, but the background images will not load into a browser. Therefore, you can not view these images. On the other hand, the browser does recognize to see the javascript videocaption text and the play_icon.png image files correctly.
// JavaScript Document
$(document).ready(function(){
$('a.videoLink').each(function(){
var thumbnailFilePath = 'video/'+$(this).attr('videofile')+'.jpg';
var videoCaption = $(this).attr('videocaption');
$(this).css('background-image','url('+thumbnailFilePath+')');
$(this).html('<div class="caption">'+videoCaption+'</div><img src="../images/play_icon.png" class="play"/>');
});
});
Maybe your video directory is not located in the same directory like your javascript file with this code? Otherwise use a additional slash in front of "video", if it is in the root:
var thumbnailFilePath = '/video/'+$(this).attr('videofile')+'.jpg';
What's the value of the following attribute?
.attr('videofile')
Did you give attention to the file extension? (maybe result is: myvideofile.mpg.jpg)
The answer is that my .jpg files must have the identical file name as my HTML source code. The videofile="bruce_waltke" must have the same .jpg name. So my .jpg file was named bruce waltke.jpg with no underscore in-between. So the .jpg was initially saved as bruce waltke.jpg but it is now saved as bruce_waltke.jpg....So the image file was broken but its now fixed.

Categories

Resources