Does JavaScript bookmark(javascript:) work with YouTube? - javascript

I want to write a js code which will un-translate video names
It is copied from title to video block name, but without 10 last chars(without "- YouTube").
And when I try this code (I know that code must be inline but it's for greater review)
javascript:var tttl = document.createElement('script');
document.getElementsByTagName("body")[0].appendChild(tttl);
tttl.innerText = "document.getElementsByClassName(\"style-scope ytd-video-primary-info-renderer\")[5].innerText = document.title.slice(0,-10);";
In browser console it works, but with bookmark (add bookmark and in address paste my code) it isn't working correctly.

Based on your question, it seems like you want to execute a javascript code while browsing youtube.com.
For doing so you need to wrap your code inside a self-executing function.
javascript:(function(){var tttl = document.createElement('script');
document.getElementsByTagName("body")[0].appendChild(tttl);
tttl.innerText = "document.getElementsByClassName(\"style-scope ytd-video-primary-info-renderer\")[5].innerText = document.title.slice(0,-10);";})();
Edit 1:
The reason why this code is wrapped inside a self-executing function is that it defines the scope for the executing code and doesn't try to write on DOM with the return value of the last statement.
Example code for better understanding:
javascript:var name;name="Ashish";
As the statement name="Ashish" returns a value in javascript this will be printed on the browser if executed through address bar.
Same is the case with the last statement in your code.

Related

Run script after appending it to the HTML

I have a string with HTML:
var str = '<div><p>Examplee</p></div><script>alert("testing!")</script>';
and then I append it to the HTML:
document.body.innerHTML += str;
and the content is appended but the script does not execute, is there a way to force it?
First, a caveat: Naturally, only do this with scripts you trust. :-)
There are a couple of ways. Basically, you need to:
Get the text of the script, and
Run it
Getting the text
One option is just to go ahead and add it, then find it and grab its text:
document.body.innerHTML += str;
var scripts = document.querySelectorAll("script");
var text = scripts[scripts.length - 1].textContent;
On obsolete browsers, you may need to feature-detect textContent vs. innerText.
You might want to give it an identifying characteristic (id, class, etc.) so you don't have to find it by position like that.
Alternately, you could do what the PrototypeJS lib does and try go get it from the string with regex. You can find their source code for doing that here (look for extractScripts).
Running it
Once you have the text of the script, you have several options:
Use indirect eval (aka "global eval") on it: (0, eval)(text). This is not the same as eval(text); it runs the code in global scope, not local scope.
Create a new script element, set its text to the text, and add it
var newScript = document.createElement("script");
newScript.textContent = text;
document.body.appendChild(newScript);
If doing that, might make sense to remove the original, though it doesn't really matter.
Example that grabs the text of the script element after adding it and uses indirect eval:
var str = '<div><p>Examplee</p></div><script>alert("testing!")<\/script>';
document.body.innerHTML += str;
var scripts = document.querySelectorAll("script");
(0, eval)(scripts[scripts.length - 1].textContent);
Presumably you don't really use += on document.body.innerHTML, which builds an HTML string for the whole page, appends to it, tears the whole page down, and then parses the new HTML to build a new one. I assume that was just an example in your question.
jQuery provides the $.getScript(url [,success]) function. You can then load and execute your code from a separate jquery file which helps to manage and control the flow of execution.
basically put your alert("testing!") script inside a separate file, for instance alert.js in the same directory.
Then you can run the script when adding your employee to the HTML.
var str = '<div><p>Examplee</p></div>';
var url = 'alert.js';
document.body.innerHTML += str;
$.getScript(url);
I know this may seem like more work, but it is better practice to keep your javascript out of your HTML. You can also use a callback to gather user data after the alert or notify another process that the user has been alerted.
$.getScript(url, function(){
//do something after the alert is executed.
});
For instance maybe it would be a better design to add the employee after the alert is executed.
var str = '<div><p>Examplee</p></div>';
var url = 'alert.js';
$.getScript(url, function(){
document.body.innerHTML += str;
});
Edit: I know jQuery is not tagged, but I am also no petitioning to be the accepted answer to this question. I am only offering another alternative for someone who may run into the same issue and may be using jQuery. If that is the case $.getScript is a very useful tool designed for this exact problem.
You should change the HTML after it was loaded.
Try this:
document.addEventListener("DOMContentLoaded", function(event) {
document.body.innerHTML += str;
});

