Using a javasscript closure that exist in an external site - javascript

I want to use a javascript file that exist in another host.
the file contain a closure on it
//http://ExternalHost.com/somefile.js
(function(){
//...........
//...........
return { x:1, y:2 };
})();
and I want to use the returned object of the closure.
My question is
1) how can I get that file?
2) how can I use the return value of closure?
I know we can use the returned value if the file likes below
//http://ExternalHost.com/somefile.js
window.returnedObject = (function(){
//...........
//...........
return { x:1, y:2 };
})();
but the problem is: window.returnedObject is global!

Well, let's analyze it.
somefile.js resides in another domain. So, you should request it from yourdomain.tld and it's on externalhost.com. You have many options:
Request it via a simple HTTP Get method, which is issued by putting a <script /> tag anywhere on your DOM. This way, browser gets the file, executes it on its arrival, and you have to way to hook into this process, unless you and external host both agree on some similar protocol like JSONP, which is of course for data, not for libraries. So, this option is not useful.
You use XMLHttpRequest to load the file either synchronously or asynchronously. But XMLHttpRequest is by design under strict control of same-origin policy. So, external host should let you use it via some HTTP Headers, like Access-Control-Allow-Origin. This of course is applicable to normal request too. However, when you use XMLHttpRequest, you have the chance to hook into the load of the content, and do something on it. However, in this case, browser compiles JavaScript and runs it before giving it to XMLHttpRequest (your ajax call). So, again no use.
Understand the true meaning and philosophy of closure in JavaScript. It's like private access modifier in object-oriented programming. Technically it's there to NOT ALLOW you to access it :D. This options works. But it only increases your knowledge, to not expect to use that closure.
So, I think you can't AMAIK.

Related

js "scripts" folder name prepended to XMLHttpRequest calls

I have a javascript file named myscripts.js in the "scripts" folder of my webserver. It could be accessed with this:
http://www.example.com/scripts/myscripts.js
Within myscripts.js is a javascript function which makes a XMLHttpRequest call to somemethod.html of my website. Here is the calling code:
xmlhttp.open("GET","somemethod.html",false);
99% of the time everything works fine. But I am finding some browsers are prepending "scripts/" to the call. So the result is a call like this:
http://www.example.com/scripts/somemethod.html
when it should be this:
http://www.example.com/somemethod.html
This is a custom built webserver (i.e. I basically handle ALL requests).
Should my webserver be able to handle this? Or is this just some fluky browser that I should not worry about?
Should I not be using "relative" paths in the javascript? And instead use absolute calls in the java script? e.g.: instead of "somemethod.html" it should be coded like this:
xmlhttp.open("GET","http://www.example.com/somemethod.html",false);
It's absolutely fine (and overwhelmingly the standard of practice) to use relative paths in the JavaScript, just be aware of what they're relative to: The document in which you've included the JavaScript (not the JavaScript file.) You seem clear on this, but just emphasizing.
I've never seen a browser get this wrong. It's possible the requests you're seeing are from a poorly-written web crawler looking at the source of the JavaScript rather than doing something intelligent like figuring out where/how it's run.
Just for clarity, though, about the relative thing (more for lurkers than for you):
Given this structure:
foo.html
index.html
js/
script.js
In that structure, if you include script.js in index.html:
<script src="js/script.js"></script>
...then use code in that script file to do an XHR call, the call will be relative to index.html, not script.js, on a correctly-functioning browser.
I never use relative requests, I built my own url handing js code to build up and pass urls around with a 'toString' method to give me exactly the url I need.
Also as an aside, try not to use synchronous XHR calls anymore, ideally you should use async and call backs, it's a pain, but it's for the best.
client . open(method, url [, async = true [, username = null [, password = null]]])
Sets the request method, request URL, and synchronous flag.
Throws a "SyntaxError" exception if either method is not a valid HTTP method or url cannot be parsed.
Throws a "SecurityError" exception if method is a case-insensitive match for `CONNECT`, `TRACE` or `TRACK`.
Throws an "InvalidAccessError" exception if async is false, the JavaScript global environment is a document environment, and either the timeout attribute is not zero, the withCredentials attribute is true, or the responseType attribute is not the empty string.
source: http://xhr.spec.whatwg.org/#the-open%28%29-method

Call a function in one Javascript file from another Javascript file?

