VB.NET Gecko Web Browser javascript function calling? - javascript

I am using GeckoWebBrowser within my VB.NET (Windows Form App) program. The GeckoWebBrowser loads a local html file. This html has embed in-line a svg file (human body diagram with bones and internal organs) with a javascript function for picking up all the "ids" of the elements from the svg document. I'd like to call the aforementioned javascript function from VB.NET (Windows form app), but I don't know how to do so. Can anyone help me, or give me a source code example please? All the stuff I've found is based in C#...
This is my javascript function in my html file:
<script type="text/javascript">
(funcion () {
// Function to be called in VB.NET when the DOM is loaded
var SVGHandler = function () {
// Picking up the id Root Node="CUERPO_HUMANO" into svg variable
var svg = document.querySelector('#CUERPO_HUMANO');
// In Items we save all the <g> which have an ID
var items = svg.querySelectorAll('g[id], path[id]');
//var items = svg.querySelectorAll('g[id]');
// We loop all the nodes saved in Items and add them to click event listener
forEach(items, function (index, value) {
value.addEventListener('click', function (event) {
event.preventDefault();
//We avoid the spread of events
event.stopPropagation();
return event.currentTarget.id
// console.log(event.currentTarget.id)
});
});
}
// https://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/
var forEach = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) {
callback.call(scope, i, array[i]); // passes back stuff we need
}
};
// With this method, we call a SVGHandler when DOM is totally loaded
document.addEventListener('DOMContentLoaded', SVGHandler);
})();
</script>
What code should I use in VB.NET for calling my javascript function each time I click on a specific bone or organ in the human body diagram loaded in GeckoWebBrowser?
I want to save the "id" picked up with the calling into a string variable in order to use it as a parameter in a SQL statement and populate a DataGridView.
I've been searching and all that I could find was related to C#, not a single VB.NET example. Even though I was trying to figure out the equivalence in VB.NET trying to convert the C#'s examples to VB.NET, I have some doubts on how to do the javascript call. According to my javascript function It could be something like this:
browserControl.Navigate("javascript:void(funcion())");
Please, Can anyone help me to solve this? I would be very thankful...

Well since you have set click EventListener's I think that you're not looking for a way to call the eventual function from VB.NET but this is quite unclear according to your post so I'll give you examples on how to call a javascript function and how to trigger a reaction in your VB.NET code through javascript using GeckoWebBrowser.
Your code snippet of your attempt to call a js function from your vb code is correct. The only problem is that you haven't defined any callable js function in your html file. In your case you should do this to trigger your main js function from vb:
//Sorry I don't know vb. I'll give example in c# keeping it as simple as possible so that you can easily convert it to vb
Gecko.GeckoHtmlElement humanBodyPart = (Gecko.GeckoHtmlElement) browserControl.Document.GetElementById("your id");
humanBodyPart.Click();
The above code finds the element with the matching id in the GeckoWebBrowser and clicks it. Since you've set click EventListener's, by clicking one of the elements this will trigger the function assigned to them to run.
Moving on, in order to save the id of the elements to a string variable in your vb code you'll need to add this little bit of js code in to the code that you pass as 'callback' parameter in your forEach function:
var event = document.createEvent('MessageEvent');
var origin = window.location.protocol + '//' + window.location.host;
var event = new MessageEvent('jsCall', { 'view': window, 'bubbles': false, 'cancelable': false, 'data': 'YOUR EVENTUAL ID AS A STRING (THIS STUFF GOES BACK TO THE VB/C# CODE)' });
document.dispatchEvent (event);
Then the above snippet should be handled in your vb code like this:
browserControl.AddMessageEventListener("jsCall", (id) =>
{
//Here in the variable id you have your clicked id as a string. Do what you wanted to do...
});

VB side :
you need wait until the document is completed to add listeners
for example : _DocumentCompleted
Private Sub GeckoWebBrowser1_DocumentCompleted(sender As Object, e As Gecko.Events.GeckoDocumentCompletedEventArgs) Handles GeckoWebBrowser1.DocumentCompleted
GeckoWebBrowser1.AddMessageEventListener("my_function_name JS_side", AddressOf my_sub_for_treatment)
End Sub
JS side :
var event = document.createEvent('MessageEvent');
var origin = window.location.protocol + '//' + window.location.host;
var event = new MessageEvent('my_function_name JS_side', { 'view': window, 'bubbles': false, 'cancelable': false, 'data': my_data_to transfer });
document.dispatchEvent (event);