Javascript calling Function() to create a function

Can anyone explain the following code?
Function(
Function(
'return \'\\141\\75\\160\\162\\157\\155\\160\\164\\50\\47\\105\\156\\164\\162\\145\\172\\40\\154\\145\\40\\155\\157\\164\\40\\144\\145\\40\\160\\141\\163\\163\\145\\47\\51\\73\\151\\146\\50\\141\\75\\75\\47\\164\\157\\164\\157\\61\\62\\63\\154\\157\\154\\47\\51\\173\\141\\154\\145\\162\\164\\50\\47\\142\\162\\141\\166\\157\\47\\51\\73\\175\\145\\154\\163\\145\\173\\141\\154\\145\\162\\164\\50\\47\\146\\141\\151\\154\\56\\56\\56\\47\\51\\73\\175\''
) ()
)()
Interesting here: an actual function is getting created using the Function().
But since I cannot view the native code, I am having difficulty understanding the actual function that is getting created. This is taken from root-me.org Javascript - native code challenge.
It deobfuscates to:
a = prompt('Entrez le mot de passe');
if(a=='toto123lol'){
alert('bravo');
} else{
alert('fail...');
}
To verify, in Chrome, open Developer Tools, open the console, and paste in:
Function(
'return \'\\141\\75\\160\\162\\157\\155\\160\\164\\50\\47\\105\\156\\164\\162\\145\\172\\40\\154\\145\\40\\155\\157\\164\\40\\144\\145\\40\\160\\141\\163\\163\\145\\47\\51\\73\\151\\146\\50\\141\\75\\75\\47\\164\\157\\164\\157\\61\\62\\63\\154\\157\\154\\47\\51\\173\\141\\154\\145\\162\\164\\50\\47\\142\\162\\141\\166\\157\\47\\51\\73\\175\\145\\154\\163\\145\\173\\141\\154\\145\\162\\164\\50\\47\\146\\141\\151\\154\\56\\56\\56\\47\\51\\73\\175\''
) ()
This is the steps of "how the encoding works", essentially. To "encode" the letter "a":
test = "a";
console.log(test.charCodeAt(0)); //97
console.log(parseInt('141', 8)); //97
console.log('\141'); //a
But since I cannot view the native code, I am having difficulty understanding the actual function that is getting created.
You have native code inside the script tag. It just looks unusual as it is referencing the ASCII key codes; octagonal to be exact (OCT). Here is a link
ASCII Key Codes
First we have an executable tag that starts things off. Here is a link explaining what it does.
HTML tags
Inside the tag we have two functions or function constructors.
If your were to type Function() into your console you would get
function anonymous() {}
For more information check out this link.
Funciton JS link
Let's start with the nested function first.
Function(
'return \'\\141\\75\\160\\162\\157\\155\\160\\164\\50\\47\\105\\156\\164\\162\\145\\172\\40\\154\\145\\40\\155\\157\\164\\40\\144\\145\\40\\160\\141\\163\\163\\145\\47\\51\\73\\151\\146\\50\\141\\75\\75\\47\\164\\157\\164\\157\\61\\62\\63\\154\\157\\154\\47\\51\\173\\141\\154\\145\\162\\164\\50\\47\\142\\162\\141\\166\\157\\47\\51\\73\\175\\145\\154\\163\\145\\173\\141\\154\\145\\162\\164\\50\\47\\146\\141\\151\\154\\56\\56\\56\\47\\51\\73\\175\'')()
By using the JS Function constructor, we can pass in arguments to our new function as well as the function body.
new Function ([arg1[, arg2[, ...argN]],] functionBody)
In the nested function we just create an anonumous funciton and pass it a function body in the form of a string like this
'return \'\\141\\75\\160\\162\\157\\155\\160\\164\\50\\47\\105\\156\\164\\162\\145\\172\\40\\154\\145\\40\\155\\157\\164\\40\\144\\145\\40\\160\\141\\163\\163\\145\\47\\51\\73\\151\\146\\50\\141\\75\\75\\47\\164\\157\\164\\157\\61\\62\\63\\154\\157\\154\\47\\51\\173\\141\\154\\145\\162\\164\\50\\47\\142\\162\\141\\166\\157\\47\\51\\73\\175\\145\\154\\163\\145\\173\\141\\154\\145\\162\\164\\50\\47\\146\\141\\151\\154\\56\\56\\56\\47\\51\\73\\175\''
When the function runs the first string '' (quotes) are removed and this statement is run
return \'\\141\\75...'
The return, of course executes and exits the function, and when THIS function is run we get another function body in the form of another string value.
"a=prompt('Entrez le mot de passe');if(a=='toto123lol'){alert('bravo');}else{alert('fail...');}"
The leading '\', which is after the return statement, but before the actual string is only to escape the following quote, so the compiler does not mistake it for the second closing quote of the quote just before the return statement. We could get rid of it, as well as the second one just after the last number, and instead write the function body like this
Function(
'return "\\141\\75\\160\\162\\157\\155\\160\\164\\50\\47\\105\\156\\164\\162\\145\\172\\40\\154\\145\\40\\155\\157\\164\\40\\144\\145\\40\\160\\141\\163\\163\\145\\47\\51\\73\\151\\146\\50\\141\\75\\75\\47\\164\\157\\164\\157\\61\\62\\63\\154\\157\\154\\47\\51\\173\\141\\154\\145\\162\\164\\50\\47\\142\\162\\141\\166\\157\\47\\51\\73\\175\\145\\154\\163\\145\\173\\141\\154\\145\\162\\164\\50\\47\\146\\141\\151\\154\\56\\56\\56\\47\\51\\73\\175"')()
If you ran this code in your console you would get the same result, try it!
If you do you will find that all these numbers have compiled to actual letters and numbers, in fact it compiled to ASCII character codes. This happened because of the use of '\' which proceeds each number. For less confusion, let's turn this "\\" instead into this "\"
Function(
'return "\141\75\160\162\157\155\160\164\50\47\105\156\164\162\145\172\40\154\145\40\155\157\164\40\144\145\40\160\141\163\163\145\47\51\73\151\146\50\141\75\75\47\164\157\164\157\61\62\63\154\157\154\47\51\173\141\154\145\162\164\50\47\142\162\141\166\157\47\51\73\175\145\154\163\145\173\141\154\145\162\164\50\47\146\141\151\154\56\56\56\47\51\73\175"')()
As you will see, this will still run and we get
"a=prompt('Entrez le mot de passe');if(a=='toto123lol'){alert('bravo');}else{alert('fail...');}"
So the nested function returns a function body as a string, which then gets executed in the outer Function constructer in the same way that the nested function fired. Here is the same example with a few things removed for better clarity
Function(
Function('return "\141\75\160\162\157\155\160\164\50\47\105\156\164\162\145\172\40\154\145\40\155\157\164\40\144\145\40\160\141\163\163\145\47\51\73\151\146\50\141\75\75\47\164\157\164\157\61\62\63\154\157\154\47\51\173\141\154\145\162\164\50\47\142\162\141\166\157\47\51\73\175\145\154\163\145\173\141\154\145\162\164\50\47\146\141\151\154\56\56\56\47\51\73\175"')())()
Note: you may need to open a new window and then paste this in the console and click enter.
And for even more clarity, we could just copy and paste the initial returned value into the outer function like this
Function("a=prompt('Entrez le mot de passe');if(a=='toto123lol'){alert('bravo');}else{alert('fail...');}")()
This will also work.
What the nested function does
The first part opens a browser prompt window and attaches its future value to variable 'a'. Try this
Function("a=prompt('Enter Password');console.log(a);")()
when you press enter your value will show in the console. The second part of the function analizes this returned value by comparing it to a string 'toto123lol'.
when the entered value is exactly 'toto123lol' a new alert window will appear displaying 'bravo'.
If the entered value is not exactly 'toto123lol' a new alert window will appear displaying 'fail...'
As you can see, the initial function of your question contains all the needed information to not only run working code, but also all the native code you need to figure out what it is doing.
After checkout out the website you mentioned
Root-me.org
Perhaps what the test is trying to show is that what may look like harmless code, can actually be anything with could be executable within an HTML tag. Or perhaps that there are many ways in which to influence behavior?
I hope this answers your question.
UPDATE: If you are wondering what the difference is between '\\' or '\' I have asked it here - why double or single escapes

