Optimize a search & insert text to a gcode file - javascript

I am trying to process a *.gcode file (plain text) that is currently 2.5mb in size, but could be as large as 50mb. In the process it searches for the indexOf a key and then it finds the following new line character to insert the specific script needed to modify the gcode. For a 2.5mb file it takes nearly 6 seconds to process this request. I'd like to shorten this process if possible and test it against a 50mb file.
Thx for any input.
var loc_array = [2.500, 10.000];
var pos = 0;
for(var i = 0; i < loc_array.length;i++){
//Find Line
var p = gcode.indexOf("END_LAYER_OBJECT z="+loc_array[i], pos);
if (p != -1){
//Go to the following line and insert String
p = gcode.indexOf("\n", p);
gcode = gcode.slice(0, p)+ "G28 X\nM117\nM0\nG28 X"+gcode.slice(p);
pos = p;
}
}

Related

Javascript performance optimization

I created the following js function
function csvDecode(csvRecordsList)
{
var cel;
var chk;
var chkACB;
var chkAF;
var chkAMR;
var chkAN;
var csvField;
var csvFieldLen;
var csvFieldsList;
var csvRow;
var csvRowLen = csvRecordsList.length;
var frag = document.createDocumentFragment();
var injectFragInTbody = function () {tblbody.replaceChild(frag, tblbody.firstElementChild);};
var isFirstRec;
var len;
var newEmbtyRow;
var objCells;
var parReEx = new RegExp(myCsvParag, 'ig');
var tblbody;
var tblCount = 0;
var tgtTblBodyID;
for (csvRow = 0; csvRow < csvRowLen; csvRow++)
{
if (csvRecordsList[csvRow].startsWith(myTBodySep))
{
if (frag.childElementCount > 0)
{
injectFragInTbody();
}
tgtTblBodyID = csvRecordsList[csvRow].split(myTBodySep)[1];
newEmbtyRow = getNewEmptyRow(tgtTblBodyID);
objCells = newEmbtyRow.cells;
len = newEmbtyRow.querySelectorAll('input')[0].parentNode.cellIndex; // Finds the cell index where is placed the first input (Check-box or button)
tblbody = getElById(tgtTblBodyID);
chkAF = toBool(tblbody.dataset.acceptfiles);
chkACB = toBool(tblbody.dataset.acceptcheckboxes) ;
chkAN = toBool(tblbody.dataset.acceptmultiplerows) ;
tblCount++;
continue;
}
csvRecordsList[csvRow] = csvRecordsList[csvRow].replace(parReEx, myInnerHTMLParag); // Replaces all the paragraph symbols ΒΆ used into the db.csv file with the tag <br> needed into the HTML content of table cells, this way will be possible to use line breaks into table cells
csvFieldsList = csvRecordsList[csvRow].split(myEndOfFld);
csvFieldLen = csvFieldsList.length;
for (csvField = 0; csvField < csvFieldLen; csvField++)
{
cel = chkAN ? csvField + 1 : csvField;
if (chkAF && cel === 1) {objCells[cel].innerHTML = makeFileLink(csvFieldsList[csvField]);}
else if (chkACB && cel === len) {objCells[cel].firstChild.checked = toBool(csvFieldsList[csvField]);}
else {objCells[cel].innerHTML = csvFieldsList[csvField];}
}
frag.appendChild(newEmbtyRow.cloneNode(true));
}
injectFragInTbody();
var recNum = getElById(tgtTblBodyID).childElementCount;
customizeHtmlTitle();
return csvRow - tblCount + ' (di cui '+ recNum + ' record di documenti)';
}
More than 90% of records could contain file names that have to be processed by the following makeFileLink function:
function makeFileLink(fname)
{
return ['<a href="', dirDocSan, fname, '" target="', previewWinName, '" title="Apri il file allegato: ', fname, '" >', fname, '</a>'].join('');
}
It aims to decode a record list from a special type of *.db.csv file (= a comma-separated values where commas are replaced by another symbol I hard-coded into the var myEndOfFld). (This special type of *.db.csv is created by another function I wrote and it is just a "text" file).
The record list to decode and append to HTML tables is passed to the function with its lone parameter: (csvRecordsList).
Into the csv file is hosted data coming from more HTML tables.
Tables are different for number of rows and columns and for some other contained data type (which could be filenames, numbers, string, dates, checkbox values).
Some tables could be just 1 row, others accept more rows.
A row of data has the following basic structure:
data field content 1|data field content 2|data field content 3|etc...
Once decoded by my algorithm it will be rendered correctly into the HTML td element even if into a field there are more paragraphs. In fact the tag will be added where is needed by the code:
csvRecordsList[csvRow].replace(par, myInnerHTMLParag)
that replaces all the char I choose to represent the paragraph symbol I have hard-coded into the variable myCsvParag.
Isn't possible to know at programming time the number of records to load in each table nor the number of records loaded from the CSV file, nor the number of fields of each record or what table field is going to contain data or will be empty: in the same record some fields could contain data others could be empty. Everything has to be discovered at runtime.
Into the special csv file each table is separated from the next by a row witch contains just a string with the following pattern: myTBodySep = tablebodyid where myTBodySep = "targettbodydatatable" that is just a hard coded string of my choice.
tablebodyid is just a placeholder that contains a string representing the id of the target table tbody element to insert new record in, for example: tBodyDataCars, tBodyDataAnimals... etc.
So when the first for loop finds into the csvRecordsList a string staring with the string into the variable myTBodySep it gets the tablebodyid from the same row: this will be the new tbodyid that has to be targeted for injecting next records in it
Each table is archived into the CSV file
The first for loop scan the csv record list from the file and the second for loop prepare what is needed to compile the targeted table with data.
The above code works well but it is a little bit slow: in fact to load into the HTML tables about 300 records from the CSV file it takes a bit more of 2.5 seconds on a computer with 2 GB ram and Pentium core 2 4300 dual-core at 1800 MHz but if I comment the row that update the DOM the function needs less than 0.1 sec. So IMHO the bottle neck is the fragment and DOM manipulating part of the code.
My aim and hope is to optimize the speed of the above code without losing functionalities.
Notice that I'm targeting just modern browsers and I don't care about others and non standards-compliant browsers... I feel sorry for them...
Any suggestions?
Thanks in advance.
Edit 16-02.2018
I don't know if it is useful but lastly I've noticed that if data is loaded from browser sessionstorage the load and rendering time is more or less halved. But strangely it is the exact same function that loads data from both file and sessionstorage.
I don't understand why of this different behavior considering that the data is exactly the same and in both cases is passed to a variable handled by the function itself before starting checking performance timing.
Edit 18.02.2018
Number of rows is variable depending on the target table: from 1 to 1000 (could be even more in particular cases)
Number of columns depending on the target table: from 10 to 18-20
In fact, building the table using DOM manipulations are way slower than simple innerHTML update of the table element.
And if you tried to rewrite your code to prepare a html string and put it into the table's innerHTML you would see a significant performance boost.
Browsers are optimized to parse the text/html which they receive from the server as it's their main purpose. DOM manipulations via JS are secondary, so they are not so optimized.
I've made a simple benchmark for you.
Lets make a table 300x300 and fill 90000 cells with 'A'.
There are two functions.
The first one is a simplified variant of your code which uses DOM methods:
var table = document.querySelector('table tbody');
var cells_in_row = 300, rows_total = 300;
var start = performance.now();
fill_table_1();
console.log('using DOM methods: ' + (performance.now() - start).toFixed(2) + 'ms');
table.innerHTML = '<tbody></tbody>';
function fill_table_1() {
var frag = document.createDocumentFragment();
var injectFragInTbody = function() {
table.replaceChild(frag, table.firstElementChild)
}
var getNewEmptyRow = function() {
var row = table.firstElementChild;
if (!row) {
row = table.insertRow(0);
for (var c = 0; c < cells_in_row; c++) row.insertCell(c);
}
return row.cloneNode(true);
}
for (var r = 0; r < rows_total; r++) {
var new_row = getNewEmptyRow();
var cells = new_row.cells;
for (var c = 0; c < cells_in_row; c++) cells[c].innerHTML = 'A';
frag.appendChild(new_row.cloneNode(true));
}
injectFragInTbody();
return false;
}
<table><tbody></tbody></table>
The second one prepares html string and put it into the table's innerHTML:
var table = document.querySelector('table tbody');
var cells_in_row = 300, rows_total = 300;
var start = performance.now();
fill_table_2();
console.log('setting innerHTML: ' + (performance.now() - start).toFixed(2) + 'ms');
table.innerHTML = '<tbody></tbody>';
function fill_table_2() {// setting innerHTML
var html = '';
for (var r = 0; r < rows_total; r++) {
html += '<tr>';
for (var c = 0; c < cells_in_row; c++) html += '<td>A</td>';
html += '</tr>';
}
table.innerHTML = html;
return false;
}
<table><tbody></tbody></table>
I believe you'll come to some conclusions.
I've got two thoughts for you.
1: If you want to know which parts of your code are (relatively) slow you can do very simple performance testing using the technique described here. I didn't read all of the code sample you gave but you can add those performance tests yourself and check out which operations take more time.
2: What I know of JavaScript and the browser is that changing the DOM is an expensive operation, you don't want to change the DOM too many times. What you can do instead is build up a set of changes and then apply all those changes with one DOM change. This may make your code less nice, but that's often the tradeoff you have when you want to have high performance.
Let me know how this works out for you.
You should start by refactoring your code in multiples functions to make it a bit more readable. Make sure that you are separating DOM manipulation functions from data processing functions. Ideally, create a class and get those variables out of your function, this way you can access them with this.
Then, you should execute each function processing data in a web worker, so you're sure that your UI won't get blocked by the process. You won't be able to access this in a web worker so you will have to limit it to pure "input/output" operations.
You can also use promises instead of homemade callbacks. It makes the code a bit more readable, and honestly easier to debug. You can do some cool stuff like :
this.processThis('hello').then((resultThis) => {
this.processThat(resultThis).then((resultThat) => {
this.displayUI(resultThat);
}, (error) => {
this.errorController.show(error); //processThat error
});
}, (error) => {
this.errorController.show(error); //processThis error
});
Good luck!