Related

Ajax call in "for" loops skips odd/even iterations

If I am here asking it is because we are stuck on something that we do not know how to solve. I must admit, we already searched in StackOverflow and search engines about a solution.. but we didn't manage to implement it / solve the problem.
I am trying to create a JavaScript function that:
detects in my html page all the occurrences of an html tag: <alias>
replaces its content with the result of an Ajax call (sending the
content of the tag to the Ajax.php page) + localStorage management
at the end unwraps it from <alias> tag and leaves the content returned from ajax call
the only problem is that in both cases it skips some iterations.
We have made some researches and it seems that the "problem" is that Ajax is asynchronous, so it does not wait for the response before going on with the process. We even saw that "async: false" is not a good solution.
I leave the part of my script that is interested with some brief descriptions
// includes an icon in the page to display the correct change
function multilingual(msg,i) {
// code
}
// function to make an ajax call or a "cache call" if value is in localStorage for a variable
function sendRequest(o) {
console.log(o.variab+': running sendRequest function');
// check if value for that variable is stored and if stored for more than 1 hour
if(window.localStorage && window.localStorage.getItem(o.variab) && window.localStorage.getItem(o.variab+'_exp') > +new Date - 60*60*1000) {
console.log(o.variab+': value from localStorage');
// replace <alias> content with cached value
var cached = window.localStorage.getItem(o.variab);
elements[o.counter].innerHTML = cached;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(window.localStorage.getItem(o.variab),o.counter);
} else {
console.log(o.variab+': starting ajax call');
// not stored yet or older than a month
console.log('variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language);
$.ajax({
type: 'POST',
url: my_ajax_url,
data: 'variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language,
success: function(msg){
// ajax call, storing new value and expiration + replace <alias> inner html with new value
window.localStorage.setItem(o.variab, msg);
var content = window.localStorage.getItem(o.variab);
window.localStorage.setItem(o.variab+'_exp', +new Date);
console.log(o.variab+': replacement from ajax call');
elements[o.counter].innerHTML = content;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(msg,o.counter);
},
error: function(msg){
console.warn('an error occured during ajax call');
}
});
}
};
// loop for each <alias> element found
//initial settings
var elements = document.body.getElementsByTagName('alias'),
elem_n = elements.length,
counter = 0;
var i = 0;
for(; i < elem_n;i++) {
var flag = 0;
console.info('var i='+i+' - Now working on '+elements[i].innerHTML);
sendRequest({
variab : elements[i].innerHTML,
api_key : settings.api_key,
language : default_lang,
counter : i
});
$(elements[i]).contents().unwrap().parent();
console.log(elements[i].innerHTML+': wrap removed');
}
I hope that some of you may provide me some valid solutions and/or examples, because we are stuck on this problem :(
From our test, when the value is from cache, the 1st/3rd/5th ... values are replaced correctly
when the value is from ajax the 2nd/4th .. values are replaced
Thanks in advance for your help :)
Your elements array is a live NodeList. When you unwrap things in those <alias> tags, the element disappears from the list. So, you're looking at element 0, and you do the ajax call, and then you get rid of the <alias> tag around the contents. At that instant, element[0] becomes what used to be element[1]. However, your loop increments i, so you skip the new element[0].
There's no reason to use .getElementsByTagName() anyway; you're using jQuery, so use it consistently:
var elements = $("alias");
That'll give you a jQuery object that will (mostly) work like an array, so the rest of your code won't have to change much, if at all.
To solve issues like this in the past, I've done something like the code below, you actually send the target along with the function running the AJAX call, and don't use any global variables because those may change as the for loop runs. Try passing in everything you'll use in the parameters of the function, including the target like I've done:
function loadContent(target, info) {
//ajax call
//on success replace target with new data;
}
$('alias').each(function(){
loadContent($(this), info)
});

Handling asynchronous response in JS - limited access to JS files that can be edited

I am modifying a third party - web client application in which I only have access to certain js files.
The search function is limited to search in one given server node at a time, and as a work around, I hardcoded all the server nodes and created a for loop, invoking the "search" several times, at different nodes.
The server response (in a form of FORM - without getters) are automatically handled by a callback, which then renders the view of the form. This means I am only able to display the last response and thus displaying only one set of result.
To handle this, I added $trs = $(tr).clone(true) on the callback function, saving all the rows from previous forms and then - I made the last loop to "search" to have another callback - which will then append the collected rows from $tr and display the last form complete with all the results from all nodes.
But the result is inconsistent. It sometimes just displays result from one server node. I would think this is caused by some delay in server response which caused that form to render last. I tried to put delay by setTimeout function, but that keeps me from getting any result at all
I am very new with all the web programming - JS and JQUERY both (well CSS and HTML even lol) and I would like to ask for your suggestions on a better way to handle this.
Thank you!
_handleConfigSubmit: function (form, error) {
//alert("_handleConfigSubmit");
if (form) {
var formView = new jabberwerx.ui.XDataFormView(form);
var that = this;
formView.event("xdataItemSelected").bind(function(evt) {
that.jq.find(".muc_search_button_join").removeAttr("disabled");
var resultTable = that.jq.find(".muc_search_results table.result_table");
resultTable.find("tr.selected").removeClass("selected");
that._selectedItem = evt.data.selected;
resultTable.find("tr#"+evt.data.selected._guid).addClass("selected");
});
var searchResultsDiv = jabberwerx.$(".muc_search_results", this.jq);
searchResultsDiv.empty();
this.update();
var dim = {
width: searchResultsDiv.width(),
height: searchResultsDiv.height()
};
formView.render().appendTo(searchResultsDiv);
formView.dimensions(dim);
$trs = $("table.result_table tbody>tr:not(:first)").clone(true);
if ($trList!=null){
$trList = $trList.add($trs);
}else{
$trList = $trs;
}
$("table.result_table tbody>tr:not(:first)").remove()
if (ctr<=3){
$("table.result_table tbody").append($trList);
}else{
ctr++;
}
} else {
this._showError(error);
}
}

how to send an additional parameter for Telerik MVC [JQuery] event handlers

I'm new to jQuery right now I'm using Telerik asp.net MVC control along with Razor view engine. Here is a code snippet from my view.
Html.Telerik().ComboBox().Name("cmb")
.AutoFill(true)
.DataBinding(binding => binding.Ajax().Select("_loadData", "MyController").Cache(false))
.ClientEvents(ce => ce.OnLoad("cmbLoaded"))
function cmbLoaded(e) {
var ComboBox = $("#cmb").data("tComboBox");
//do stuff here
}
Ok this what I'm trying to do I want to send extra parameter to the telerik Combobox event say cmbLoaded event handler. how can i do that.
thanks in advance.
It's been awhile but a ran into a similar situation. The string you pass as a parameter to the OnLoad event is a Javascript function reference, so you can pass something like:
.ClientEvents(ce => ce.OnLoad("function(e) { cmbLoaded.call(this,e," + Model.Id + "); }"))
Then your Javascript function signature would change to:
function cmbLoaded(e, id)
You want to submit extra parameter when your combo is fetching the data from the server?
Then you can send it with the help of the OnDataBinding event.
For example:
function onComboBoxDataBinding(e){
e.data={extraParameter:"Cool"};
}
you can do it by defining some variables in a code block, and then use them in your javascript event handler. Here is an example:
#{
string myPar1 = "some text";
int myPar2 = 10;
}
<script type="text/javascript">
function cmbLoaded(e, '#myPar1' #myPar2) {
var ComboBox = $("#cmb").data("tComboBox");
//do stuff here
}
</sciprt>