Use JavaScript to update the contents of a script tag dynamically

This click event works:
document.getElementById('control').addEventListener('click', function(){
alert('test');
});
Why can't I make it the content of a script tag that already exists on the page?
var s = document.getElementsByTagName("script")[0];
s.innerHTML = "document.getElementById('control').addEventListener('click', function(){alert('test');});";
I've tried enclosing the attempt to do something in an IIFE:
s.innerHTML = "(function(){alert('test')}());";
so that it will call itself.
Also:
s.innerHTML = "var f = function(){alert('f')}; f();"
I'm only interested theoretically and in no way am saying this is a good or bad idea, but how could I make it work?
Script tags are evaluated once and code is not replaced if you change their contents (they work differently than other types of tags in this regard). If you want to add new code to the page, you can simply add new script tags to the page that contains the new code. If you want to redefine existing publicly accessible functions, you can simply redefine them with new code.
So, if you want to replace a previous definition of function f(), you can simply assign new code to that symbol:
f = function() {alert("new f");}
You can insert new scripts from a remote source at any time with a code snippet like this:
var s = document.createElement("script");
s.type = "text/javascript";
s.src = url;
document.getElementsByTagName("head")[0].appendChild(s);
It is also possible to use eval() to pass text to the javascript engine that you want it to parse and then run, but there are rarely any good reasons to do it this way as there are generally better ways to solve a problem like this.
In addition to what jfriend00 said. If you want to dynamically execute scripts. Use should probably use JavaScript's eval function.
Note: But use it carefully!!!
you can't do this with an existing script tag, you have to create a new script tag and push your script before adding it to the DOM:
var sc =document.createElement("script");
sc.type="text/javascript";
var scriptText=document.createTextNode("document.getElementById('control').addEventListener('click', function(){"+
"alert('test');" +
"});");
sc.appendChild(scriptText);
document.querySelector("head").appendChild(sc);
and it will do what you want.
The important point here, once the first script in a script tag is executed, you no longer can inject any script in it, actually you can, but it won't work.

