How to encode a JSON string that includes HTML characters? - javascript

I have an ASP.NET MVC view model that I need to pass to a JavaScript function when the page loads, and I'm currently doing that like so:
<script type="text/javascript">
$(window).on("load", function () {
myFunction(#Html.Raw(JsonConvert.SerializeObject(Model)));
});
</script>
In other words, I'm using JSON.NET to serialize the model to JSON, and inserting that (un-encoded) into my <script> block.
When rendered, the script block ends up looking something like:
<script type="text/javascript">
$(window).on("load", function () {
myFunction({"myProperty": "the property value"});
});
</script>
That works, up to a point. But when my model contains a string property that whose text includes HTML tags *, these confuse the browser into thinking that the <script> block has ended, and the browser starts rendering the tags embedded in the view model.
Edit: per the comments, this only happens if there's a </script> tag.
For example:
<script type="text/javascript">
$(window).on("load", function () {
myFunction({"myProperty": "<script>...</script>"});
});
</script>
How can I solve this?

This is a know problem. PHP's json_encode function encodes / as \/ to avoid exactly this problem.
A straightforward solution is to write your own JSON encode wrapper-function that replaces </script> by <\/script> after JSON encoding.
Maybe there is a better solution though, I'm not familiar with asp.net.

Related

convert blocks from text/plain to text/javascript and execute it

my problem is as follow.
I have a multiple script blocks that contain javascript code type of text/plain.
So my question is is there any script that i can dynamically convert them from text/plain to text/javascript type and execute its content ? The problem i'm having is the place of execution, because scripts contain document.write so the output is appended in the end of the html not on the location of the script itself.
eg: let's say something like this
<script type="text/plain">alert('hello world');</script>
<script type="text/javascript">
$(document).ready(function(){
$("script[type*=plain]").each(function() {
$(this).attr('type','text/javascript');
});
});
</script>
thnx
Modifying a script element in place doesn't cause it to be executed. You need to create a new script element with the appropriate content. Another option is to eval() the contents of the old element:
$(document).ready(function(){
$("script[type*=plain]").each(function() {
eval($(this).text());
});
});
You can go with some nasty approach, of selecting the tag with type=plain and then accessing its innerText attribute, assigning it to eval method.
var el = $("script[type*=plain]")[0];
eval(el.innerText);
But I'd try to avoid using eval
If there's a different approach I'm not aware of it, and for this I am sorry.
You can not make document.write to place it's output in place of <script> element if you change script's type, because entire page's javascript code woudn't re-run every time you add new <script> or evaluating existing one.
If you want to replace <script type="text/plain"> with the result of script execution, you can return some string from that script instead of using document.write and then insert this string in place of <script type="text/plain"> element:
<script type="text/plain">
var result = "line1<br>";
result += "line2";
alert('hello world');
result
</script>
<script type="text/javascript">
$(document).ready(function(){
$("script[type*=plain]").each(function() {
$(this).replaceWith(eval($(this).text()));
});
});
</script>

How do I get the original innerHTML source without the Javascript generated contents?

Is it possible to get in some way the original HTML source without the changes made by the processed Javascript? For example, if I do:
<div id="test">
<script type="text/javascript">document.write("hello");</script>
</div>
If I do:
alert(document.getElementById('test').innerHTML);
it shows:
<script type="text/javascript">document.write("hello");</script>hello
In simple terms, I would like the alert to show only:
<script type="text/javascript">document.write("hello");</script>
without the final hello (the result of the processed script).
I don't think there's a simple solution to just "grab original source" as it'll have to be something that's supplied by the browser. But, if you are only interested in doing this for a section of the page, then I have a workaround for you.
You can wrap the section of interest inside a "frozen" script:
<script id="frozen" type="text/x-frozen-html">
The type attribute I just made up, but it will force the browser to ignore everything inside it. You then add another script tag (proper javascript this time) immediately after this one - the "thawing" script. This thawing script will get the frozen script by ID, grab the text inside it, and do a document.write to add the actual contents to the page. Whenever you need the original source, it's still captured as text inside the frozen script.
And there you have it. The downside is that I wouldn't use this for the whole page... (SEO, syntax highlighting, performance...) but it's quite acceptable if you have a special requirement on part of a page.
Edit: Here is some sample code. Also, as #FlashXSFX correctly pointed out, any script tags within the frozen script will need to be escaped. So in this simple example, I'll make up a <x-script> tag for this purpose.
<script id="frozen" type="text/x-frozen-html">
<div id="test">
<x-script type="text/javascript">document.write("hello");</x-script>
</div>
</script>
<script type="text/javascript">
// Grab contents of frozen script and replace `x-script` with `script`
function getSource() {
return document.getElementById("frozen")
.innerHTML.replace(/x-script/gi, "script");
}
// Write it to the document so it actually executes
document.write(getSource());
</script>
Now whenever you need the source:
alert(getSource());
See the demo: http://jsbin.com/uyica3/edit
A simple way is to fetch it form the server again. It will be in the cache most probably. Here is my solution using jQuery.get(). It takes the original uri of the page and loads the data with an ajax call:
$.get(document.location.href, function(data,status,jq) {console.log(data);})
This will print the original code without any javascript. It does not do any error handling!
If don't want to use jQuery to fetch the source, consult the answer to this question: How to make an ajax call without jquery?
Could you send an Ajax request to the same page you're currently on and use the result as your original HTML? This is foolproof given the right conditions, since you are literally getting the original HTML document. However, this won't work if the page changes on every request (with dynamic content), or if, for whatever reason, you cannot make a request to that specific page.
Brute force approach
var orig = document.getElementById("test").innerHTML;
alert(orig.replace(/<\/script>[.\n\r]*.*/i,"</script>"));
EDIT:
This could be better
var orig = document.getElementById("test").innerHTML + "<<>>";
alert(orig.replace( /<\/script>[^(<<>>)]+<<>>/i, "<\/script>"));
If you override document.write to add some identifiers at the beginning and end of everything written to the document by the script, you will be able to remove those writes with a regular expression.
Here's what I came up with:
<script type="text/javascript" language="javascript">
var docWrite = document.write;
document.write = myDocWrite;
function myDocWrite(wrt) {
docWrite.apply(document, ['<!--docwrite-->' + wrt + '<!--/docwrite-->']);
}
</script>
Added your example somewhere in the page after the initial script:
<div id="test">
<script type="text/javascript"> document.write("hello");</script>
</div>
Then I used this to alert what was inside:
var regEx = /<!--docwrite-->(.*?)<!--\/docwrite-->/gm;
alert(document.getElementById('test').innerHTML.replace(regEx, ''));
If you want the pristine document, you'll need to fetch it again. There's no way around that. If it weren't for the document.write() (or similar code that would run during the load process) you could load the original document's innerHTML into memory on load/domready, before you modify it.
I can't think of a solution that would work the way you're asking. The only code that Javascript has access to is via the DOM, which only contains the result after the page has been processed.
The closest I can think of to achieve what you want is to use Ajax to download a fresh copy of the raw HTML for your page into a Javascript string, at which point since it's a string you can do whatever you like with it, including displaying it in an alert box.
A tricky way is using <style> tag for template. So that you do not need rename x-script any more.
console.log(document.getElementById('test').innerHTML);
<style id="test" type="text/html+template">
<script type="text/javascript">document.write("hello");</script>
</style>
But I do not like this ugly solution.
I think you want to traverse the DOM nodes:
var childNodes = document.getElementById('test').childNodes, i, output = [];
for (i = 0; i < childNodes.length; i++)
if (childNodes[i].nodeName == "SCRIPT")
output.push(childNodes[i].innerHTML);
return output.join('');

HTML onload - using variables as parameters

I want to use something like:
<body onLoad="init('A sentence with "quoted text" as parameter')">
Unfortunately, this does work, as the quotes in the parameter are not treated properly.
Escaping the quotes also does not work
<body onLoad="init('A sentence with \"quoted text\" as parameter')">
(Above also does not work).
How do I deal with this. I though maybe I can create a string variable and assigne my sentence (with quotes) to it. But I dont know how to do it! The body onload is HTML and the Javascript variables would be visible only within the scope of the script, right? To be precise, the following does not work:
<script language="JavaScript">
var dada='A sentence with \"quoted text\" as parameter';
</script>
<body onLoad="init($dada, '</a>')">
You would have to use HTML entities to make it work:
<body onLoad="init('A sentence with "quoted text" as parameter')">
the much cleaner way, though, would be to assign the value in a separate <SCRIPT> part in the document's head.
...
<script>
body.onload = function() { init('A sentence with "quoted text" as parameter'); }
</script>
<body>
...
the onload event has the general downside that it is fired only when the document and all its assets (images, style sheets...) have been loaded. This is where the onDOMLoad event comes in: It fires when the core HTML structure is ready, and all elements are rendered. It is not uniformly supported across browsers, though, so all frameworks have their own implementation of it.
The jQuery version is called .ready().
Rather than inlining the onLoad method, why not set it programatically. You can then use the power of closures to get the data into the function.
<script type="text/javascript">
var data = "some data";
document.body.onload = function()
{
alert(data);
}
</script>
not the nicest way
onload='init("A sentence with \"quoted text\" as parameter")'
By the way, I was making a silly mistake.
<script language="JavaScript">
var dada='A sentence with \"quoted text\" as parameter'; </script>
<body onLoad="init(dada, '</a>')">
This works too. I was using $dada instead of dada in the onload call.

Missing } in XML expression

