How do I use jQuery to extract part of a dynamically loaded mustache template - javascript

In an attempt to avoid repetition, I am trying to use mustache for templating on both the server and client side. I have a template something like the following:
<html>
<head>...</head>
<body>
<table>
<tbody id="#this">
{{rows}}
<tr>{{row}}<tr>
{{/rows}}
</tbody>
</table>
</body>
</html>
This works great for my server side templating. On the client side however, I'm having some trouble. I can successfully load the template using ajax, but I don't need the whole thing to update the rows of the table.
$(function () {
var template;
$.ajax('templates/thetemplate.mustache', {
success : function (data) {
template = $('#this', data).html();
},
dataType : 'html'
});
});
When I use the above jQuery to get at the contents of the #this element however, the {{rows}} and {{/rows}} lines are stripped from the result, while the <tr>{{row}}</tr> between them are returned successfully. How can I get the entire contents?
I've tried $('#this', data).contents(); which gives the same results, and $('#this', data).val(); which returns an empty string. Using $(data).find('#this') instead has the same three results. I've also tried setting the dataType of the ajax call to 'text' with no apparent effect.
I realize that I could probably accomplish what I want (not duplicating parts of my templates for client vs server side use) by using partials, and that it would have the added bonus of avoiding the transfer of more template than I actually need to the client just to have it thrown away, but that doesn't answer my question. (tho slick solutions are appreciated)
Thanks

The problem has already occurred at $('#this', data), which sends data via the browser's HTML parser to convert it to a DOM fragment. Being error tolerant, the HTML parser does what it can with the template but it's not what you want because the template itself is not valid HTML. For this reason $('#this', data).anythingYouLike() or $(data).anythingYouLike() will not work.
As the HTML must be valid before submitting to the browser's HTML parser, there's no other choice than to perform Mustache rendering before submitting valid HTML to the browser with a $(renderedHTML) expression.
With that constraint in mind, there are two realistic options regarding the order of events :
strip down data to just <tbody ...>...</tbody> (with
a regular expression), then Mustache render, then $tbody = $(renderedHTML).
Mustache render data in full, then $tbody = $(renderedHTML).find('tbody')
As far as I'm aware, either approach will work. In both cases, you end up with a jQuery object, $tbody, containing a populated tbody node, which can then be inserted into the DOM with $tbody.appendTo($('#myTable')) or similar.

