I am creating a userscript for a game that will modify certain parts of a page in real time to help the user know how long they must wait to perform certain actions.
The problem I am running into is that the game has some AJAX already built in, every three seconds it calls the jQuery.getJSON() function to grab information to update things. My script needs to make it appear to the end user as if the page was updating in real time, rather than every 3 seconds. As well as add extra information. Without adding extra requests (the games owners will not like that).
To do this I need to override the default behavior of the page, I need to change the callback function of the jQuery.getJSON() call to add my functionality. Or at least disable it completely so I can write a new one. And it isn't as easy as assigning a new function to the old name, as it has no name, they just build the function within the jQuery.getJSON() call. Is this possible?
The page script is contained in a separate .js file btw, if that makes any difference.
If the jQuery.getJSON() call is assigned to a variable, it will return a jqXHR object, which you can then modify by adding or changing its callbacks.
If it is not exposed as a variable, but instead is simply called like so
... js blah ...
jQuery.getJSON("myurl",function(){
more blah
});
... more blah ...
... then I believe you're up a creek without a paddle, as that becomes an anonymous function call with no handle. The only way, at that point, would be to try to override by loading another script in place over the first one, but I am really uncertain how stable that would leave the browser environment.
See the jQuery reference for http://api.jquery.com/jQuery.getJSON/ and http://api.jquery.com/Types/#jqXHR for more details on how the $.ajax() system works.
Related
I'm trying to create a loader that tracks when AJAX calls start and end. It's using JSONP so the .ajaxComplete() doesn't work / isn't reliable.
Ideally I'd like to modify getJSON so that every time it is called a function, addAJAX(), is also called. The callback will also fire a function removeAJAX().
Currently I'm having to do this by adding in functions to every getJSON, of which there are many and likely to be many more.
For example:
// Add ajax tracker
hl.addAJAX();
$.getJSON('someurl.com?callback=?',{ key: APIKEY }, function(json) {
// Remove the ajax tracker
hl.removeAJAX();
});
Creating a wrapper function for AJAX calls is one option, but I'd really like to know if jQuery can be modified this way?
Yes you can override jQuery to do such actions. I have posted one answer for a similar type question. So Instead of re-posting, providing the reference -
How to get default error of ajax call
Technically it certainly could be done -- jQuery is just JavaScript, so you could dig through the sources .js files, find the method in question and modify to your heart's content. However, this certainly isn't a trivial edit and I would strongly advise you against modifying core functionality of third party libraries unless you think that there's no other feasible option and you're ready for what comes along with it.
I say that because that will mean, among other things, that you'll have to maintain those changes when you're trying to move to future versions, and that you may break support for other libraries or plugins which expect certain functions to work certain ways.
As much as it may be annoying to call the function in the callback every time, that's the recommended way of accomplishing this type of functionality.
On page load, I have my DataTable results available which I need to pass back to the javascript for processing.
What are my options?
Use a hidden field to pass the data back up. not sure how exactly, by maybe convert it to xml/json and then access it from javascript that way. Seems like a pain. No extra round trips for this approach.
Use webmethod/webservice to issue a call directly from the javascipt and then get back the DataTable, however this requires an extra round trip since I already have the DataTable available on pageload.
It is also possible to access objects in code behind using ASP.NET inline expressions (i.e. <% syntax) usable from the .aspx page. No extra round trips for this approach.
Convert DataTable to json/xml and then use ASP's ClientScript.RegisterStartupScript to make it available in there as a string returned from a function or something. Sounds hacky though.
Bind the DataTable/DataSet (or any object that implements the IEnumerable interface) to an ASP data control such as a DataGrid, DataList, Repeater, etc... and then just hide the control via some CSS: #datacontrol {display: none;}
How can I do this?
If it is just array, refer this.
If it is custom object, IMO it is better to call server method via. ajax requests and load them into javascript objects and work on data.
There are a number of ways you could do it as you have already reasoned through. Any time I have had a need to do this I have written my data out to a javascript variable using the clientscript.registerclientscriptblock method and then used javascript in the page to access the variable and do the rest of the parsing.
See this for example
You would just use the string builder or whatever you chose to dim a javascript array containing your table values and pass that to the page.
I had this same requirement in one of my asp.net application. What I did was, I created a string variable from the DataTable on Page_Load() by using custom separators like :: ; etc for row-separator and column separator. And then set the string to a hidden field or textbox text with CSS {display: none;}
After that, you can get the value of the hidden-field or TextBox in javascript $(document).ready() block or javascript pageLoad function() if you want it to be executed on every full/partial postback.
Get the value and decode it as you had coded the DataTable into the string in code-behind, and process it as u need.
Hope it helps :)
After trying several of the above options, I've found #3 (ASP.NET inline expressions) to be the best choice for accessing an ADO query result such as a DataTable or DataSet since it was the quickest to implement and had no additonal round trips due to the fact the inline expressions are resolved during the page construction.
I tried #5 (bind data to data control, then hide it), but it was noticeably slower and I had problems finding a control that would expose all the features of a DataTable/DataSet. There are a handful of data controls that you can bind records to, but what I found was that much of the "nice-ities" that come with a DataSet/DataTable are lost when converting to a repeater control or such. If you use one of the more full featured ASP controls available to get more of those features back, you lose on performance since those controls are meant for read/write and to render the content for display. And it wasn't as simple as I expected to access the data as it is in the code behind.
I thought about #4 (passing data thru ASP's ClientScript.RegisterStartupScript), but didn't feel like serializing/deserializing every object I need to expose and working though any hiccups that come with it. It just didn't seem like the right way. I suppose it would be fine for simpler objects though.
And #1 (serialize data to a hidden field) is pretty much the same concept as the above #4 (passing data thru ASP's ClientScript.RegisterStartupScript so I didn't bother with that one either.
The other (2nd best) possibility is to do #2 (using webmethod/webservice) as #Sundeep and #ron_tornambe have pointed out. However, this option adds an extra round trip to the page request and since the above scenario has the DataTable/DataSet ready for consumption on page load it is not optimal for me. If it wasn't for that I'd say it was equal to my first choice of #3 (ASP.NET inline expressions) since you'll get the full featured object to work with.
I am currently implementing a graph visualisation tool using lift on the server side and d3 ( a javascript visualisation framework) for all the visualisation. The problem I have is that in the script I want to get session dependent data from the server.
So basically, my objective is to write lift-valid ajax callbacks in a static js script.
What I have tried so far
If you feel that the best solution is one that I already tried feel free to post a detailed answer telling me how to use it exactly and how it completely solves my problem.
Write the ajax callback in another script using lift and call it from the main script
This solution, which is similar to a hidden text input is probably the more likely to work. However it is not elegant and it would mean that I would have to load a lot of scripts on load, which is not really conveniant.
This seems to be one of the prefered solutions in the lift community as explained in this discussion on the mailing list.
REST interface
Usually what one would do to get data from a javascript function in lift is to create a REST interface. However this interface will not be linked to any session. This is the solution I got from my previous question: Get json data in d3 from lift snippet
Give function as argument of script
Another solution would be to give the ajaxcallback as an argument of the main script called to generate my graph. However I expect to have a lot of callbacks and I don't want to have to mess with the arguments of my script.
Write the whole script in lift and then serve it to the client
This solution can be elegant, however my script is very long and I would really prefer that it remainss static.
What I want
On client side
While reviewing the source code of my webpage I found that the callback for an ajaxSelect is:
<select onchange="liftAjax.lift_ajaxHandler('F966066257023LYKF4=' + encodeURIComponent(this.value), null, null, null)" name="F96606625703QXTSWU" id="node_delete" class="input">
Moreover, there is a variable containing the state of the page in the end of the webpage:
var lift_page = "F96606625700QRXLDO";
So, I am wondering if it is possible to simulate that my ajaxcall is valid using this liftAjax.lift_ajaxHandler function. However I don't know the exact synthax to use.
On server side
Since I "forged" a request on client side, I would now like to get the request on client side and to dispatch it to the correct function. This is where the LiftRules.dispatch object seems the best solution: when it is called, all the session management has been made (the request is authentified and linked to a session), however I don't know how to write the correct piece of code in the append function.
Remark
In lift all names of variables are changed to a random string in order to increase the security, I would like to have the same behavior in my application even if that will probably mean that I will have to "give" the javascript these values. However an array of 15 string values is still a better tradeoff than 15 functions as argument of a javascript function.
Edit
While following my research I found this page : Mapping server functions to client actions which somehow explains the goal of named functions even if it stil didn't lead me to a working solution.
Quick Answer
Rest in Lift does not have to be stateless. If you register your RestHelper with LiftRules.dispatch.append, then it will be handled statefully and Session information will be available through the S object as usual.
Long Answer
Since you seem interested, and it's come up on SO before, here's a more detailed explanation of how server-side functions are registered and called in Lift. If you haven't worked with Lift for some time, look away. What follows should not in any way be used to evaluate Lift or its complexity. This is purely library developer level stuff and a majority of Lift users go about their development blissfully unaware of it.
How it works
When you create stateful callbacks, typically by using the methods within the SHtml object, what you are really doing is registering objects of type S.AFuncHolder within the context of the users session, each with a unique ID. The unique ID that was generated during this process is what you're seeing when you come across a pattern like F96606625700QRXLDO. When data is submitted, via form post, ajax, or whatever, Lift will check the request for these function ids and execute the associated function if they exist. There are several helpers that provide more specific types of AFuncHolder, like S.SFuncHolder (accepts a single string query parameter) and S.BinFuncHolder (parameter is multipart form data) but they all return Any and behind the scenes Lift will collect those return values to create the proper type of response. A JsCmd, for instance, will result in a JavaScriptResponse that executes the command. You can also return a LiftResponse directly.
How to use it
AFuncHolders are registered using the S.fmapFunc method. You'd call it like this
S.fmapFunc(SFuncHolder({ (str: String) =>
doSomethingAwesomeWithAString(str)
}))(id => <input type="text" name={id} value=""/>)
The first parameter is your function, wrapped in the proper *FuncHolder type and the second parameter is a function that takes the generated id and outputs something. The something that gets output is what you will include on the page. It should somehow result in the id being sent to the server as a query parameter so that your function is executed.
Putting it all together
You could use the above to make your own Ajax calls, but when Lift makes an ajax call there are a few other considerations:
1) Most browsers only allow so many simultaneous connections to a given domain. Three seems to be the magic number.
2) AFuncHolders will often close over the scope of the snippet they are contained within and if multiple ajax requests are handled at once, each in its own thread, bad things can happen.
To combat these issues, the liftAjax.lift_ajaxHandler function queues each ajax request, ensuring that only one at a time is sent to the server.
The drawback to this approach is that it can make it difficult to make an Ajax call where the result needs to be passed to a callback. JQuery autocomplete, for instance, provides a callback function when input changes that accepts a list of matches. If you are manually calling LiftAjax.lift_ajaxHandler though, you can provide your own callback functions for success & error and I would recommend that you look at the source of those functions in your browser for more information on how they work.
There's actually more to it, like how Lift restores RequestVars on ajax callbacks (which is where the lift_page comes in, but that's about all I'm prepared to explain over coffee on a Saturday morning :)
Good luck with your app!
So in my page I have some little scripts which I dont really need to load once you visit the site and in fact the user might not need them at all in their entire session.
Also, according to this: http://code.google.com/speed/page-speed/docs/payload.html#DeferLoadingJS its not a good practise either.
So for example, currently I have everything in 'When dom ready':
$(function() {
// most of the code of which is not needed
});
If I dont place the code inside the Dom ready, its not executable at most of the times. So I thought of doing seperate functions for each snippet.
For exmaple:
function snippet1() {
// Code here
}
and then when I need that snippet, I load it when needed with mouseclick. (Not always a mouselcick, depends what I need to load).
For example:
$('#button1').click(function() {
snippet1();
});
So my question is: Is this the way of loading functions async so you reduce the page load time or is there a better way? I havent read this anywhere my examples, I just thought of it.
Note that I am aware of the asynch loading's but that is not my option here, since I could combine all the functions in just one js file which will be cached, so page load time will be less than loading every time asynch js files.
You're mixing several things:
Page load time
JavaScript parsing time - After the script is loaded, it has to be parsed (error checking, compiling to byte code, etc)
Function execution time
You can't do much about the page load time since you don't want to split the script. You may consider to split it into two parts: One which you always need and an "optional" part which is rarely needed. Load the rare functions in the background.
Note that page load times become pretty moot after the site has been visited once and you've made sure the browser can cache the files.
If you want to reduce parse times, you have two options:
Don't load parts that you don't need.
Compress the scripts. Google has a great tool for that: the Closure Compiler. Besides making your scripts faster, it will also check for many common mistakes.
The last part is the execution times. These are only relevant if the functions are called at all and when they do a lot. In your case, I guess you can ignore this point.
Yes, as much as possible you should define objects, functions, etc. outside of the document.ready wrapper. Some devs will define absolutely everything outside the wrapper and then just call an init() function inside the wrapper to load everything else. I am one such dev.
As for async, this doesn't do true async loading, but it speeds up your page since there is much less work to do on page load.
In general, if you're not using a script loader like requireJS or yepnope, it's a good idea to put all your script references – or at least those that don't need to be run instantly – at the end of your body so the page renders before the resources that aren't going to be run until after page load anyway.
I would load all additional resources using RequireJS ( http://requirejs.org/ ) or similar library.
Put everything that you don't need immediately to separate script and load it after main content is loaded.
If I want to add an isEmpty method to all JavaScript arrays, I would use the following code
Array.prototype.isEmpty = function() {
return this.length == 0;
}
Assume this code is in a file foo.js. If I want isEmpty to be available on all pages of a web site, would I need to include foo.js in all the HTML files? In other words, do the prototypes get "reset" whenever the user navigates to a different page?
Thanks,
Don
Yes, you wil need to include your code on each page load.
Think of each page load as a compile/linking cycle. All the various bits of Javascript on the page are linked together1 and then executed as one giant program. The next time a page is loaded, the default Javascript objects start in a fresh state.
1. Linked together in a brain-dead "every piece of code shares the same global namespace" fashion
Yes, you will have to modify the prototype after each page loads
yes, http is stateless so each page is loaded separately.
however adding to Array.prototype isn't a good idea. it means that if you try and loop round it you can get yourself into trouble.