I need to call a function in an external ".js" file from another ".js" file, without referencing the external file in the <head> tag.
I know that it is possible to dynamically add an external ".js" file to the which allows access to that file, i can do that like so...
var AppFile = "test/testApp_1.js";
var NewScript=document.createElement('script');
var headID = document.getElementsByTagName("head")[0];
NewScript.src = AppFile;
headID.appendChild(NewScript);
However...
this is no use to me as the external files need to be stand-alone files that run start-up procedures on...
$(document).ready(function()
{...}
so adding the full file dynamically has an unwanted affect. Also, i cannot pre-reference the external file in the <head> tag as it needs to be dynamic.
So, this external file "test/testApp_1.js" contains a function that returns a string variable...
function setAppLogo(){
var LogoFile = "test/TestApp_1_Logo.png";
return LogoFile;
}
I need access to either this function, or I could store the string as a global var in the external file... either way is fine, I just need access to the value in LogoFile without loading the whole external file.
This one has had me stumped for a few hours now so any ideas would be greatly appreciated.
You might benefit from having some sort of app.js file that contains global variables/values that you will want to use from lots of places. You should include this .js file on every page (and maybe minify it/concatenate it with other js if you want to be clever and improve performance). Generally these globals should be attached to some object you create such as var APPNAME = { }; with variables/functions on it that will be used from many places.
Once you have this, then the external '.js' file that you want to load, and the one you are currently in, can both access the global APPNAME variable and all its attributes/functions and use them as desired. This may be a better approach for making your javascript more modular and separatable. Hope this helps.
You want to load the file once jQuery has loaded using ajax, and then run the related script in the successful ajax function.
See jQuery's getScript function: http://api.jquery.com/jQuery.getScript/
$(document).ready(function(){
$.getScript("http://domain.com/ajax/test.js", function(data, textStatus, jqxhr) {
console.log(data); //data returned
console.log(textStatus); //success
console.log(jqxhr.status); //200
console.log('Load was performed.');
//run your second script executable code here
});
});
It is possible to load the whole script through XHR (e.g. $.get in jQuery) and then parse it, perhaps using a regular expression, to extract the needed part:
$.get('pathtoscript.js', function(scriptBody) {
var regex = /function\s+setUpLogo\(\)\s*\{[^}]+}/g;
alert(scriptBody.match(regex)[0]); // supposed to output a function called
// 'setUpLogo' from the script, if the
// function does not have {} blocks inside
});
Nevertheless, it shall be noted that such an approach is highly likely to trigger maintenance obstacles. Regular expressions are not a best tool to parse JavaScript code; the example above, for instance, will not parse functions with nested {} blocks, which may well exist in the code in question.
It might be recommended to find a server-side solution to the problem, e.g. adding necessary script path or its part before the page is sent to browser.
I'm not sure this is a good idea but you can create an iframe and eval the file inside its 'window' object to avoid most of the undesired side effects (assuming it does not try to access its parent). Then you can access whatever function/variable you want via the iframe's window object.
Example:
function loadSomeJsInAFrame(url,cb) {
jQuery.get(url,function(res) {
iframe = jQuery('<iframe></iframe>').hide().appendTo(document.body);
iframe[0].contentWindow.eval(res);
if(cb) cb(iframe[0].contentWindow);
},'text');
}
loadSomeJsInAFrame('test/testApp_1.js',function(frameWindow) {
console.log(frameWindow.setAppLogo());
jQuery(frameWindow.frameElement).remove();
});
This will not guarantee that the sript in the file can not mess with your document, but not likely if it comes from a trusted source.
Also, don't forget to remove your iframe after you get what you need from it.
Ok, thanks everybody for all the input but i think that what I was trying to do is currently not possible, i.e. accessing a function from another file without loading that file.
I have however found a solution to my problem. I now query my server for a list of apps that are available, i then use this list to dynamically build the apps in a UI. when an app is then selected i can then call that file and the functions within. Its a bit more complex but its dynamic, has good performance and, it works. Thanks again for the brainstorming! ;)
It may be possible with the help of Web Workers. You would be able to run your script you've wanted to inject in kinda isolated environment, so it won't mess up your current page.
As you said, it is possible for setAppLogo to be global within "test/testApp_1.js", so I will rely on this statement.
In your original script you should create a worker, which references to a worker script file + listen to messages that would come from the worker:
var worker = new Worker('worker.js');
worker.onmessage = function (e) {
// ....
};
Then, in the worker (worker.js), you could use special function importScripts (docs) which allows to load external scripts in worker, the worker can also see global variables of these scripts. Also there is a function postMessage available in worker to send custom messages back to original script, which in turn is listening to these messages (worker.onmessage). Code for worker.js:
importScripts('test/testApp_1.js');
// "setAppLogo" is now available to worker as it is global in 'test/testApp_1.js'
// use Worker API to send message back to original script
postMessage(setAppLogo());
When it invokes you'll get the result of setAppLogo in you listener:
worker.onmessage = function (e) {
console.log(e.data); // "test/TestApp_1_Logo.png"
};
This example is very basic though, you should read more about Web Workers API and possible pitfalls.

Why does this cross-domain request workaround work?

