DOM manipulation inside web worker - javascript

I know that workers can't manipulate the document directly, but how about the DOM API methods? Where did they go?!
For example if I make a request that receives a HTML fragment, what I'm supposed to do if need just to parse it in order to retrieve some data from a specific node?!
There's absolutely no way to work with virtual DOM on web workers?!

Support in browsers
DOMParser or document.implementation are usually used to parse HTML to DOM in browsers. Neither is available in worker context.
In Firefox, this is not possible because someone decided there will be only one DOM parser instance for all threads. See this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=677123
In google chrome it doesn't work either.
Workaround - external library
That's right, since browser developers didn't realize DOM and XML parsing will be one of main uses of WebWorkers, we'll have to fall back to external library. The best bet seems to be JSDOM, but you'll need to figure out how to browserify it.
Here's my failed attempt with DOMParser, I keep it for future experiments on this topic: https://jsfiddle.net/svaqb2wn/2/

You can use one of the DOM implementations running in a Web Worker:
Domino
JSDOM
XMLDOM
WorkerDOM
Via.js

At first you might think the proper way to go about parsing XML is XMLHttpRequest and in 9 out of 10 cases you would be right. However, in the current web worker spec we don't have access to XMLHttpRequest. An alternative is to use one of a few popular XML parsing libraries:
https://github.com/isaacs/sax-js
http://xmljs.sourceforge.net/index.html or https://www.npmjs.com/package/xmljs
HTML is just XML, so you can parse your data in this manner. An example using xmljs from npmjs above:
var XmlParser = require("xmljs");
var fs = require("fs");
var p = new XmlParser({ strict: true });
var xml = fs.readFileSync("./SOAP1.xml"); // XML in the examples direct
var xmlNode = p.parseString(xml, function(err, xmlNode) {
if(err) {
console.error(err);
return;
}
var nodes = xmlNode.path(["Envelope", "Body", "GetstockpriceResponse", "Price"], true);
console.log(nodes.map(function(n) { return n.text; }));
});

Related

how to get javascript code too with the actual source with Html Agility Pack

i am getting source of a website using Html Agility pack which is different than the code when i inspect with firebug.i have searched many things but still not getting clear of what i should do.Source is different than the code when i inspect please tell me how to get javascript code too with that Html. Even when i disable javascript in my browser i still cannot get the Javascript code along the source. i am using
string url="";
HtmlDocument doc = new HtmlDocument();
WebClient client = new WebClient();
html = client.DownloadString(url);
doc.LoadHtml(html);
to get source tell me if i should need a request and response method to get JS code too.
To expand on #alecxe answer, you can use Selenium* to load your target page like a real browser would do, and then pass the result to HtmlAgilityPack for further processing :
using OpenQA.Selenium;
.....
IWebDriver driver = new PhantomJS.PhantomJSDriver();
driver.Navigate().GoToUrl(url);
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(driver.PageSource);
alternatively, you can just run your query (XPath or CSS selector) using Selenium directly, for example :
var result = driver.FindElements(By.XPath("your query"));
//print HTML of the returned elements
foreach (var item in result)
{
Console.WriteLine(item.GetAttribute("outerHTML"));
}
*) Need to download Selenium first, as well as the driver i.e PhantomJS, Firefox, etc. Selenium can be installed to your project easily from NuGet.
For that you would need a real browser. Consider automating a browser (which can be headless - see PhantomJS) with the help of selenium.
See also:
Running Scripts in HtmlAgilityPack

Need help reading a file in Javascript located in the same directory

