I am working with some legacy code and I'm looking for a safe way to handle the following scenario:
I have an iframe showing a product selection page. I need to pass this iframe the name of a callback function with a query string.
In the iframe I get the callback name from the query string and execute it like so:
$("iframe#content:first", window.top.document)[0].contentWindow[callbackName]();
But this only works if the callback is a globally exposed function. That's lame.
This callback should live in a nested object.
Is there any way I can specify a callback like :
'myNamespace.nestedNamespace.myCallbackFunction'
and execute it off of the contentWindow object ?
In that case, if I understand correctly, something like the following should do the trick:
// Your callback "path".
var callbackPath = 'myNamespace.nestedNamespace.myCallbackFunction';
if (callbackPath != '') {
// We will need to walk our way to the actual callback.
var parts = callbackPath.split('.');
var callback = $("iframe#content:first", window.top.document)[0].contentWindow;
for (var i = 0; i < parts.length; ++i) {
callback = callback[ parts[i] ];
// Was unable to find callback, bail!
if (!callback)
break;
}
if (typeof callback === 'function')
callback();
}
Related
How to show callback function's parameter to user?
I'm writing a pagination component for table by javascript.
And this is my component code. (not finished yet)
function ComponentPagination(paginationAreaID, pageViewDataCount ,
totalDataCount, ajaxUrl)
{
//div
var paginationArea = document.getElementById(paginationAreaID);
//prev button
var prevBtn = document.createElement("a");
prevBtn.textContent = "Prev";
paginationArea.appendChild(prevBtn);
//page buttons
var pageCount = (totalDataCount / pageviewDataCount);
for (var i = 0; i < pageCount; ++i) {
var pageBtn = document.createElement("a");
pageBtn.textContent = (i + 1) + "";
paginationArea.appendChild(pageBtn);
}
//next button
var nextBtn = document.createElement("a");
nextBtn.textContent = "Next"
paginationArea.appendChild(nextBtn);
//Use this method to set callback function
//The call back function called when user click page button <a> or
//next or prev button.
this.addCallBack = function(onUpdatePagingMethod)
{
// (example)
var clickedButton;// = clickedButtonIndex;
this.onUpdatePagingMethodCallBack = onUpdatePagingMethod;
//and then it will call when update will neccessay
//like this.onUpdatePagingMethodCallBack(clickedButton, ajaxUrl);
}
}
User will use this like..
<script>
windows.onload = function()
{
//the total data is 105 and the page will show each 10 items
var pagination = new ComponentPagination("pageArea", 10,
105,"/API/FileList");
//Register CallBack
pagination.addCallBack( onPageUpdate );
}
And then the user will design the callback function which name is on PageUpdate.
But, user can't know the callback function's parameter info which
addCallBack() method want. Like This.
function onPageUpdate(/* hum? how should i know the parameter? */)
{
}
well.. In c or c++ have function pointer(maybe they use typedef), so it can limit the parametes numberand each type and user can infer how to design callback function and parameters meaning.
I have no ideas how to limit or invoke parameter info to user in javascript.
Is there have any ideas about this? The comment is only way to solve this problem?
p.s : Not like ts, i want only js.
Javascript doesn't have any way to declare the signature of callback functions in code. You should put it in the documentation of your component, e.g.
onPageUpdate: function(string argName1, object argName2, ...)
For an example, see how jQuery describes the callback function in jQuery.each
Well if you want the parameters you can use arguments:
function onPageUpdate()
{
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
All function parameters are present in the Arguments Scope
you do console.log(arguments) inside any function and it'll log all the parameters on your console.
Edit:
predefining a signature of a callback isn't possible in JavaScript except via the usual way of documentation by a comment.
This is possible in TypeScript though, in a similar format.
public myCallback: (name: type) => returntype;
I have a function in Web Sql for multilanguage.
I need to set the language after the callback of a query, calling by id.
Example:
var lng = function(language){
var testo_query="SELECT id," + language + " FROM lingue;";
comanda.sincro.query(testo_query,function(result){
result.forEach(function(obj){
//those values will be go into an array
//language[id]=obj[language];
console.log(obj.id,obj[language]);
});
});
};
Then i'd want to write in HTML:
<p><script>language[0]</html></p>
and in the js function:
'('+language[1]+')'
But just after the callback function, otherwise the array will not be created.
Could it be done?
https://jsfiddle.net/tbatnwb4/1/
You can use JQuery to manipulate the DOM with functions like 'append' or 'prepend' inside the forEach and call something like this for calling the function from a string value:
var fn = window[settings.functionName];
if(typeof fn === 'function') {
fn(language[1]);
}
getting error "could not locate bindings file"
trying to search for blog posting, then lookup banner based on blog abbrev, then write out
without banner lookup, everything works fine
think we are screwing up the callback but can't find good example
app.get("/posting/:id", function(req,res) {
var db = app.get('db');
var id = [req.params.id];
console.log(id);
db.run("select * from postings where id=$1",[1], function(err,posting) {
console.log(posting);
var choice = posting[0].choice;
var banner = banner_lookup(choice, function(err, banner) {
console.log(banner);
res.render("posting", {posting:posting[0], banner:banner});
});
});
});
function banner_lookup(abbrev) {
console.log("banner lookup");
for (i=0;i++;i<banners.length) {
if (abbrev == banners[i].abbrev) {
console.log(banners[i]);
return (banners[i]);
}
}
return {"name":"","color":"#000"};
}
var banners = [{"abbrev":"aaa","name":"Newsletter A", "color":"#888888"}];
The problem lies in this part of the program. You are basically passing two arguments to the banner_lookup function - the variable choice and the callback function where you expect the banner to be available after the lookup
var banner = banner_lookup(choice, function(err, banner) {
console.log(banner);
res.render("posting", {posting:posting[0], banner:banner});
});
However the actual function definition of banner_lookup accepts only one named argument - the choice. The banner_lookup function calculates and returns the banner value but it is not being passed to the callback function like you expect it to. You will have to explicitly write the functionality to support callback functionality in banner_lookup
Your code (with callback functionality) would look like this
function banner_lookup(abbrev, callback) {
console.log("banner lookup");
var bannerOutput = {"name":"","color":"#000"};
for (i=0;i++;i<banners.length) {
if (abbrev == banners[i].abbrev) {
console.log(banners[i]);
bannerOutput = banners[i];
}
}
if(typeof callback == "function") {
callback(null, bannerOutput);
} else {
return bannerOutput;
}
}
The above method now accepts a callback parameter, and if it is defined as a function, then passes the output to the function else returns it like earlier.
Tip 1 : You don't really need to implement callback functionality if the banner_lookup function is doing simple tasks in sync. You can simply write
var banner = banner_lookup(choice);
console.log(banner);
This banner will have the expected result.
Tip 2 : And as Amit pointed out in his comment, read about SQL injection attacks. Make sure you sanitize all variables that go into making a DB query (You are directly accepting the id parameter from the route and passing it to the DB query which can be easily misused.
Your function banner_lookup requires only 1 parameter on its decleration, yet you pass it 2 when you call it (choise and the callback function).
Pretty sure this has been asked already, but I don't know what to search for. Anyway,
var livemarks = [];
var livemarkIds = PlacesUtils.annotations.getItemsWithAnnotation("livemark/feedURI", {});
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
}
});
}
alert(livemarks.length);
I am trying to play a bit with a Firefox addon that's no longer maintained by its creator, just to learn a bit. I recently got an error saying getFeedURI is going to be deprecated and I want to change his old function.
EDIT:
From a function defined in a function (inner function), I am unable to access a var defined in the parent. Why?
E.g. I cannot access var livemarks from inside getLivemark(), or other similar internal functions.
I was checking (scroll down completely): this and his code works fine. So what's wrong with my code? I just wanted to avoid the recursion, if possible.
I suspect the PlacesUtils.livemarks.getLivemark function does its work asynchronously, so your callback is called after you alert the length. Put your alert inside the callback and you should see the correct length (eventually). Here's one way:
var expecting = livemarkIds.length;
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
// ***New stuff***
if (livemarks.length === expecting) {
// Now you have them all, now you can do the next thing
doSomethingWithTheLiveMarks(livemarks);
}
}
});
}
Note that there I put livemarkIds.length into expecting, just in case you do other things with livemarkIds while the function is running. If you aren't, you can just use that directly.
Re your comment below:
However, the system works like this: I get the livemarks in an array. This code is in a class (and method) actually, so another class initializes this one and will call the function getFeeds(), which will return that array of livemarks.
If PlacesUtils.livemarks.getLivemark is asynchronous, it's impossible for getFeeds to return the array as a return value. E.g., it cannot be used like this:
a = b;
c = 42;
feeds = getFeeds(feedIds);
if (feeds.length === 0) {
// Do something
}
else {
// Do something else
}
The good news is it's really easy to fix: Have getFeeds accept a callback function that it calls when it has the feeds. The code above changes to look like this:
a = b;
c = 42;
feeds = getFeeds(feedIds, function(feeds) {
if (feeds.length === 0) {
// Do something
}
else {
// Do something else
}
});
As you can see, it's a pretty straightforward change. Assuming the loop above is all of getFeeds, then getFeeds ends up looking something like this:
function getFeeds(livemarkIds, callback) {
var livemarks = [];
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
if (livemarks.length === livemarkIds.length) {
// Done, trigger the callback
callback(livemarks);
}
}
});
}
}
And the pattern continues: If the code calling getFeeds is being called by something else that's expecting a return value from the async stuff, instead of returning that value, you have that code accept a callback, and call the callback from the getFeeds callback. And so on.
Once you get used to it, it's very easy to do. Getting used to it can be tricky. :-)
I have the following code which is designed to create an onchange event handler for all elements with class name 'State'. The only problem is that I want the element to be passed into the 'StateChange' function. How can I update this JS to pass 'this' into the StateChange function? Thanks!
var c = document.getElementsByClassName('State');
for (i = 0; i < c.length; i++) c[i].onchange = createEventHandler( StateChange, c[i] );
Edit: I forgot to provide the createEventHandler function. Sorry about that... Here it is:
function createEventHandler(fn, input) {
return function () {
fn(input);
};
}
Also, some clarification. The purpose of the function is to obviate the need to put the onchange event next to each element with class name = 'State'. The result should be the same as if I were to write:
<select id="MyState1" class="State" onchange="StateChange(this)">
Update:
Re your updated question: You've said that the end result you want is as though you'd done this:
<select id="MyState1" class="State" onchange="StateChange(this)">
Your quoted createEventHandler function does exactly that.
Original Answer(s):
I'm not entirely sure I know exactly what you're trying to do. I can read the question at least two ways:
Inside the StateChange function call, you want this to refer to the element that changed.
Inside the StateChange function call, you want this to be the same as this where you're setting up your event handler.
Option 1: You want this = element within StateChange
You don't actually have to pass the element instance into createEventHandler, because when the event occurs, this will refer to the element because of the way you're hooking it up. But if you prefer to set it explicitly, your createEventHandler function could look like this:
function createEventHandler(handler, element) {
return function(event) {
return handler.call(element, event || window.event);
};
}
What that does is return a function that, when the event is triggered, will call the function you pass in (StateChange) with this set to the element you pass in.. This uses the JavaScript call feature of function objects, which allows you to define what this will be during the function call. You just pass it in as the first argument to call (subsequent arguments are passed on to the function being called).
If you want to rely on the fact that the way you're setting up the handler, this will already be set to the element instance, you can do away with the element argument:
function createEventHandler(handler) {
return function(event) {
return handler.call(this, event || window.event);
};
}
That just passes along the this value set up for the event handler by the browser.
Option 2: You want this = this as of where you're setting up the handler
It's the same principle as the above, just with a different argument. In this case, you'll need to pass this in:
var c = document.getElementsByClassName('State');
for (i = 0; i < c.length; i++) c[i].onchange = createEventHandler( StateChange, this, c[i] );
...and then createEventHandler looks like this:
function createEventHandler(handler, thisArg, element) {
return function(event) {
return handler.call(thisArg, event || window.event, element);
};
}
(Note I've passed in the element as a second argument to StateChange.)
More reading:
Mythical methods
You must remember this
One way is:
var c = document.getElementsByClassName('State');
for (i = 0; i < c.length; i++)
c[i].onchange = createEventHandler(function(){
StateChange(c[i]);
});