In this John Resig article, he's is dealing with a dictionary-sized list of words with javascript, and he's loading the content via ajax from a CDN.
The words are loaded in with newlines separating the words. Then he says cross domain fails:
There's a problem, though: We can't load our dictionary from a CDN!
Since the CDN is located on another server (or on another sub-domain,
as is the case here) we're at the mercy of the browser's cross-origin
policy prohibiting those types of requests. All is not lost though -
with a simple tweak to the dictionary file we can load it across
domains.
First, we replace all endlines in the dictionary file with a space.
Second, we wrap the entire line with a JSONP statement. Thus the final
result looks something like this:
dictLoaded('aah aahed aahing aahs aal... zyzzyvas zzz');
This allows us to do an Ajax request for the file and have it work as
would expected it to - while still benefiting from all the caching and
compression provided by the browser.
So, if I'm reading this correctly, simply adding his method dictLoaded('original content') around the original content alone causes the ajax request to not fail.
Is that (turning it into a function + param) really all it takes? and why does JSONP solve the problem of cross domain access restriction?
the <script> tags can load any JS file from anywhere (even cross domain). The nice thing that comes with it is that the code inside that script is also executed, therefore, a method of bypassing cross-domain restrictions.
The problem is, when the code gets executed, it's executed in the global scope. so having this code:
var test = 'foo'
will create a test variable in the global scope.
To mitigate this, you use enclose the reply in a function. This is the "P" in "JSONP" which means "padding". This encloses your reply in a function call.
So if your foreign script has:
myFunction({
test : 'foo'
});
It calls myFunction and passes an object with test key which has value foo. The receiving function would look like:
function myFunction(data){
//"data.test" is "foo"
}
Now we have successfully bypassed the cross-domain restriction. The essential parts needed are:
the receiving function (which can be dynamically created and discarded after use)
the "padded" JSON reply
Is that (turning it into a function + param) really all it takes?
Yes.
and why does that solve the problem of cross domain access restriction?
You should read about JSONP. The idea is that you can now include a <script> tag dynamically pointing to the resource instead of sending an AJAX request (which is prohibited). And since you have wrapped the contents with a function name, this function will be executed and passed as argument the JSON object. So all that's left for you is to define this function.
It is because of the JSONP statement that he added.
"JSON with padding" is a complement to the base JSON data format. It provides a method to request data from a server in a different domain, something prohibited by typical web browsers because of the Same origin policy.
This works via script element injection.
JSONP makes sense only when used with a script element. For each new JSONP request, the browser must add a new element, or reuse an existing one. The former option - adding a new script element - is done via dynamic DOM manipulation, and is known as script element injection. The element is injected into the HTML DOM, with the URL of the desired JSONP endpoint set as the "src" attribute. This dynamic script element injection is usually done by a javascript helper library. jQuery and other frameworks have jsonp helper functions; there are also standalone options.
Source: http://en.wikipedia.org/wiki/JSONP

accessing json object with javascript (no var) - dynamically called in file (experiment)

I am creating a page which calls dynamic content from the web with javascript and obviously run into a snag with the SOP (same origin policy) I know this would not work on a production page as the calls would need to be checked on the server but I have it at a point where it practically works but I cant access the data. I would really like to know if it is possible.
The code below adds the URL to the file:
var url = 'http://theSite.com/?query=' + userInput + '&othercode';
var script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', "application/json");
script.setAttribute('id', "special");
document.getElementsByTagName('body')[0].appendChild(script);
This adds the following to my html:
<script src="http://theSite.com/?query=userInput&othercode" type="application/json" id="special"></script>
Now the problem:
How do I access the resulting data?
I get a JSON script but dont know how to attach it to a variable in Javascript or use it!
the code arrives in this basic format:
{"count":5,"results":[{"name":"value","id":"value2", ..... }], ... "code":200}
How do I access the data or assign it to a variable?
(I have not yet tried jQuery and know it will probably be easier but if anyone can help with this code that would be great rather than trying to do this all again in jQuery).
You need to have control of the service you're calling (theSite.com). One option is to make it return JSONP instead of JSON:
dataCallback({"count":5,"results":[{"name":"value","id":"value2", ..... }], ... "code":200});
Or, make your script inside an iframe that is served from theSite.com, and use postMessage to communicate its result back to your window.
Otherwise... well, you're doing exactly what SOP is looking to prevent, so the chances are grim, AFAIK.
Oh, one more option: make an AJAX call to your server, which will make the service call and pass the data to you. Servers are not restricted like clients are.

How to get an included js file's contents without xhr?

is this possible?
<html>
<script src="local.js>
<script>
// get contents of local.js file here without doing an ajax call?
</script>
</html>
local.js resides on the same server, and I know that by doing an xhr call I can get its contents (if not on file://).
But, as it is already requested synchronously by the browser, its contents is known to the document so I hope there is a way to access it? The document.scripts collection was no help to me.
Somewhat like getting innerHTML (which works for scripts defined in-page)?
I'm not sure on how to get the included javascript code, nor why you would need this, but how about going the other direction?
Instead of having a script tag, make a XHR call to the file and eval its contents + keep its contents as a variable also.
**Disclaimer: I cannot see why you would need this, nor would I actually suggest you use this method, but it's a work-around.
Are you assuming XHR will not use the cached version? It will, there may be a request, but it should be fast (reuse the same HTTP connection) and return 304 (not modified). So the cached version will be used unless your JavaScript file's HTTP response headers prohibit or do not specify caching directives (but usually they should).
I suspect that while the contents of the script are known to the browser, they're not known to the document, and are therefore not accessible via the DOM API. So you will have to use the XHR approach. With a bit of luck, if you ensure the script is properly cached, the XHR request will pull the script contents from the local cache anyway.

Categories

Resources