I have in my stock rails 3.1 app's projects#index.html.erb a javascript section to reads like this:
<script type="text/javascript" charset="utf-8">
var projects = <%= #projects.to_json.html_safe -%>
$(function () {
// some other code...
});
</script>
However, Chrome's console complains that "Uncaught SyntaxError: Unexpected identifier"
now, if I remove anything that follows the "var projects" line like this
<script type="text/javascript" charset="utf-8">
var projects = <%= #projects.to_json.html_safe -%>
</script>
everything is fine and when one typed "projects" in the Chrome javascript console, I do get the list of objects
[ object, object, object, ..., object ]
I cannot work out what the issue is here, can you help?
Thanks!
try adding a ; semicolon at the end of statement
Related
Ich want to call a JavaScript function within the application.html.erb. The function is imported and provided in the application.js:
import myFunction from 'myJSModule';
window.myFunction = myFunction;
I am using turbolinks and vite_rails and it seems like the JS function is called before the application.js is loaded and it is undefined.
Example:
application.html.erb
<html>
<head>
<script type="text/javascript">
</script>
<%= vite_javascript_tag "application", 'data-turbolinks-track': "reload" %>
<%= render "layouts/partial" %> # here I call the window.myFunction
</head>
<body></body>
</html>
layouts/_partial.html.erb
<script>
window.myFunction
</script>
Behaviour:
window.myFunction is undefined.
When I try to call the function in the console, after the side is loaded, then it is defined.
Also when I wait for the turbolinks:load event:
document.addEventListener("turbolinks:load", function() { window.myFunction }
The Problem with the event is, that it is triggered every navigation event, but I want to call the function only when the partial is re-rendered.
I recently migrated from webpack to vite. For webpack the solution seemed to be to move the render partial below the java_script_tag. Unfortunately, this does not work with with vite.
I could find a solution. Appending the attribute type of the script with module:
<script type="module">
window.myFunction
</script>
I am writing some exploratory javascript for a page. It includes the following line:
dashboard_settings.addEventListener('ajax:complete', function(){
...
I have placed the script just before the closing body tag.
I notice that when I navigate to the page, the JS works as I expect it to and there are no error messages in the console. However, when I navigate away from the page, an error message displays in the JS console:
Uncaught TypeError: Cannot read property 'addEventListener' of null
I understand that this is something to do with turbolinks. However, I am unsure how to go about avoiding it without turning turbolinks off.
Is there any way at all that I can specify that the script loads for the one page only?
I have tried the following without success:
<%= yield :page_scripts %>
<% content_for :page_scripts do %>
<script>
document.addEventListener('turbolinks:load', function(){
...
You can add the listener only if the element exists.
if (dashboard_settings)
dashboard_settings.addEventListener('ajax:complete', function(){...})
Or, if you wan't to add it only for a specific page, you could add a yield statement on your layout and only set it's content con the desired page
#layout
<html>
<body>
...
</body>
<%= yield(:after_body) -%>
</html>
#your view
<%= content_for :after_body do -%>
<script>
document.addEventListener('turbolinks:load', function(){
dashboard_settings.addEventListener('ajax:complete', function(){...})
})
</script>
<%- end -%>
I cannot seem to get the syntax right for parameters to partials. The following keeps getting a syntax error: Uncaught SyntaxError: Expected buffer, comment, partial, reference, section or special but "{" found dust.js line 60.
<html>
<head>
<script type='text/javascript' src='/static/js/jquery.js'></script>
<script type='text/javascript' src='/static/js/dust.js'></script>
<body>
<p>TEST</p>
<p class='area1'>content_stuff</p>
<script>
$(document).ready(function() {
dust.loadSource(dust.compile("THE PARTIAL IS: {>inner foo='bar' /} AND THATS ALL", "outer"));
dust.loadSource(dust.compile("INNERPART", "inner"));
dust.render("outer", {}, function(err, out) {
console.log(out);
$(".area1").html(out);
});
});
</script>
</body>
</html>
Stick with the latest linkedin release. I think you may need double quotes around bar param value though.
I use tablesorter in combination with jquerycsvtotable.
Everything works fine, but... there's a lag between the moment when the table is loaded and the moment when tablesorter is shown that makes the data appear without any formatting on the screen. The time varies on the amount of data, between 2 and say 5 seconds.
Is there any way to show a "loading" gif or just nothing untill all the process is completed to avoid showing ugly data?
Thanks!
PS: I don't mean the time tablesorter takes to re-order rows when you click on a certain header cell, which I know is already arranged with optional processing gifs shown into the header...
EDIT: please find below my code.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="/js/ts/css/theme.default.css">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript" src="/js/ts/js/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="/js/ts/js/jquery.tablesorter.widgets.min.js"></script>
<script type="text/javascript" src="/js/csv/js/jquery.csvToTable.js"></script>
<script>
$(function() {
var mytable = $('#tabla1')
.CSVToTable('/est/data/cli_30_0000.txt',{
loadingImage: '/js/csv/images/loading.gif',
separator: ";"
})
.bind("loadComplete",function(){
var footer = mytable.find('tr:last');
mytable
.find('thead').after( footer.wrap('<tfoot/>').parent() ).end()
.tablesorter({
sortList: [[5,0]],
widthFixed : true,
widgets: ["zebra", "filter", "stickyHeaders"],
widgetOptions: {filter_hideFilters : true}
});
});
});
</script>
</head>
<body>
<p>
<a href=../../>Home</a> <a href=../>30</a> <a href=./>0000</a><br>
2013-04-15 12:45:17
</p>
<div>
<table id="tabla1" class="tablesorter">
</div>
</table>
</body>
</html>
I wonder if the solution is as easy as just adding the "tablesorter" class name to the table. If you are using a theme other than default, that theme name should also be included:
var mytable = $('#tabla1')
.CSVToTable('/est/data/cli_30_0000.txt',{
tableClass: 'tablesorter tablesorter-default', // adjust theme name here
loadingImage: '/js/csv/images/loading.gif',
separator: ";"
})
In a Rails 3.1 app, how can I safely embed some JSON data into an HTML document?
Suppose I have this in a controller action:
#tags = [
{name:"tag1", color:"green"},
{name:"</script><b>I can do something bad here</b>", color:"red"}
]
And this in a corresponding view:
<script type="text/javascript" charset="utf-8">
//<![CDATA[
var tags_list = <%= #tags.to_json %>;
// ]]>
</script>
Then I get this in resulting HTML:
var tags_list = [
{"name":"tag1","color":"green"},
{"name":"</script><b>I can do something bad here</b>","color":"red"}
];
which triggers a SyntaxError: Unexpected token & in Chrome
If I remove the Rails' default HTML escaping with <%=raw tags.to_json
%>, then it returns this:
var tags_list = [
{"name":"tag1","color":"green"},
{"name":"</script><b>I can do something bad here</b>","color":"red"}
];
which, of course, breaks the HTML document with </script>.
Can I somehow tell to_json() method to return something more like this:
var tags_list = [
{"name":"tag1","color":"green"},
{"name":"</script><b>I can do something bad here</b>","color":"red"}
];
I asked this question on rubyonrails-talk mailing list, and I understand now that some people think that's a very bad idea to begin with, but in my case it works very nicely, as long as there are no HTML special chars in the data. So I just want to make the string returned by to_json HTML safe and still have JavaScript parse it properly.
UPDATE:
Based on #coreyward comment, I did make it a JS string literal, and that seems to be working great now. Its not quite as elegant of a solution as I was hoping for, but its not too bad either. Here is the code that is working for me:
<% tags = [{name:"tag1", color:"green"}, {name:"</script><b>I can \n\ndo something bad here</b>", color:"red"}] %>
<script type="text/javascript" charset="utf-8">
//<![CDATA[
var tags_list = $.parseJSON('<%=j tags.to_json.html_safe %>');
// ]]>
</script>
which results in:
<script type="text/javascript" charset="utf-8">
//<![CDATA[
var tags_list = $.parseJSON('[{\"name\":\"tag1\",\"color\":\"green\"},{\"name\":\"<\/script><b>I can \\n\\ndo something bad here<\/b>\",\"color\":\"red\"}]');
// ]]>
</script>
Your code using just #tags.to_json works in rails3, if you enable it with:
ActiveSupport.escape_html_entities_in_json = true
Otherwise, your other option is this:
var tags_list = <%= raw #tags.to_json.gsub("</", "<\\/") %>;
This saves the client having to parse the whole thing through $
The proper way in 2019 is to wrap obj.to_json with json_escape function. json_escape is directly intended for escaping specific HTML symbols inside JSON strings. Example below from the documentation:
json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
# => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
json_escape(json)
# => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
JSON.parse(json) == JSON.parse(json_escape(json))
# => true
It seems this page appears on top of Google Search results, that's why I decided to provide a comment with an update :)
btw, this works but is not a good solution in my opinion:
<script type="text/javascript" charset="utf-8">
//<![CDATA[
var tags_list = <%=raw #tags.to_json.gsub('/', '\/') %>;
// ]]>
</script>
I think that if you try this it will work:
var tags_list = "<%== #tags.to_json.gsub('/', '\/') %>";
(Notice the double == and the " ")
For instance with this in app/layouts/application.html.slim:
javascript:
window.translations = #{raw t("js").to_json};
And this in the translations:
js:
name:
must_be_present: Must be present<script>alert(1)</script>
The result will be escaped:
<script>window.translations = {"name":{"must_be_present":"Must be present\u003cscript\u003ealert(1)\u003c/script\u003e"}};</script>