I have an external javascript file that I want to, upon include, write some HTML to the end of the web page.
Upon doing so though I get the error Missing } in XML expression on the line that uses dropdownhtml.
Here is my code
var dropdownhtml = '<div id="dropdown"></div>';
$(document).ready(function(){
//$(document).append(dropdownhtml);
alert(dropdownhtml);
});
The XHTML webpage that includes this file does so like this:
<script type="text/javascript" src="/web/resources/js/dropdownmenu.js"></script>
Doing either append or alert throws up the same error, what is going wrong?
I got this error because I called an external JavaScript within an existing JavaScript, so ended up with:
<script type="text/javascript">
<script type="text/javascript">
code
</script>
code
</script>
Edit Your update changes the question a bit. :-)
There's nothing wrong with your quoted Javascript or with the script tag that includes it, the problem must lie elsewhere on the page.
The old answer:
If you're including Javascript inside an XML document, you must wrap it up in a CDATA section, or you'll run into trouble like this because the XML parser neither knows nor cares about your Javascript quotes, and instead seems markup (your <div>s in the string).
E.g.:
<foo>
<bar><![CDATA[
var dropdownhtml = '<div id="dropdown"></div>';
$(document).ready(function(){
//$(document).append(dropdownhtml);
alert(dropdownhtml);
});
]]></bar>
</foo>
Naturally you need to ensure that the ]]> sequence never appears in a string (or comment, etc.) your script, but that's quite easy to do (for instance: "Be sure to interrupt the end sequence with a harmless backslash like this: ]]\>; that escape just resolves to > anyway.")
There's definitely a missing ); at the end of your code sample. Don't get where there may be a missing } though.
I have empty script on my page
<script src=""></script>
And this leads to such error

