RubyRacer allows me to easily execute javascript code within a ruby environment, like so:
cxt = V8::Context.new
wanted_output = cxt.eval(whatever_javascipt_code)
How does one have to do it when objects like "document" are involved, like for example in the follwing code snippet?
(In other words, how can I generate the output of "document.write" in this case?)
<script language="javascript" type="text/javascript">
A="some_string";
B="some_other_string";
C="";
for(j=0;j<B.length;j++){
C+=A.charAt(B.charCodeAt(j)-48);
}document.write(C);
</script>
Simply evaluating the script above would result in the error message:
"V8::JSError: document is not defined"
Thanks a lot for any hints!
Assuming that the variable 'script' contains the javascript code mentioned above:
cxt = V8::Context.new
script.insert(0, "output=''")
document_write_count = script.scan('document.write(').count
remaining = script
document_write_count.times do |i|
document_write_clause_content = remaining.partition('document.write(')[2].partition(')')[0]
document_write_clause = "document.write(#{document_write_clause_content})"
document_write_clause_replacement = "output+=#{document_write_clause_content}"
script.gsub!(document_write_clause, document_write_clause_replacement)
remaining = remaining.rpartition(document_write_clause_replacement)[2]
end
script.insert(-1, "output;")
wanted_output = cxt.eval("#{script}")
Related
I have some arbitrary javascript code that is run via the following method:
download it onto client browser via ajax call
pass resulting code string into javascript eval function.
Note: I cannot download/run the code by adding its file URL to a <script>'s src property because the code must run within a particular scope.
I would like to debug this code in Google Chrome. I am able to do this using plain Javascript, but not when I am using AngularJS. How can I fix this?
Example 1 (plain JS, I am given option to inspect code.js):
var codeToRun = ""
+"var a = 1;\n"
+"var b = 2;\n"
+"This line will fail\n"
+"//# sourceURL=code.js";
eval(codeToRun);
result: http://i.imgur.com/3LL9xi7.png
demo: https://jsfiddle.net/AlexLeung/mpjrwmbm/
Example 2 (AngularJS, I am not given option to inspect code.js):
var codeToRun = ""
+"var a = 1;\n"
+"var b = 2;\n"
+"This line will fail\n"
+"//# sourceURL=code.js";
angular.module("myApp", [])
.controller("myController", function($scope) {
eval(codeToRun);
});
result: http://i.imgur.com/x7lZcUf.png
demo: http://jsfiddle.net/oeoaep0o/
Use a named function rather than an anonymous function. For example:
var codeToRun = "var a = 1; console.log(a) //# sourceURL=foo.js";
function evaler()
{
eval(codeToRun);
}
angular.module("foo", []).run(evaler);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="foo"></div>
References
Named versus Anonymous Functions: Angular Style Guide
sourceURL and displayName in action: Eval and anonymous functions
I am writing a simple counter, and I would like to make installation of this counter very simple for users. One of the simplest counter code (for users who install it) I ever see was Google Analytics Code
So I would like to store main code in a file and user who will install my counter will need just to set websiteID like this:
<html><head><title></title></head><body>
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var websiteId = 'XXXXX';
</script>
</body></html>
Here is my code:
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var page = _gat.init('new');
</script>
and this is my JS file:
(function() {
var z = '_gat';
var aa = function init(data) { alert(data); alert(z);};
function na() {
return new z.aa();
}
na();
})();
I tried to understand Google Analytics javascript code but I failed to do this. Can anyone suggest how can I specify variable between tags and then read it in anonymous function which is located in a javascript file ?
Thanks.
In your example, websiteId is a global variable. So it is accessible everywhere including anonymous functions unless there is a local variable with the same name
<script> var websiteId = "something"; </script>
Later in the page or included js file...
(function() {
alert(websiteId); //this should work
})();
Can anyone suggest how can I specify variable between tags and then read it [...]
Not if your tag has both a SRC attribute and JS content.
<script type="text/javascript" src="http:/x.com/x.js"></script>
.. is different from,
<script type="text/javascript">
var x = 1;
</script>
One framework that optionally adds JS variables to SCRIPT tags is Dojo. So if you're using Dojo you can add variables to the global djConfig hash by writing,
<script type="text/javascript" src="mxclientsystem/dojo/dojo.js"
djConfig="
usePlainJson: true,
parseOnLoad: true
">
</script>
Dojo does this by running through the SCRIPT tags and evaluating the custom djConfig attribute.
This does not, however solve your problem.
You do really want two SCRIPT tags. One saying,
<script type="text/javascript">
var websiteId = '123456';
</script>
which will set a global variable websiteId and a second one,
<script type="text/javascript" src="http:/x.com/myreporter.js"></script>
which can load from anywhere and read out the websiteId variable and, I assume, report it back.
You can pass variables to an anonymous function like so:
(function(arg1, arg2, arg3) {
alert(arg1);
alert(arg2);
alert(arg3);
})("let's", "go", "redsox");
// will alert "let's", then "go", then "redsox" :)
I'm not entirely clear about what you're asking, but...
You can tag any HTML element with an id attribute, then use
document.getEntityById() to retrieve that specific element.
You can also give any HTML element user-defined attributes having names of your own choosing, then get and set them for that element within Javascript.
I think you've got a bit confused with how JS objects are called.
z is a String, '_gat'. You can't call aa() on it because a String has no member called aa. aa is a standalone function stored in a local variable. Even if you did call aa(), it doesn't return anything, so using the new operator on its results is meaningless. new can only be called on constructor-functions.
I guess you mean something like:
var _gat= function() {
// Private variable
//
var data= null;
// Object to put in window._gat
//
return {
// Set the private variable
//
init: function(d) {
data= d;
}
};
}();
Then calling _gat.init('foo') as in your second example would set the variable to website ID 'foo'. This works because the _gat object is the return {init: function() {...}} object defined inside the anonymous function, keeping a reference (a ‘closure’) on the hidden data variable.
If you specify a src attribute as part of a script element, any code within the script element tags themselves will not be executed. However, you can add this functionality with the following code. I got this technique from Crockford (I believe it was him), where he uses it in of his talks on the unrelated topic of rendering performance and asynchronously loading scripts into a page to that end.
JavaScript:
(function() {
// Using inner class example from bobince's answer
var _gat = (function() {
var data= null;
return {
init: function(d) {
console.info("Configuration data: ", d);
data = d;
}
}
})();
// Method 1: Extract configuration by ID (SEE FOOT NOTE)
var config = document.getElementById("my-counter-apps-unique-and-long-to-avoid-collision-id").innerHTML;
// Method 2: search all script tags for the script with the expected name
var scripts = document.getElementsByTagName("script");
for ( var i=0, l=scripts.length; i<l; ++i ) {
if ( scripts[i].src = "some-script.js" ) {
config = scripts[i].innerHTML;
break;
}
}
_gat.init( eval("(" +config+ ")") );
})();
HTML:
<script type="text/javascript" src="some-script.js" id="my-counter-apps-unique-and-long-to-avoid-collision-id">
{some: "foo", config: "bar", settings: 123}
</script>
Both methods have their draw backs:
Using a unique and non-colliding ID will make determining the proper script element more precise and faster; however, this is not valid HTML4/XHTML markup. In HTML5, you can define arbitrary attributes, so it wont be an issue at that time
This method is valid HTML markup; however, the simple comparison that I have shown can be easily broken if your url is subject to change (e.g.: http vs https) and a more robust comparison method may be in order
A note on eval
Both methods make use of eval. The typical mantra concerning this feature is that "eval is evil." However, that goes with say that using eval without knowing the dangers of eval is evil.
In this case, AFAIK, the data contained within the script tags is not subject to inject attack since the eval'ing script (the code shown) is executed as soon as that element is reached when parsing the HTML into the DOM. Scripts that may have been defined previously are unable to access the data contained within the counter's script tags as that node does not exist in the DOM tree at the point when they are executed.
It may be the case that a well timed setTimeout executed from a previously included script may be able to run at the time between the counter's script's inclusion and the time of the eval; however, this may or may not be the case, and if possible, may not be so consistently depending on CPU load, etc.
Moral of the story, if you're worried about it, include a non-eval'ing JSON parser and use that instead.
Pretty straightforward, I've got a JS script that's included on many different sites and needs to have parameters passed to it.
It would be useful if those could be passed via the URL, eg:
<script type="text/javascript"
src="http://path.to/script.js?var1=something&var2=somethingelse"></script>
Yes, you can still pre-populate variables, in a separate script tag, but it's a smidge messy and less easy to pass around:
<script type="text/javascript">var1=something; var2=somethingelse</script>
<script type="text/javascript" src="http://path.to/script.js"></script>
Yep. Added bonus: I convert the query string parameters into a more usable javascript hash.
HTML:
<script src="script.js?var1=something&var2=somethingelse" type="text/javascript"></script>
script.js:
var scriptSource = (function() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1].src
}());
var params = parseQueryString(scriptSource.split('?')[1]);
params.var1; // 'something'
params.var2; // 'somethingelse'
// Utility function to convert "a=b&c=d" into { a:'b', c:'d' }
function parseQueryString(queryString) {
var params = {};
if (queryString) {
var keyValues = queryString.split('&');
for (var i=0; i < keyValues.length; i++) {
var pair = keyValues[i].split('=');
params[pair[0]] = pair[1];
}
}
return params;
}
This method should only be used if the varaibles would determine what JavaScript code was loaded (e.g., with the request being processed by PHP, dynamically building the JS file).
If you want to pass information to JavaScript code, use functions or variables in the code after it is loaded.
error.fileName will tell you the file that a script is from (not sure if it works in every browser; I've tested it in Firefox & Opera)
var filename = (new Error).fileName;
In order to do something like that you'd need to use a server side language to render the JS for you.
I wouldn't recommend it.
I am trying to build some sort of logger functionality in javascript. Is there any API for a script to get its own filename?
This should work:
(new Error).fileName
Or you can try this:
var filepath;
(function(){
var scripts = document.getElementsByTagName('script');
filepath = scripts[ scripts.length-1 ].src;
}());
The second option gives you the path of your script file.
I see two ways:
put into every JS file a variable var filename = 'script.js';
get the filename using <script> tag name
JS can not get filename like bash/perl/c scripts.
If we can get the current script's tag, then we can read its src attribute. Excerpt from https://stackoverflow.com/a/22745553/4396817 below:
document.currentScript will return the element whose script is currently being processed.
<script>
var me = document.currentScript;
</script>
Benefits
Simple and explicit. Reliable.
Don't need to modify the script tag
Works with asynchronous scripts (defer & async)
Works with scripts inserted dynamically
Problems
Will not work in older browsers and IE.
...So from there, we can simply read the src attribute!
<script src="http://website.com/js/script.js">
alert(document.currentScript.src);
</script>
// Alerts "http://website.com/js/script.js"
Unfortunately this is not possible.
If you change your approach, getting function names may help you which is sometimes possible. Your best chance would be extracting function name from "arguments.callee". This only works if function is defined like
function FN() { ... }
And does not work when
var FN = function() { ... }
this is my modification that fixes a few possible issues, but adds a requirement.
It needs you to name the file in a certain way, so for example if you have a .js file, but you want to know which version is loaded (for example to tell a php server). so your js file would be "zoom_v34.js".
var version;
(function(){
var scripts = document.getElementsByTagName('script');
for (var i=0; i<scripts.length; i++) {
var start = scripts[i].src.indexOf('zoom_');
if (start != -1) { var end = scripts[i].src.indexOf('.',start); version = scripts[i].src.substr(start+6,end-start-6); break; }
}
}());
post='login{JS:'+version+'}';
You can try putting this at the top of your JavaScript file:
window.myJSFilename = "";
window.onerror = function(message, url, line) {
if (window.myJSFilename != "") return;
window.myJSFilename = url;
}
throw 1;
Make sure you have only functions below this. The myJSFilename global variable will contain the full path of the JavaScript file, and the filename can be parsed from that. Tested in IE11, but it should work elsewhere.
In my HTML file I have linked to the JS with:
src="myscript.js?config=true"
Can my JS directly read the value of this var like this?
alert (config);
This does not work, and the FireFox Error Console says "config is not defined". How do I read the vars passed via the src attribute in the JS file? Is it this simple?
<script>
var config=true;
</script>
<script src="myscript.js"></script>
You can't pass variables to JS the way you tried. SCRIPT tag does not create a Window object (which has a query string), and it is not server side code.
Yes, you can, but you need to know the exact script file name in the script :
var libFileName = 'myscript.js',
scripts = document.head.getElementsByTagName("script"),
i, j, src, parts, basePath, options = {};
for (i = 0; i < scripts.length; i++) {
src = scripts[i].src;
if (src.indexOf(libFileName) != -1) {
parts = src.split('?');
basePath = parts[0].replace(libFileName, '');
if (parts[1]) {
var opt = parts[1].split('&');
for (j = opt.length-1; j >= 0; --j) {
var pair = opt[j].split('=');
options[pair[0]] = pair[1];
}
}
break;
}
}
You have now an 'options' variable which has the arguments passed. I didn't test it, I changed it a little from http://code.google.com/p/canvas-text/source/browse/trunk/canvas.text.js where it works.
You might have seen this done, but really the JS file is being preprocessed server side using PHP or some other language first. The server side code will print/echo the javascript with the variables set. I've seen a scripted ad service do this before, and it made me look into seeing if it can be done with plain ol' js, but it can't.
You need to use Javascript to find the src attribute of the script and parse the variables after the '?'. Using the Prototype.js framework, it looks something like this:
var js = /myscript\.js(\?.*)?$/; // regex to match .js
var jsfile = $$('head script[src]').findAll(function(s) {
return s.src.match(js);
}).each(function(s) {
var path = s.src.replace(js, ''),
includes = s.src.match(/\?.*([a-z,]*)/);
config = (includes ? includes[1].split('=');
alert(config[1]); // should alert "true" ??
});
My Javascript/RegEx skills are rusty, but that's the general idea. Ripped straight from the scriptaculous.js file!
Your script can however locate its own script node and examine the src attribute and extract whatever information you like.
var scripts = document.getElementsByTagName ('script');
for (var s, i = scripts.length; i && (s = scripts[--i]);) {
if ((s = s.getAttribute ('src')) && (s = s.match (/^(.*)myscript.js(\?\s*(.+))?\s*/))) {
alert ("Parameter string : '" + s[3] + "'");
break;
}
}
Whether or not this SHOULD be done, is a fair question, but if you want to do it, http://feather.elektrum.org/book/src.html really shows how. Assuming your browser blocks when rendering script tags (currently true, but may not be future proof), the script in question is always the last script on the page up to that point.
Then using some framework and plugin like jQuery and http://plugins.jquery.com/project/parseQuery this becomes pretty trivial. Surprised there's not a plugin for it yet.
Somewhat related is John Resig's degrading script tags, but that runs code AFTER the external script, not as part of the initialization: http://ejohn.org/blog/degrading-script-tags/
Credits: Passing parameters to JavaScript files , Passing parameters to JavaScript files
Using global variables is not a so clean or safe solution, instead you can use the data-X attributes, it is cleaner and safer:
<script type="text/javascript" data-parameter_1="value_1" ... src="/js/myfile.js"></script>
From myfile.js you can access the data parameters, for instance with jQuery:
var parameter1 = $('script[src*="myfile.js"]').data('parameter_1');
Obviously "myfile.is" and "parameter_1" have to match in the 2 sources ;)
You can do that with a single line code:
new URL($('script').filter((a, b, c) => b.src.includes('myScript.js'))[0].src).searchParams.get("config")
It's simpler if you pass arguments without names, just like function calls.
In HTML:
<script src="abc.js" data-args="a,b"></script>
Then, in JavaScript:
const args=document.currentScript.dataset.args.split(',');
Now args contains the array ['a','b'].