JS bookmarklet issue: Code works in console, fails in bookmarklet

Using a JS bookmarklet to set a custom field in the Wordpress Edit Post screen. The following code works well when I copy/paste it into the console (latest Chrome stable):
document.getElementById('metakeyselect').value = "foo";
document.getElementById('metavalue').value = "bar";
document.getElementById('meta-add-submit').click();
Works without a hitch; I jut paste this into the console, and a new custom var is added to the post (I have "foo" as a var name in WP already).
In bookmarklet form, this same code looks like this:
javascript:document.getElementById('metakeyselect').value = "foo";document.getElementById('metavalue').value = "bar";document.getElementById('meta-add-submit').click();
And it fails: When I click it, the Name and Value boxes are filled in, but nothing gets submitted. The console shows the following error:
Uncaught TypeError: Cannot call method 'click' of null
Any idea why? Same exact code, same browser, same page.
I had a similar problem and am quite sure that's what causes your code to break too.
My minimal example would be the following code on this website (should work afor the entire stackoverflow.com domain):
document.getElementsByName("q")[0].value="foo";
This should write "foo" to the search field (that has no id but is the only element with the name "q"). Both web console and bookmarklet will set the value as expected but the bookmarklet will also change the page to an empty header and a body containing only the word "foo" after a short delay. Assuming that this is not a random bug that only applies to me, the reason for the thrown exception in your example is that the bookmarklet sets the value "foo", then "bar" but changes the content of the web page to "foo", then "bar" before your last line terminates.
Unfortunately I don't know the reason for this behaviour (I found this question looking for that exact information) but that is what most likely causes the TypeError in your excample.
It is possible that the same code runs without any problems when used in a Greasemonkey script (e.g. as the onclick script of a button you added using Greasemonkey).
[Edit:] Apparently, if a bookmarklet evaluates to anything other than undefined, the inner html of the website is replaced by a string representation of that value. To make sure that the bookmarklet evaluates to undefined, you can just type undefined as the last line outside of any condition block. unfortunately that means it is less likely that my assumption toward OP's error is correct but at least future visitors still might find this information usefull.
It looks like the code you use in console works ok.
It seems like the method you turn console code into a bookmarklet is what might result into an error.
The basic IIFE-construction, i.e. Immediately Invoked Function Expression, looks like this:
javascript:(function () {
/*code here*/
})();
Therefore, the code that is supposed to work might be this.
javascript:(function () {
document.getElementById('metakeyselect').value = "foo";
document.getElementById('metavalue').value = "bar";
document.getElementById('meta-add-submit').click();
})();
Does it solve your problem?