I've written this function to try and read a file located in the same directory as my Javascript files and index.html file. I've read from files before, but normally I have the user select the file themselves, so I've never had to create the actual file object.
Does anyone know why the code below doesn't work?
function getFile()
{
var reader=new FileReader();
var file=new File("input.txt");
var str=reader.result;
reader.readAsText(file);
return str;
}
Update:
Some additional information (My apologies if I don't answer your questions, I'm really new to this, and everything I know is self-taught).
Server side or client side? I think this it is going to be hosted serverside - I have a domain that I'm going to upload the file to.
Not possible due to security restrictions. You must use a file that was selected by a user. Imagine what would happen if javascript could read any file on your harddrive - nightmare!
I had a similar code for a desktop app written in JS. It worked well on IE and FF until Chrome came along (yes, it was a very long time ago!) and started restricting the sandbox ever more. At some point, IE and FF also tightened permissions and the method didn't work anymore (I used this code which does not work anymore):
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
file = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(filename);
if (file.exists())
{
inStream = Components.classes["#mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
inStream.init(file, 0x01, 00004, null);
sInStream = Components.classes["#mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
sInStream.init(inStream);
content = sInStream.read(sInStream.available());
}
} catch (ex) {
// and so on...
}
At some point FF and Chrome had some startup flags that enabled the sandbox to be lax on certain security restrictions, but in the end I realized that this is simply old code.
Today, I use localStorage to store data on the local machine. The only disadvantage is that you have to implement an import/export functionality so that it is portable to other browsers/machines by copy/paste the raw text.
What I do is simply this. The maximum size of the database is said to be about 10 MB (but deviations are known to exist) and all I store is a single JSON string:
function store(data)
{
localStorage.data = JSON.stringify(data);
}
function retrieve()
{
return JSON.parse(localStorage.data);
}
Obviously, these two functions are an oversimplification, you need to handle edge cases like when no data exists or a non-JSON string was imported. But I hope you get the idea on how to store local data on modern browsers.
With the File() constructor you can only create new file objects, but cannot read files from lokal disk.
It is not quite clear what exactly you want to achieve.
You say, that the input.txt is located in the same directory as your html and js. Therefore it is located on the server.
If you want to read a file from the server you have to use an ajax request which is already explained in anoter question.

Web Workers for Parsing Raw Input in Chrome Packaged Calculator App

I am currently working on a calculator that will run as a packaged (desktop) chrome app. I am using the math.js library to parse math input. This is my old code:
evaluate.js:
var parser = math.parser();
function evaluate(input){
$("#output").text(parser.eval(input));
}
However, if the input is something unreasonable like 6234523412368492857483928!, the app just freezes, because it is trying to evaluate the input. I know that math.js is still in beta so eventually there might be a fix (overflow errors), but I couldn't find any other library that parses raw input the way math.js does.
To fix this, I am trying to fix this using web workers to run it asynchronously. Here is the code that I have right now:
main.js
var evaluator = new Worker('evaluate.js');
evaluator.addEventListener('message', function(e){
$("#output").text(e.data);
}, false);
function evaluate(input){
evaluator.postMessage(input);
}
evaluate.js
var parser = math.parser();
function mathEval(input){
return parser.eval(input);
}
self.addEventListener('message', function(e){
self.postMessage(mathEval(e.data));
});
However, this doesn't work when I run it. Also, I noticed that when I use web workers, it throws the error Uncaught ReferenceError: math is not defined - evaluate.js:1, but it didn't throw this error with the old code.
Questions: Why doesn't this code work to evaluate the input? Is it possible to use multiple workers to speed it up? If I wanted to implement some sort of overflow error for when the worker takes more than 2 seconds, what would be the best way to go about doing it? Finally, is there a better way to do this?
Web Workers are run in totally separate context. They don't have access to the objects from parent web page. If you want to use math.js you have to import it into the worker using importScript.
I recommend to read Using Web Workers guide, part "Importing Scripts And Libraries" which describes how to do it, and how it works in detail.

Firefox ChromeWorker not loading script

I have a requirement where I need to communicate with native code to perform some operations. I have been successful by using JS-Ctypes and things are panning out as expected. Since the communication from my web application with the native code takes some time, thus blocking the main JS thread consequently freezing the UI.
Thus I need to create a separate thread to be delegated with the communication with the native code and post back results to the main thread which will give the appropriate feedback to the user. Firefox ChromeWorker are exactly what I need to use, since they are independent threads with access to JS-Ctypes.
My problem is that for the life of me, I can't seem to load a script using that approach. This is what I currently have:
main.js
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
Components.utils.import("resource://gre/modules/Services.jsm");
var worker = new ChromeWorker("js/fpman/myworker.js");
worker.onmessage = function(e){
console.log(e.data);
};
worker.postMessage('start');
myworker.js
self.onmessage = function(e){
var sum = 1 + 1;
postMessage("Sum is " + sum);
};
When that code runs in the main JS, I get this error on firebug console
Failed to load script: http://localhost:8080/myapp/js/fpman/myworker.js (nsresult = 0x805303f4)
Point to note, when I use a normal worker thread i.e
var worker = new Worker("js/fpman/myworker.js");
the js file (myworker.js) is loaded fine and I get the expected result, but of course that doesn't suffice my needs since a normal worker doesn't have access to JS-Ctypes. So it seems the problem is how am creating the ChromeWorker. Could someone please enlighten me on how to appropriately instantiate and use the ChromeWorker Object from an application. I have seen a lot of reference of usage of ChromeWorker in extensions, but that is not what I want, I want to use the ChromeWorker in my web application.
Thanks.
That particular error is NS_ERROR_DOM_BAD_URI
I don't believe what you are doing will work, and I know it won't work very soon in Firefox because enablePrivilege is going away completely.

Reading a txt file from Javascript

am trying to read few lines from a txt file using JS,and i have this code but its not working for some reason,,
var fso = new ActiveXObject("Scripting.FileSystemObject");
var s = fso.OpenTextFile("C:\\wamp\\www\\22.txt", 1, true);
var row = s.ReadLine();
alert(row);
any suggestions?!
Make sure your browser has the right permissions to perform that kind of operation. Usually, browsers won't allow direct file system access by default.
Only IE supports ActiveXObject. Trying to use ActiveXObject on any other browser will fail because there is no such variable defined.
You need to either limit yourself to IE, write a browser plugin instead, or give up trying to get file system access on other browsers and proxy files through a server instead.
If you're running WAMP anyway, just use standard AJAX to fetch the file 22.txt from the server. The easiest way is to use jQuery, where the code would be:
$.get("22.txt", function(data) {
alert(data);
}
You can search for how to do this without jQuery if you wish.

Categories