first, your template is not a good structure of table (of couse, it's template), it will become an good table after you do $(data), something like this:
[" {{rows}} {{row}} {{/rows}} ", <table>​…​</table>​]
{{rows}} are not in table element, so you can not get what you want by $('#this', data).html();
My solution:
data.replace(/\n/g, '').match(/<tbody id="#this">(.*)<\/tbody>/g)[0]

Related

Responding to jQuery Ajax request with Python

I am currently trying to implement the pre-built inline editor located here: https://github.com/wbotelhos/inplace
Unfortunately, the support documentation leaves a lot to desire and I have very little experience with Javascript, jQuery, or Ajax.
I have been able to successfully implement the HTML edits:
<td><div class="inplace" data-field-name="name" data-field-value="{{people['name']}}" data-url="/update/{{id}}">{{ people['name'] }}</a></td>
The Js:
<script type="text/javascript">
$('.inplace').inplace();
</script>
and have successfully grabbed, and printed the info sent from the Javascript.
#app.route('/update/<id>', methods=["POST", "PATCH"])
#login_required
def update(id):
new_data = request.get_data(as_text=True)
print(new_data)
return "200"
The issue I am facing, is that the Js returns an Undefined value which is what the HTML updates to.
Ignore the return "200" - I have tired several different methods. Success = True, json values, etc and nothing seems to work.
I am sure I am missing something simple.
It looks like you need to print json with the field name that matches your field_name attribute which is name.
So you will need to print something like this. I don't use python, so you will need to follow actual python syntax. Where the word name is correct, but you will need to add the value that you want shown
print('{"name":"NEW FIELD VALUE"}')

JSON.parse reviver to sanitise data (stopping XSS)

I'm trying to reduce vulenrabilities in an old code base, specifically XSS attacks. The general pattern is:
The site is a collection of HTML pages with tags
It uses jQuery
Each page runs its own scripts
Each page's scripts make an $.ajax request to a PHP file which returns text/plain JSON (via json_encode($data))
This data is then used to create dynamic HTML markup to inject into the DOM via either $(selector).html, $(selector).append, or occasionnally document.getElementById(id).innerHTML =
Here's a simplified example of the data flow:
page.html
<head>
<script src="script.js"></script>
</head>
<body>
<table id="myTable"></table>
</body>
script.js
$.ajax({url: "phpFile.php"}, function(data){
data = JSON.parse(data);
$("#myTable tbody").html(data.redcuce(row=>"<tr><td>"+row.text"+</td></tr>", ""))
}
phpFile.php
//database query resulting in $data = [text->'<img src="x" onerror="alert(1)">', text->'Innocent value']
echo json_encode($data);
Some of the data retrieved is user input, so if a field contains <img src="x" onerror="alert(1)">, this executes when rendered in the table.
Solution?
I've passed the below sanitising function to JSON.parse to sanitise the data
const sanitiseJson = (key, value) => typeof value === "string" ? DOMPurify.sanitize(value, { USE_PROFILES: { html: true } }) : value;
This uses the DOMPurify library, which would be very easy for me to implement with search and replace.
As I understand it, this will remove potentially malicious HTML from the JSON object. This will prevent XSS attacks in this scenario.
Is this sufficient for sanitising fetched data that is then inserted into the DOM?
What are the other XSS vulenrabilities that are not addressed by this?
Edit: note I'm well aware that .html() etc is not the proper way to insert data, but this is an old, large code base (45k+ lines) and it's heavily entrenched, so my question is more about if this is an 'acceptable' solution in a pinch or if it doesn't come close
From my limited testing it seems to work, and as I (poorly) understand the way the data is processed I can't see how this would be circumvented easily

How do I save external JSON data to a JavaScript variable? [duplicate]

I have this JSON file I generate in the server I want to make accessible on the client as the page is viewable. Basically what I want to achieve is:
I have the following tag declared in my html document:
<script id="test" type="application/json" src="http://myresources/stuf.json">
The file referred in its source has JSON data. As I've seen, data has been downloaded, just like it happens with the scripts.
Now, how do I access it in Javascript? I've tried accessing the script tag, with and without jQuery, using a multitude of methods to try to get my JSON data, but somehow this doesn't work. Getting its innerHTML would have worked had the json data been written inline in the script. Which it wasn't and isn't what I'm trying to achieve.
Remote JSON Request after page loads is also not an option, in case you want to suggest that.
You can't load JSON like that, sorry.
I know you're thinking "why I can't I just use src here? I've seen stuff like this...":
<script id="myJson" type="application/json">
{
name: 'Foo'
}
</script>
<script type="text/javascript">
$(function() {
var x = JSON.parse($('#myJson').html());
alert(x.name); //Foo
});
</script>
... well to put it simply, that was just the script tag being "abused" as a data holder. You can do that with all sorts of data. For example, a lot of templating engines leverage script tags to hold templates.
You have a short list of options to load your JSON from a remote file:
Use $.get('your.json') or some other such AJAX method.
Write a file that sets a global variable to your json. (seems hokey).
Pull it into an invisible iframe, then scrape the contents of that after it's loaded (I call this "1997 mode")
Consult a voodoo priest.
Final point:
Remote JSON Request after page loads is also not an option, in case you want to suggest that.
... that doesn't make sense. The difference between an AJAX request and a request sent by the browser while processing your <script src=""> is essentially nothing. They'll both be doing a GET on the resource. HTTP doesn't care if it's done because of a script tag or an AJAX call, and neither will your server.
Another solution would be to make use of a server-side scripting language and to simply include json-data inline. Here's an example that uses PHP:
<script id="data" type="application/json"><?php include('stuff.json'); ?></script>
<script>
var jsonData = JSON.parse(document.getElementById('data').textContent)
</script>
The above example uses an extra script tag with type application/json. An even simpler solution is to include the JSON directly into the JavaScript:
<script>var jsonData = <?php include('stuff.json');?>;</script>
The advantage of the solution with the extra tag is that JavaScript code and JSON data are kept separated from each other.
It would appear this is not possible, or at least not supported.
From the HTML5 specification:
When used to include data blocks (as opposed to scripts), the data must be embedded inline, the format of the data must be given using the type attribute, the src attribute must not be specified, and the contents of the script element must conform to the requirements defined for the format used.
While it's not currently possible with the script tag, it is possible with an iframe if it's from the same domain.
<iframe
id="mySpecialId"
src="/my/link/to/some.json"
onload="(()=>{if(!window.jsonData){window.jsonData={}}try{window.jsonData[this.id]=JSON.parse(this.contentWindow.document.body.textContent.trim())}catch(e){console.warn(e)}this.remove();})();"
onerror="((err)=>console.warn(err))();"
style="display: none;"
></iframe>
To use the above, simply replace the id and src attribute with what you need. The id (which we'll assume in this situation is equal to mySpecialId) will be used to store the data in window.jsonData["mySpecialId"].
In other words, for every iframe that has an id and uses the onload script will have that data synchronously loaded into the window.jsonData object under the id specified.
I did this for fun and to show that it's "possible' but I do not recommend that it be used.
Here is an alternative that uses a callback instead.
<script>
function someCallback(data){
/** do something with data */
console.log(data);
}
function jsonOnLoad(callback){
const raw = this.contentWindow.document.body.textContent.trim();
try {
const data = JSON.parse(raw);
/** do something with data */
callback(data);
}catch(e){
console.warn(e.message);
}
this.remove();
}
</script>
<!-- I frame with src pointing to json file on server, onload we apply "this" to have the iframe context, display none as we don't want to show the iframe -->
<iframe src="your/link/to/some.json" onload="jsonOnLoad.apply(this, someCallback)" style="display: none;"></iframe>
Tested in chrome and should work in firefox. Unsure about IE or Safari.
I agree with Ben. You cannot load/import the simple JSON file.
But if you absolutely want to do that and have flexibility to update json file, you can
my-json.js
var myJSON = {
id: "12ws",
name: "smith"
}
index.html
<head>
<script src="my-json.js"></script>
</head>
<body onload="document.getElementById('json-holder').innerHTML = JSON.stringify(myJSON);">
<div id="json-holder"></div>
</body>
place something like this in your script file json-content.js
var mainjson = { your json data}
then call it from script tag
<script src="json-content.js"></script>
then you can use it in next script
<script>
console.log(mainjson)
</script>
Check this answer: https://stackoverflow.com/a/7346598/1764509
$.getJSON("test.json", function(json) {
console.log(json); // this will show the info it in firebug console
});
If you need to load JSON from another domain:
http://en.wikipedia.org/wiki/JSONP
However be aware of potential XSSI attacks:
https://www.scip.ch/en/?labs.20160414
If it's the same domain so just use Ajax.
Another alternative to use the exact json within javascript. As it is Javascript Object Notation you can just create your object directly with the json notation. If you store this in a .js file you can use the object in your application. This was a useful option for me when I had some static json data that I wanted to cache in a file separately from the rest of my app.
//Just hard code json directly within JS
//here I create an object CLC that represents the json!
$scope.CLC = {
"ContentLayouts": [
{
"ContentLayoutID": 1,
"ContentLayoutTitle": "Right",
"ContentLayoutImageUrl": "/Wasabi/Common/gfx/layout/right.png",
"ContentLayoutIndex": 0,
"IsDefault": true
},
{
"ContentLayoutID": 2,
"ContentLayoutTitle": "Bottom",
"ContentLayoutImageUrl": "/Wasabi/Common/gfx/layout/bottom.png",
"ContentLayoutIndex": 1,
"IsDefault": false
},
{
"ContentLayoutID": 3,
"ContentLayoutTitle": "Top",
"ContentLayoutImageUrl": "/Wasabi/Common/gfx/layout/top.png",
"ContentLayoutIndex": 2,
"IsDefault": false
}
]
};
While not being supported, there is an common alternative to get json into javascript. You state that "remote json request" it is not an option but you may want to consider it since it may be the best solution there is.
If the src attribute was supported, it would be doing a remote json request, so I don't see why you would want to avoid that while actively seeking to do it in an almost same fashion.
Solution :
<script>
async function loadJson(){
const res = await fetch('content.json');
const json = await res.json();
}
loadJson();
</script>
Advantages
allows caching, make sure your hosting/server sets that up properly
on chrome, after profiling using the performance tab, I noticed that it has the smallest CPU footprint compared to : inline JS, inline JSON, external JS.

Using jQuery on a string containing HTML

I'm trying to make a field similar to the facebook share box where you can enter a url and it gives you data about the page, title, pictures, etc. I have set up a server side service to get the html from the page as a string and am trying to just get the page title. I tried this:
function getLinkData(link) {
link = '/Home/GetStringFromURL?url=' + link;
$.ajax({
url: link,
success: function (data) {
$('#result').html($(data).find('title').html());
$('#result').fadeIn('slow');
}
});
}
which doesn't work, however the following does:
$(data).appendTo('#result')
var title = $('#result').find('title').html();
$('#result').html(title);
$('#result').fadeIn('slow');
but I don't want to write all the HTML to the page as in some case it redirects and does all sorts of nasty things. Any ideas?
Thanks
Ben
Try using filter rather than find:
$('#result').html($(data).filter('title').html());
To do this with jQuery, .filter is what you need (as lonesomeday pointed out):
$("#result").text($(data).filter("title").text());
However do not insert the HTML of the foreign document into your page. This will leave your site open to XSS attacks.
As has been pointed out, this depends on the browser's innerHTML implementation, so it does not work consistently.
Even better is to do all the relevant HTML processing on the server. Sending only the relevant information to your JS will make the client code vastly simpler and faster. You can whitelist safe/desired tags/attributes without ever worrying about dangerous ish getting sent to your users. Processing the HTML on the server will not slow down your site. Your language already has excellent HTML parsers, why not use them?.
When you place an entire HTML document into a jQuery object, all but the content of the <body> gets stripped away.
If all you need is the content of the <title>, you could try a simple regex:
var title = /<title>([^<]+)<\/title>/.exec(dat)[ 1 ];
alert(title);
Or using .split():
var title = dat.split( '<title>' )[1].split( '</title>' )[0];
alert(title);
The alternative is to look for the title yourself. Fortunately, unlike most parse your own html questions, finding the title is very easy because it doesn;t allow any nested elements. Look in the string for something like <title>(.*)</title> and you should be set.
(yes yes yes I know never use regex on html, but this is an exceptionally simple case)

jQuery, Ajax and getting a complete html structure back

I'm new to jQuery and to some extent JavaScript programming. I've successfully started to use jQuery for my Ajax calls however I'm stumped and I'm sure this is a newbie question but here goes.
I'm trying to return in an Ajax call a complete html structure, to the point a table structure. However what keeps happening is that jQuery either strips the html tags away and only inserts the deepest level of "text" or the special characters like <,>, etc get replaced with the escaped ones
I need to know how to turn off this processing of the received characters. Using firebug I see the responses going out of my WebServer correctly but the page received by the user and thus processed by jQuery are incorrect. A quick example will so what I mean.
I'm sending something like this
<results><table id="test"><tr>test</tr></table></results>
what shows up on my page if I do a page source view is this.
<results><table....
so you can see the special characters are getting converted and I don't know how to stop it.
The idea is for the <results></results> to be the xml tag and the text of that tag to be what gets placed into an existing <div> on my page.
Here is the JavaScript that I'm using to pull down the response and inserts:
$.post(url, params, function(data)
{
$('#queryresultsblock').text(data)
}, "html");
I've tried various options other than "html" like, "xml", "text" etc. They all do various things, the "html" gets me the closest so far.
The simplest way is just to return your raw HTML and use the html method of jQuery.
Your result:
<table id="test"><tr>test</tr></table>
Your Javascript call:
$.post(url, params, function(data){ $('#queryresultsblock').html(data) })
Another solution with less control — you can only do a GET request — but simpler is to use load:
$("#queryresultsblock").load(url);
If you must return your result in a results XML tag, you can try adding a jQuery selector to your load call:
$("#queryresultsblock").load(url + " #test");
You can't put unescaped HTML inside of XML. There are two options I see as good ways to go.
One way is to send escaped HTML in the XML, then have some JavaScript on the client side unescape that HTML. So you would send
<results><results><table....
And the javascript would convert the < to < and such.
The other option, and what I would do, is to use JSON instead of XML.
{'results': "<table id="test"><tr>test</tr></table>" }
The JavaScript should be able to extract that HTML structure as a string and insert it directly into your page without any sort of escaping or unescaping.
The other thing you could do is create an external .html file with just your HTML code snippet in it. So create include.html with
<results><table id="test"><tr>test</tr></table></results>
As the contents, then use a jquery .load function to get it onto the page. See it in action here.

Categories