How to access the contents of a directory passed in the URL parameters passed to a browser

First off, for Chromecast reasons, I want to, currently, limit the solution to the Chrome browser.
What I'd like to do is package up a directory of images, with a launcher batch file (cmd or sh) and an html file. The html file is to be completely self contained, with no imports.
The bat file would contain something like:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --allow-file-access-from-files "file://%CD%\SlideShow1.2.html?slideDir=%CD%\Slides"
Currently I can use
<input type="file" id="slideInput" multiple="multiple" webkitdirectory="webkitdirectory" onchange="appendToSlideList();" accept=".png,.gif,.jpg,.jpeg" />
to manually select files, and
var slideList = [];
var numSlides = 0;
function appendToSlideList()
{
var slideInput = document.getElementById("slideInput");
var slides = slideInput.files;
for(j = 0; j < slides.length; j++)
{
slideList[numSlides++] = slides[j];
}
}
to append pictures from selected directories to the master slide list. Then the following, via a Timer() object displays the slides:
function showNext()
{
if(picture == null)
{
picture = document.getElementById("slideShow");
intervalElem = document.getElementById("interval");
}
if(currentPicture == numSlides)
{
currentPicture = 0;
}
// this comes from https://www.w3.org/TR/file-upload/#file
picture.src = window.URL.createObjectURL(slideList[currentPicture++]);
var interval = parseInt(intervalElem.value) * 1000;
timer = new Timer(showNext, interval);
}
If you've read this far, kudos :). So, all this (plus other auxiliary code that is not germane to the desired solution) works to show a slideshow based on user input from the object.
My desire is to package things up so that all the user has to do is double click on the bat file, and the browser proceeds to show the slide show.
So, after all this, my question is, how do I take the directory passed in, and get all the graphic files in that directory, for use in the already working code.
I've spent the last six hours researching this question, much on StackOverflow, and it appears, to me, currently, that this is an impossible quest.
Here's a fiddle with 'complete' minimalist code: https://jsfiddle.net/hrvrdfjs/
Thanks!
Tom.
There is currently no way for JavaScript in the browser to list files from a directory without the user explicitly choosing the directory in the browser.
However, you can easily create a JavaScript file that contains a list of the image files in the directory from a batch file like this:
#echo off
echo var toC = ` > "C:\slides\data.js"
dir C:\slides\*.png,*.gif,*.jpg,*.jpeg /B >> "C:\slides\data.js"
echo `; >> C:\slides\data.js
This will create file data.js which looks like this:
var toC = `
funny.png
serious.png
holidays.jpeg
`;
Explanations:
echo var toC = ` > "C:\slides\data.js"
Creates or overwrites the file C:\slides\data.js with the javascript code that is the first part of creating a template literal.
dir C:\slides\*.png,*.gif,*.jpg,*.jpeg /B >> "C:\slides\data.js"
Tells dir to list the files with the given extensions in the given directory and appends the file names to the file data.js. The parameter /B makes dir only output the files names, no other information and also skip outputting a header and footer for the listing.
echo `; >> C:\slides\data.js
This appends the end of the javascript template literal.
In JavaScript ES6 and newer, template literals can be used to create string literals that span multiple lines and can contain arbitrary characters.
Load the file data.js dynamically from your html document (by inserting a script tag that refers to the file) and you can access the variable toC which contains the list of files as a multi-line string.
This is a demo where the data.js file is statically included:
<html>
<head>
<script src="C:\slides\data.js"></script>
</head>
<body>
<script>
window.alert(toC);
</script>
</body>
</html>
I wasn't able to use the "dir" command as given above, perhaps because I'm using windows 7. Here is what I came up with, instead:
mkdir C:\Temp\slideShow
rem Replace each instance of a back slash with two, as javascript will remove a single backslash
rem (Unless it is followed by a 'valid' backslash char (r,n,etc.), which is not what we want.).
echo var topLocation = '%CD:\=\\%'; > C:\Temp\slideShow\topLocation.js
echo var slideDirContents = ' > "C:\Temp\slideShow\slideDirContents.js"
dir %CD%\Slides\*.jpeg %CD%\Slides\*.jpg %CD%\Slides\*.png %CD%\Slides\*.gif /B /ON >> "C:\Temp\slideShow\slideDirContents.js"
echo '; >> C:\Temp\slideShow\slideDirContents.js
Then, to split out each file from the input .js file, and create a file path that would later be used to build an URL that the img.src could present the file (NOTE: the add function had to be done after the page was loaded, so the textarea element would be present for the javascript to modify):
function doOnloadFunctions()
{
addStaticsToSlideList();
}
function addStaticsToSlideList()
{
// Empty dir on Windows == size 2 (CR-LF)
// Empty dir on others == size 1 (CR or LF)
if((slideDirContents.length != 2) && (slideDirContents.length != 1))
{
var slidelistElem = document.getElementById("slidelist");
// Linux/Unix/BSD based line separator = \n
// Windows based line separator = \r\n
// Mac based line separator = \r
var re = /\n|\r\n|\r/;
var slides = slideDirContents.split(re);
for(j = 0; j < slides.length; j++)
{
var aFile = {};
var theName = slides[j].trim();
if(theName.length > 0)
{
var fileName = slides[j];
aFile.fileName = topLocation + '\\Slides\\' + fileName;
// Set the original index to the current insertion point.
aFile.oi = numSlides;
// Set the shuffle index simply to instantiate it. We'll
// set the si for real when we want to shuffle the array.
aFile.si = 0;
var listElem = document.createElement("li");
listElem.appendChild(document.createTextNode(fileName));
slidelistElem.appendChild(listElem);
slideList[numSlides++] = aFile;
}
}
}
}
There's a <file> input element that can be used to append local files to the slide list, the <file> input doesn't fill in fileName it just fills in name thus this is the differentiating code that instantiates each <img> with the proper url:
if(!(typeof slideList[currentPicture].fileName === 'undefined' || slideList[currentPicture].fileName === null))
{
// variable is defined and not null
picture.src = "file://" + slideList[currentPicture++].fileName;
}
else
{
// this comes from https://www.w3.org/TR/file-upload/#file
picture.src = window.URL.createObjectURL(slideList[currentPicture++]);
}
Thanks to NineBerry for the code and suggestions!