Getting content of a script file using Javascript

I have the following script element in my web page:
<script src="default.js" type="text/javascript"></script>
Using JavaScript, I want to be able to retrieve the content of the script file. I know I could use an ajax request to get the data but then I am getting something from the server that I already have locally.
So what I would prefer to do is retrieve the content from the DOM (if that's possible) or something that has the same result.
Cheers
Anthony
UPDATE
I was trying to simplify the question, maybe a bad a idea, I thought this way would cause less questions.
The real situation I have is as follows, I actually have
<script type="text/html" class="jq-ItemTemplate_Approval">
...
html template that is going to be consumed by jQuery and jTemplate
...
</script>
Now this works fine but it means each time the page loads I have to send down the template as part of the HTML of the main page. So my plan was to do the following:
<script src="template.html" type="text/html"></script>
This would mean that the browser would cache the content of template.html and I would not have to send it down each time. But to do this I need to be able to get the content from the file.
Also in this case, as far as I know, requesting the content via ajax isn't going to help all that much because it has to go back to the server to get the content anyway.
If I understand you correctly, you don't want to use Ajax to load an html template text, but rather have it loaded with the rest of the page. If you control the server side, you can always include the template text in an invisible div tag that you then reference from Javascript:
<div id="template" style="display:none;">
...template text...
</div>
<script>
// pops up the template text.
alert(document.getElementById("template").innerHTML);
</script>
If you are just looking for to load the template so that you can have it cached, you can put the contents in a variable like this:
<script>
var template = "template text..";
</script>
or you can load it using ajax and store the template in a variable so it is accessible. It's pretty trivial in jquery:
var template;
$.get("template.html", function(data){
template = data;
});
unless you load a script as literal text in the page, it does not exist as text. It is interpreted by the browser and melded into the runtime, with any other scripts.
If you want the source you have to fetch it again,if with Ajax get the responseText.
It will come from the browser cache, and doesn't have to be downloaded again.
I think what you want to do is to assign a variable inside template.js. Then you have the variable available for use wherever you want in jquery. Something like:
var tpl = "<div> ... </div>"
Wouldn't this be a simpler solution to your problem? We do this in Ext JS. I think this will work for you in jQuery.
You could get the attribute of the src of the script and then use XHR to get the contents of the JS file. It's a much cleaner way of doing it IMO. e.g.:-
if(window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.status == 200 && xhr.readyState == 4) {
var sourceCode = xhr.responseText;
alert('The source code is:-\n'+sourceCode);
}
}
xhr.open("GET",document.getElementById('scriptID').src,true);
xhr.send(null);
}
Using an iFrame & HTML5 Local Storage
Save the templates for rendering later...
not stoked about the iFrame, but it seems to be working pretty good (haven't ran performance tests yet)
Put the iFrame on the page you want the template on (index.html)
<html>
<head>
<iframe src="mustache.Users.html" onload="this.remove();" class="hidden" id="users_template"></iframe>
</head>
</html>
Make sure the src attribute is set
hide the element until you can get rid of it after it loads
Put this body wrapper around your template (mustache.Users.html)
(don't worry it won't show up in the template)
<body onload="localStorage.setItem('users_template',this.document.body.innerHTML);">
<ul class="list-group" id="users" >
{{#users}}<li>{{name}}</li>{{/users}}
</ul>
</body>
replace 'users_template' with whatever name for your variable
the 'onload' attribute saves the template into localStorage during load
Now You can access your templates from anywhere
localStorage.getItem('users_template')
OR
window.localStorage.getItem('users_template')
What is in the JavaScript file? If it's actual code, you can run functions and reference variables in there just like you had cut and paste them into the webpage. You'll want to put the include line above any script blocks that reference it.
Is this what your looking to accomplish?
Why not use Ajax (well Ajah because its html :-))?
when the server is set up correctly and no no-cache or past expires headers are sent, the browser will cache it.
The way that most JavaScript import files work is they include a script, that immediately calls a function with a parameter of certain text, or of another function. To better illustrate, say you have your main index.html file, set it up like this:
<html>
<head>
</head>
<body>
<script>
let modules = {};
function started(moduleName, srcTxt) {
modules[moduleName] = (srcTxt) //or something similar
}
</script>
<!--now you can include other script tags, and any script tags that will be included, their source can be gotten (if set up right, see later)-->
<script src="someOtherFile.js"></script>
</body>
</html>
now make that other file, someOtherFile.js, and right away when its loaded, simply call that "started" function which should already be declared in the scope, and when thats done, then whatever text is passed, from the file, is stored in the main index.html file. You can even stringify an entire function and put it in, for example:
started("superModule", (function() {
/*
<?myCustomTemplateLanguage
<div>
{something}Entire Javascript / html template file goes here!!{/something}
</div>
?>
*/
}).toString());
now you can access the inner content of the function, and get all the text in between the comments, or better yet, then do other parsing etc, or make some other kind of parsing identifiers at the beginning and end of the comments, as shown above, and get all text in between those

Categories

Resources