Convert a command from Javascript to Greasmonkey

I am trying to add a character counter to a page, on this page i enter in three values and it returns a large string in the innerHTML of a div with the ID of 'AnswerBoxID', now i want my script to obviously count the number of characters in it to do this i have written
var submit=document.getElementsByClassName('Row3');
function countChars(){
count = document.getElementById('AnswerBoxID').innerHTML.length;
document.title ="Chars "+count+"/160";
}
Which returns a ROUGH approximate of the chars, when i then paste it into an editor or something else that counts chars i get a different result again, counting with this method gets within 5 chars of what other things are reporting (specifically notepad++).
BUT my biggest problem is I have been unable to get countChars() to update
when the value of document.getElementById('AnswerBoxID').innerHTML updates, in javascript I overcame that using the following code
var submit=document.getElementsByClassName('Row3');
for (i=0; i<submit.length; i++){
submit[i].firstChild.setAttribute('onclick','countChars()');
}
After reading GM Pitfalls 2 i then modified my approach to the following
for (i=0; i<submit.length; i++){
submit[i].firstChild.addEventListener('click',countChars(),true);
}
But it still doesnt work!
And before anyone asks yes I do define the count variable before the function. I don't really mind the mostly accurate length thing I would prefer it to be more precise but I do really want to add onclick elements that run countChars() to the submit buttons.
You seem to add the event handler wrong.
elm.setAttribute('onclick','countChars()');
would set an attribute, and eval 'countChars()' in the global scope when the element is clicked. Yet, Greasemonkey scripts run sandboxed to their own global object, and your declared function "countChars" is not available to the eval.
elm.addEventListener('click',countChars(),true);
executes the function immediately and adds the return value as a handler. To make it work, just remove the brackets and pass the function itself. See also element.addEventListener
Greasemonkey scripts run sandboxed. That means, the script runs in the page's onload event then is removed. Calling your Greasemonkey script's countChars() function when the form is submitted will return an undefined error, as your function object is no longer present in the DOM.
Here's my simplified contentEval function (based on GM wiki's Content Script Injection's function):
function contentEval(source) {
var script = document.createElement('script');
script.setAttribute("type", "text/javascript");
script.textContent = source;
document.head.appendChild(script);
}
You can also set the text property instead of textContent for compatibility with older IE versions, thanks RogG!
Put that function in your GM script, it will serve to append any function you need to user later to the DOM's head.
Pass your countChars function as a parameter to the contentEval function:
contentEval(function countChars() {
var count = document.getElementById('AnswerBoxID').textContent.length;
document.title ='Chars '+count+'/160';
});
This way, your countChars function will be placed inside a script element appended to the document's head, which will be accessible after the GM script's execution time ends.
If you want to check a demo of the code above, use a DOM Inspector in this fiddle. It creates a new script element which is appended to the (in JSfiddle's case, the iframe's) document's head, meaning it will be accessible whenever you need it.
You could also simply attach the function to the unsafeWindow, but you should avoid using unsafeWindow unless strictly necessary, and note that unsafeWindow is not supported in Google Chrome.
Use this page for reference: http://wiki.greasespot.net/Content_Script_Injection
Also use the .textContent method to get the text content of an element (and its descendants) as noted by RobG.
In GreaseMonkey, you should be able to use:
var count = document.getElementById('AnswerBoxID').textContent.length;

Categories

Resources