I just managed to implement a small webserver on my Raspberry Pi.
The webserver is created as an UWP headless app.
It can use Javascript. Which is pretty helpful.
I only just start with HTML and JS so I'm a big noob in this and need some help.
I already managed to show the same data I show on the webpage in a headed app on the same device.
Now I want to be able to manipulate the data from the webpage.
But I don't know how I'm supposed to do that.
I parse the HTML / JS as a complete string so I can't use variables I defined in code. I would need another way to do this.
My code for the webserver is currently this:
public sealed class StartupTask : IBackgroundTask
{
private static BackgroundTaskDeferral _deferral = null;
public async void Run(IBackgroundTaskInstance taskInstance)
{
_deferral = taskInstance.GetDeferral();
var webServer = new MyWebServer();
await ThreadPool.RunAsync(workItem => { webServer.Start(); });
}
}
class MyWebServer
{
private const uint BufferSize = 8192;
public async void Start()
{
var listener = new StreamSocketListener();
await listener.BindServiceNameAsync("8081");
listener.ConnectionReceived += async (sender, args) =>
{
var request = new StringBuilder();
using (var input = args.Socket.InputStream)
{
var data = new byte[BufferSize];
IBuffer buffer = data.AsBuffer();
var dataRead = BufferSize;
while (dataRead == BufferSize)
{
await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
dataRead = buffer.Length;
}
}
string query = GetQuery(request);
using (var output = args.Socket.OutputStream)
{
using (var response = output.AsStreamForWrite())
{
string htmlContent = "<html>";
htmlContent += "<head>";
htmlContent += "<script>";
htmlContent += "function myFunction() {document.getElementById('demo').innerHTML = 'Paragraph changed.'}";
htmlContent += "</script>";
htmlContent += "<body>";
htmlContent += "<h2>JavaScript in Head</h2>";
htmlContent += "<p id='demo'>A paragraph.</p>";
htmlContent += "<button type='button' onclick='myFunction()'>Try it!</button>";
htmlContent += "</body>";
htmlContent += "</html>";
var html = Encoding.UTF8.GetBytes(htmlContent);
using (var bodyStream = new MemoryStream(html))
{
var header =
$"HTTP/1.1 200 OK\r\nContent-Length: {bodyStream.Length}\r\nConnection: close\r\n\r\n";
var headerArray = Encoding.UTF8.GetBytes(header);
await response.WriteAsync(headerArray, 0, headerArray.Length);
await bodyStream.CopyToAsync(response);
await response.FlushAsync();
}
}
}
};
}
public static string GetQuery(StringBuilder request)
{
var requestLines = request.ToString().Split(' ');
var url = requestLines.Length > 1
? requestLines[1]
: string.Empty;
var uri = new Uri("http://localhost" + url);
var query = uri.Query;
return query;
}
}
Your question is a bit vague, so I have to guess what you're trying to do. Do you mean that a browser (or another app with a Web view) will connect to your Pi server, grab some data off it, and then manipulate the data to format them / display them in a particular way on the page? If so, then first you need to decide how you get the data. You seem to imply the data will just be a stream of HTML, though it's not clear how you'll be passing that string to the browser. Traditional ways of grabbing the data might be with Ajax and possibly JSON, but it's also possible to use an old-fashioned iframe (maybe a hidden one) -- though if starting from scratch, Ajax would be better.
The basic issue is to know: what page will access the data on the server and in what format? Is it a local page served locally from the client app's filestore, that will then launch a connection to the server, grab the data and display them in a <div> or and <iframe>, or is it a page on your server that comes with the data incorporated in one part of the DOM, and you want to transform them and display them in another element?
Let's now assume your client app has received the data in an element like <div id="myData">data</div>. A script on the client page can grab those data as a string with document.getElementById('myData').innerHTML(see getElementById). You can then transform the data as necessary with JavaScript methods. Then there are various DOM techniques for inserting the transformed data either back in the same element or a different one.
Instead, let's assume you have received the data via XMLHttpRequest. Then you'll need to identify just the data you want from the received object (that might involve turning the object into a string and using a regular expression, or more likely, use DOM selection methods on the object till you have the part of the data you want). When you've extracted the data / node / element, you can insert it into a <div> on your page as above.
Sorry if this is all a bit vague and abstract, but hopefully it can point you in the right direction to look up further things as needed. https://www.w3schools.com/ is a great resource for beginners.
Related
I need to change a web page source in GeckoFX web browser including html, css and js.
This is my code:
geckoWebBrowser1.Navigate("http://example.com/");
geckoWebBrowser1.DocumentCompleted += GeckoWebBrowser1_DocumentCompleted;
private void GeckoWebBrowser1_DocumentCompleted(object sender, Gecko.Events.GeckoDocumentCompletedEventArgs e)
{
WebClient w = new WebClient();
string s = (w.DownloadString("http://example.com/"));
//after do changes on (s)
geckoWebBrowser1.LoadHtml(s, "http://example.com/");
But it's not working on javascript, can anyone help me?
The problem is that geckoWebBrowser1.LoadHtml also triggers GeckoWebBrowser1_DocumentCompleted(). So you will loop endlessly.
Move the LoadHtml to another function, or change the content live as below.
Also, are you're using WebClient to download the same page? There is no need, the source is already available.
GeckoHtmlElement element = null;
var geckoDomElement = e.Window.Document.DocumentElement;
if (geckoDomElement is GeckoHtmlElement)
{
element = (GeckoHtmlElement)geckoDomElement;
element.InnerHtml = element.InnerHtml.Replace("Google", "Göggel");
}
Javascript is most easily executed using the following:
using (AutoJSContext context = new AutoJSContext(ActiveBrowser.Window.DomWindow))
{
var result = context.EvaluateScript("testFunction();");
}
I have a Java application that is running a Java FX webengine (the end goal of all of this is to dynamically draw a D3.js plot). I'd like to be able to add new javascript files to it while it's already running, and then ideally unload them again when the javascript tells it to.
This has the effect of making some functionality available to the user or not (drawing certain features) without having to load all of the code in at once. It also helps me avoid future headaches with a forest of brittle if/then statements inside my javascript.
Question 1: Is "unloading" files from a running webengine even possible? At least, possible without redrawing the whole thing. I'm pretty sure that calling loadContent() with my new filepaths will make them available the way that I want, but I haven't come across anything talking about how you can remove existing source code from your HTML block.
Question 2: Any recommendations on how to elegantly approach feeding the extra sources into the webView? My thought process right now is stuck on brute force, but I haven't been even been able to brute force my way to a solution that works, so maybe I'm barking up the wrong tree.
Question 3: Is this even a good idea? It's possible to do this solely in the Javascript, but I want the appearance and disappearance of options directly tied to a java-side feature that's later going to be executed from the class Bridge, the same place I'm trying to load new content from right now.
I asked a lot of questions, but any help is appreciated!
Right now I am setting up the webengine's content like so:
final ResourceExtractor RESOURCE_EXTRACTOR
= new ResourceExtractor(JavaScriptPlot.class);
// prepare the local URI for d3.js
final URI D3_JS_URI = RESOURCE_EXTRACTOR
.extractResourceAsPath("d3.min.js")
.toUri();
// prepare the local URI for numeric.js
final URI NUMERIC_JS_URI = RESOURCE_EXTRACTOR
.extractResourceAsPath("numeric.min.js")
.toUri();
// prepare the local URI for topsoil.js
final URI TOPSOIL_JS_URI = RESOURCE_EXTRACTOR
.extractResourceAsPath("topsoil.js")
.toUri();
// build the HTML template (comments show implicit elements/tags)
HTML_TEMPLATE = (""
+ "<!DOCTYPE html>\n"
// <html>
// <head>
+ "<style>\n"
+ "body {\n"
+ " margin: 0; padding: 0;\n"
+ "}\n"
+ "</style>\n"
// </head>
+ "<body>"
+ "<script src=\"" + D3_JS_URI + "\"></script>\n"
+ "<script src=\"" + NUMERIC_JS_URI + "\"></script>\n"
+ "<script src=\"" + TOPSOIL_JS_URI + "\"></script>\n"
+ "<script src=\"%s\"></script>\n" // JS file for plot
// </body>
// </html>
+ "").replaceAll("%20", "%%20");
The source path to replace %s is created here:
public class BasePlot extends JavaScriptPlot {
private static final ResourceExtractor RESOURCE_EXTRACTOR
= new ResourceExtractor(BasePlot.class);
private static final String RESOURCE_NAME = "BasePlot.js";
public BasePlot() {
super(
RESOURCE_EXTRACTOR.extractResourceAsPath(RESOURCE_NAME),
new BasePlotDefaultProperties());
}
}
And then later I take a file path already extracted as a resource, sourcePath, and insert it into the HTML block:
String buildContent() {
return String.format(HTML_TEMPLATE, sourcePath.toUri());
}
Then I build my web view using the return value of buildContent()
private void initializeWebView() {
runOnFxApplicationThread(() -> {
// initialize webView and associated variables
webView = new WebView();
webView.setContextMenuEnabled(false);
WebEngine webEngine = webView.getEngine();
webEngine.getLoadWorker().stateProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue == SUCCEEDED) {
if (new IsBlankImage().test(screenCapture())) {
webEngine.loadContent(buildContent());
}
topsoil = (JSObject) webEngine.executeScript("topsoil");
topsoil.setMember("bridge", new Bridge());
}
});
// asynchronous
webEngine.loadContent(buildContent());
});
}
And the Javascript can fire off the method in the class below to trigger the change. Right now it's manually creating a hardcoded resource, but once I work out once going wrong I'll make this part more elegant/logically organized.
//loads appropriate JS files into webview based on BasePlot's isotope type
public class Bridge {
final URI ISOTOPE_URI = ISOTOPE_RESOURCE_EXTRACTOR
.extractResourceAsPath("Concordia.js")
.toUri();
String finalHtml = buildContent().concat("<script src=\""+ISOTOPE_URI.toString()+"\"></script>\n");
webView.getEngine().loadContent(finalHtml);
}
}
The loadContent() above is giving me an application thread error: "ReferenceError: Can't find variable: topsoil"
I am using MVC 4 Web Api and I have a requirement to expose a end point which can be consumed by clients to render code on their web page. Something like this
<script type="text/javascript" src="http://server/endpoint"/>
When the above line is included in web pages, the server should return a javascript code which should be automatically executed.
For example if the server returns the code - alert('hello'), then the client webpage (using the script) should automatically pop up "hello".
The server side code can be like below. In this case, the server returns javacript code to write a table in the browser. Basically this is used to return list of publications for a particular professor in the University. The enpoint is exposed so that individuals can include the script in their web pages to show their publications
// GET api/values/5
public string Get(int id)
{
var html = "<table><tbody>";
html += "<tr>";
html += "<td>row 1, col 1</td>";
html += "<td>row 1, col 2</td>";
html += "</tr>";
html += "<tr>";
html += "<td>row 2, col 1</td>";
html += "<td>row 2, col 2</td>";
html += "</tr>";
html += "</tbody></table>";
var script = string.Format(#"<script type='text/javascript'>document.write('{0}')</script>", html);
return script;
}
Any thoughts how this can be achieved. I tried using Response.Write("alert('hello')") but it does not work.
1 - If you will use the endpoint in script tag you don't need to rewrite the script tag inside de get method.
2 - The code you are trying to return isn't a javascript code, this is a html code and will not run when called but script tag.
3 - try to do this:
html:
<script type="text/javascript" src="http://server/endpoint"/>
webAPI:
public string Get(int id)
{
return "alert('hello world');";
}
I'm using an WCF service with a REST endpoint, and my script was also being treated as a string as it was received quoted between double quotes.
"alert('hello world');" instead of alert('hello world');
I found the solution here:
https://viswaug.wordpress.com/2008/05/22/customizing-the-response-serialization-in-wcf-rest-services/
You have to set the content type of the response to application/x-javascript and wrap your string inside a MemoryStream, like this:
public Stream GetSomeScript()
{
WebOperationContext context = WebOperationContext.Current;
context.OutgoingResponse.ContentType = "application/x-javascript";
const string TEXT_TO_SEND = "alert('hello world');";
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
sw.Write(TEXT_TO_SEND);
sw.Flush();
ms.Position = 0;
return ms;
}
Hope this helps.
I am trying to export some data that I have in some Backbone collections to a csv file.
So far I am opening a new export page using java script like so
var href = [];
href.push('ExportAnalysis.aspx?');
href.push('ParamSet=' + this.document.analysisParameterSetView.selectedParamSet + '&');
href.push('Start=' + start
Date + '&');
href.push('Finish=' + endDate + '&');
frames["exportIFrame"].location.href = href.join('');
And then in the code behind of exportAalysis.aspx, i am grabing the variables from the query string getting the data, building up the csv file and return the file like so.
// Get the export parmaters from the query string
var paramSet = Request["ParamSet"];
var startUnix = int.Parse(Request["Start"]);
var finishUnix = int.Parse(Request["Finish"]);
var start = DateTime.Parse("1970-01-01").AddSeconds(startUnix);
var finish = DateTime.Parse("1970-01-01").AddSeconds(finishUnix);
// GET DATA using Parameters
var filename = "analysisExport";
var content = "1,2";
Response.Clear();
Response.ContentType = "application/x-unknown";
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
Response.Write(content);
Response.End();
}
This works OK, but it seems a little inefficient, as I am having to get the data I need twice. Once for the main page and again for the export page.
Its a bit of a long shot But is it possible to get the data from the first page from the code behind of the export page? If it was all client side I could use window.opener.document to get the opener page, Can I do something similar in asp.net
Or am I completely off track, and there is a much better way to achieve this.
This only works if the protocol and domain match between the iframe and the main window.
All code is javascript
Iframe to the parent:
var pDoc = window.parent.document;
var pWin = window.parent.window;
Document to iframe:
var cDoc = document.getElementById("exportIFrame").contentDocument;
var cWin = document.getElementById("exportIFrame").contentWindow;
To call scripts on a parent:
pWin.yourFunction("parameter");
To call scripts in an iframe:
cWin.yourFunction("parameter");
I have a pdf document embedded inside a webpage in ASP.net and want to get a specific field inside the pdf document using Javascript...plain Javascript...
JavaScript in a PDF can call JS in a web page and visa versa, if BOTH are set up for it. You can see Acrobat's documentation here.
Check out the HostContainer specification, starting on page 486. In the PDF you'd need script something like:
var document = this; // hurray for closures.
this.hostContainer.messageHandler = { onDisclose: function() {return true;},
onMessage: function(msgArrayIgnored) {
// build a JSON string of field/value pairs
var outgoingMessage = "{ ";
for (var i = 0; i < this.numFields; ++i) {
var fldName = document.getNthFieldName(i);
var fld = document.getField(fld);
var val = fld.value;
// you'll probably need to escape 'val' to be legal JSON
outgoingMessage += fldName + ": \"" + val + "\";
// stick in a comma unless this is the last field
if (i != this.numFields-1) {
outgoingMessage += ", ";
}
}
outgoingMessage += "};";
this.hostContainer.postMessage( [outgoingMessage] );
};
In the HTML, you need to set up something similar. Lets assume your pdf is embedded in an object tag, and that element's id is "pdfElem". Your HTML script might look something like:
var pdf = document.getElementById("pdfElem");
pdf.messageHandler = function(message) {
var fldValPairs = eval(message);
doStuffWithFieldInfo(fldValPairs);
};
Later, any time you want to inspect the PDF's field info you post a message, and the PDF will call back to pdf.messageHandler with its JSON string wrapped in an array:
pdf.postMessage(["this string is ignored"]);
There's probably a bug or two lurking in there somewhere, but this will put you on the right track.
Webpage JavaScript will not be able to interact with the PDF form fields. You can however make a PDF form post to a web page form processor and then obtain the values in the form fields.