Javascript - Receive and format HTML

I have been trying to figure this out of quite a while now. I am building a map (using Google Maps JS API) to display an orbit of a specified satellite. I have been able to achieve some results, none of which take the data from the HTML page, which I want to be able to do. The data is dynamically generated for each page using Laravel Blade with results from my SQL database.
In order to draw an orbit of a satellite I had to use the orbits-js library.
I managed to do this so far:
Take the data from a text file, with the format of:
0 ISS (ZARYA)
1 25544U 98067A 17202.25860705 .00002617 00000-0 46671-4 0 9999
2 25544 51.6406 228.4871 0006083 57.5520 20.0514 15.54198040 67054
As you can see, this is the data used to calcuate an orbit. Each line starts with an identifier - 0,1 or 2. This is used by orbit-js to calculate the orbital parameters.
I use this JS code to get the data:
$.get("/other/stations.txt", function(data) {
stations = orbits.util.parseTLE(data);
var i = 0;
for (; i < stations.length; i++) {
var name = stations[i].name;
var satOpts = {
map: map,
tle: stations[i],
pathLength: 3,
};
var sat = new orbits.Satellite(satOpts);
sat.refresh();
sat.refresh_path();
sats.push(sat);
}
setInterval(function() {
var i = 0;
for (; i < sats.length; i++) sats[i].refresh();
}, 500);
setInterval(function() {
var i = 0;
for (; i < sats.length; i++) sats[i].refresh_path();
}, 5 * 60000);
});
The orbits-js library formats the data like this:
orbits.util.parseTLE = function(text) {
"use strict";
if (!text || typeof text != "string" || text === "") return [];
var lines = text.split("\n");
// trim emepty lines
for (var i = 0; i < lines.length; i++)
if (lines[i] === "") lines.splice(i, 1);
// see if we got something reasonable
if (lines.length < 3) return [];
if (lines.length % 3 !== 0)
throw new SyntaxError("The number of lines should be multiple of 3");
// try and make the array
var three;
var array = [];
while (lines.length) array.push(new orbits.TLE(lines.splice(0, 3).join("\n")));
return array;
};
What I want is to take the data from my HTML instead of the txt file. My HTML format is like this:
<li id="tle-data-main">0 ISS (ZARYA)</li>
<li id="tle-data-main">1 25544U 98067A 17198.59697288 +.00000849 +00000-0 +20077-4 0 9991</li>
<li id="tle-data-main">2 25544 051.6415 246.7397 0005882 046.2932 050.6122 15.54169454066485</li>
Basically what I need is to modify the JS to take the data from my webpage/HTML and not the txt file.
First, you should not have the same id multiple times in your html.
By definition, an id is meant to be unique. Use classes instead :
<li class="tle-data-main">0 ISS (ZARYA)</li>
<li class="tle-data-main">...</li>
<li class="tle-data-main">...</li>
You want to get the text inside your .tle-data-main elements, so you just need to do some jQuery (I saw you're using jQuery).
Instead of your $.get, write :
var lines = [];
$('.tle-data-main').each(function() { // for each element of your class
lines.push(this.innerText); // we push a new line containing the text
});
var data = lines.join("\n"); // we join lines to produce text data
stations = orbits.util.parseTLE(data); // and give it to orbit-js

Javascript: How to loop through files

I am making an Illustrator CS6 Javascript that does the following:
Open a folder of Illustrator files
Open each file in the folder (these files are called the source files)
Select all the contents of the source file
Copy the contents of the source file
Create a new target file Paste these contents into the target file as a new layer
Ensure the new layer has the same name as the old source file
My script works except, it doesn't loop through the files in the source folder correctly. Instead, it runs fine on the first source file. But then it endlessly just pastes the second source file in the destination document (I.e. it doesn't move onto any of the other source file). It just endlessly pastes and so I have to force quit!
How can I get it to loop through the folders properly and then move onto the next file.
Here is my code:
// JavaScript Document
//Set up vairaibles
var destDoc, sourceDoc, sourceFolder, newLayer;
// Select the source folder.
sourceFolder = Folder.selectDialog('Select the folder with Illustrator files that you want to mere into one', '~');
destDoc = app.documents.add();
// If a valid folder is selected
if (sourceFolder != null) {
files = new Array();
// Get all files matching the pattern
files = sourceFolder.getFiles();
if (files.length > 0) {
// Get the destination to save the files
for (i = 0; i < files.length; i++) {
sourceDoc = app.open(files[i]); // returns the document object
var myLayers = sourceDoc.layers; // Select All layers in Active Document
//Go through all layers of source document and copy artwork
for (i = 0; i < myLayers.length; i++) {
myLayers[i].hasSelectedArtwork = true;
};
with(sourceDoc) {
var count = pageItems.length;
for (var i = 0; i < count; i++) {
pageItems[i].selected = true;
}
redraw();
copy();
for (var i = 0; i < count; i++) {
pageItems[i].selected = false;
}
}
//Create a new title variable that has the title of the source document
var title = sourceDoc.name;
var title = title.substring(0, title.length - 4); //(remove extension from name)
//Close the Source Document
sourceDoc.close(SaveOptions.DONOTSAVECHANGES);
//Open the Destination Document and create a new layer in it that is named after the title variation
newLayer = destDoc.layers.add();
newLayer.name = title;
//Paste into this new layer
newLayer = app.paste();
}
}
else {
alert('No matching files found');
}
}
Ps. I wasn't sure if I should post this in Code Review or Graphic Design, but I think Stack overflow is the best place to post this as it is a general question about javascript looping, so I hope this is the right place.
It appears that you are using "i" for the variable in each of your loops, giving it a range of unexpected values in other loops that also use that same variable. I would try using a separate variable for each loop. E.g. for j=0, for k=0, for l=0, etc.