Need to change the function name in JS, trying to keep it from breaking

I'm no javascript guru, I'm having to call an external JS file twice in one page. The JS file includes a function. Having this function called twice (once in each JS include) breaks the functionality. So I thought I'd modify the 2nd instance to a different function name. This works to allow the first instance to work correctly but breaks the 2nd one (The one with the function changed).
The function name is address and I'm trying to work just exactly what else needs to be modified in this script to reflect the name change. I fear there are other mentions of "address" that is legitimate and not associated with the function name. I'm at my wits end and am just not sure. Anyone care to look at this JS and help me find which instances of the word address need to be changed to correctly reflect the one function and var name?
/**
* execute part
*/
$(document).ready(function(){
address.bindZipcodeFind();
});
var address = {
bindZipcodeFind: function(){
$('.zipcode-searcha').click(function(){
$('.zipcode-search-resulta').text("로딩중...");
$.get('http://www.nuvonoir.com/postalcode2/zipsearch-action.php',{
query: $('#dongNamea').val()
},function(data){
$('.zipcode-search-resulta').html(data);
address.bindPutAddress();
})
});
},
bindPutAddress: function(){
$('.zipcode-search-resulta a').click(function(){
$('[id=zipcode1a]').val($(this).parent().parent().find('.postcd1').text());
$('[id=zipcode2a]').val($(this).parent().parent().find('.postcd2').text());
$('[id=OrdAddra]').val(address.remove_useless_addr($(this).parent().parent().find('.address').text()));
address.hideZipcodeFinder();
$('[name=addr]').focus();
return false;
});
},
remove_useless_addr: function(address){
if(address.indexOf('~') != -1){
address = address.split(' ').slice(0,-1).join(' ');
}
return address;
},
hideZipcodeFinder: function(){
$('.zipcode-findera').slideUp();
}
}
If you have no way to mitigate including code twice, then there's the only option: write that function was called elsewhere:
global variable
invisible element with certain id
or even more magic things:
field in document object or document root node (html/body)
location hash (URL part after #)
cookie/sessionStorage based on document.lastModified (it is equal to page generating time on server) or anything remaining stable within one page load.
Example using global variable:
function once() {
if (window.myOnceFuncIsCalled) return;
// do the main work
window.myOnceFuncIsCalled = true;
}

Asp net mvc and javascript response

I had a javascript that needs to do two things:
1. Send data to be updated in database
2. Update my html form place in show mode.
3. Update the row of my table to reflect updated data.
My javascript do only 1 and 2:
$(".form-commands .save").live("click", function () {
var f = $(".form-edit");
var sf = f.serialize();
$.post(this.href,
sf,
function (response) {
f.html(response);
});
// I need to do something here to update the html table row...
return false;
});
I think that a solution is to call another action that will render only the table row elements.
How can I do this?
--
The table row was created something like this:
<tr id="h62">
<td>Ford</td>
<td>Focus</td>
</tr>
where 62 is the "id" of this record.
Working code, but ugly:
$(".form-commands .save").live("click", function () {
var f = $(".form-edit");
var sf = f.serialize();
var handle = $(".form-edit #Handle")[0].value;
var itemLink = this.attributes["edititem"].value;
var row = $("#grid #h" + handle);
$.post(this.href,
sf,
function (response) {
$("#form-edit").html(response);
$.get(itemLink,
sf,
function (response) {
row.replaceWith(response);
});
});
return false;
});
You need to do something like this:
$(".form-commands .save").live("click", function (evt) {
//Capture the jQuery event object and call preventDefault() to stop the default click action
evt.preventDefault();
var f = $(".form-edit");
var sf = f.serialize();
$.post(this.href,
sf,
function (response) {
f.html(response);
});
//UPDATE THE ROWS
$('#h62 td:eq(0)').text(newVehicleMakeName);
$('#h62 td:eq(1)').text(newVehicleModelName);
});
I am not sure from your code where the vehicle data is coming from. If you are passing it back from your controller then you will need to move this line into your success callback.
Also, you should generally never return false, you should capture the jQuery event as a param and call preventDefault(). If your click handler uses return false to prevent browser navigation, it opens the possibility that the interpreter will not reach the return statement and the browser will proceed to execute the anchor tag's default behavior. This is what was causing your problem, not because you were using click vs submit. The benefit to using event.preventDefault() is that you can add this as the first line in the handler, thereby guaranteeing that the anchor's default behavior will not fire.
Well I would just reload the page or recall the ajax routine (whichever is applicable) to reload the data, there is no straightforward method to do this. In fact I was not aware of the method you used (f.html(response)), i am still skeptical about that solution :)
Well, if you really just want to update that single row:
1) You need to know to know the updated row's id in your javascript code. This value is "h62" (without quotes) in this example.
2) Give class names to your TDs, e.g.
<tr id="h62">
<td class="brand">Ford</td>
<td class="model">Focus</td>
</tr>
3) Update using jquery. Let's say you hold the id of the row in a variable named "rowId":
$('#'+rowId).find('.brand').html(response.brand);
$('#'+rowId).find('.model').html(response.model);
This will conclude the process.

Categories