Javascript get all files minus certain file extentions

I have a script that returns all the files contained within a folder. However, there are some file types in there that I do not want my script to do anything with. I just want it to literally skip over it as if it wasn't there and only deal with the other file types.
How can I achieve this?
So far this is how I'm getting all the files contained within a folder:
var samplesFolder = Folder(Path)
//Get the files
var fileList = samplesFolder.getFiles()
//Creat Array to hold names
var renderTypes = new Array();
//Parse Initial name to get similar render elements
var beautyRender = fileList[0].name
beautyRender = beautyRender.substr(0, beautyRender.length-4)
//Get the render elements with a similar name
for (var i = 0; i < fileList.length; i++)
{
if(fileList[i].name.substring(0,beautyRender.length) === beautyRender)
{
renderTypes[i] = fileList[i].name
}
}
This is not used for web purposes I should hasten to add.
edit
Above is the complete code I have to get all the image files in a folder and bring them into photoshop once the user has selected the folder they want to use. At the moment it is bringing in every single image in the folder when there is a single type I want it to ignore.
You can iterate over the list and only collect those with extensions you care about. i see photoshop so I'll assume image files only:
var distilledFileList = [];
for (var i = 0; i < fileList.length; i++){
if (/\.(?:jpe?g|png|gif|psd)$/i.test(fileList[i].name)){
distilledFileList.push(fileList[i]);
}
}
Now distilledFileList contains only *.jpg, *.jpeg, *.png, *.gif, and *.psd files.
if you want an easier (more readable) way to check extensions (maybe you're not as fluent as regular expressions):
// fileList = ....
// setup an array of bad extensions here:
var bad = ['txt', 'log', 'db'],
// holds new list of files that are acceptable
distilledFileList = [];
// iterate over entire list
for (var i = 0; i < fileList.length; i++){
// grab the file extenion (if one exists)
var m = fileList[i].name.match(/\.([^\.]+)$/);
// if there is an extenions, make sure it's now in the
// 'bad' list:
if (m && bad.indexOf(m[1].toLowerCase()) != -1){
// it's safe, so add it to the distilled list
distilledFileList.push(fileList[is]);
}
}
Assuming fileList is just an array of strings you could do something along the lines of:
for (var i = 0, len = fileList.length; i < len; i++) {
var filename = fileList[i].name;
if (filename.match(/\.(txt|html|gif)$/i) !== null) { continue; }
// Your logic here
}
Where txt, html and gif are file extensions you want to skip over, you can add more by separating them with |

Categories

Resources