JS Socket not passing special characters correctly - javascript

So, I've seen this question around a lot but the accepted answer isn't working for me. I have this chatroom-like site I'm working on, and while it works fine for most of it (I'm aware of a few other minor issues with my code but they're not part of the question), I'm having issues with the character encoding whenever my JS file is involved and I use sockets. I'll post my HTML and JS files now:
HTML:
{% extends "layout.html" %}
{% block heading %}
<meta charset="UTF-8">
<script type="text/javascript" src="{{ url_for('static', filename='js/chatroom.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/chatroom.css') }}">
{% endblock %}
{% block body %}
The Chatroom name is <span id="chatroom">{{ chatroom }}</span> and it's code is <span id="code">{{ code }}</span>
<div id="messages">
{% for message in messages %}
{% if message.author == online_user %}
<div class="own">{{ message.message }}</div>
{% else %}
<div class="other"><strong>{{ message.author }}</strong><br> {{ message.message }}</div>
{% endif %}
{% endfor %}
</div>
<textarea id="message" rows=5 cols=50></textarea>
<input type="button" id="send" value="Send">
{% endblock %}
JS:
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
document.querySelector('#send').onclick = () => {
const message = document.querySelector('#message').value;
socket.emit('send message', {'chatroom': {'code':document.querySelector('#code').innerHTML, 'name': document.querySelector('#chatroom').innerHTML}, 'message': message});
};
});
socket.on('broadcast message', data => {
if (data.message['chatroom']['code'] == document.querySelector('#code').innerHTML) {
const msg = document.createElement('div');
if (data.message['author'] == document.getElementById('current_user').innerHTML)
msg.innerHTML = data.message['message'];
else
msg.innerHTML = `${data.message['author']}: ${data.message['message']}`
document.querySelector('#messages').append(msg);
var listlen = document.getElementById('messages').getElementsByTagName('div').length;
while (listlen > 100) {
var msglist = document.getElementById('messages');
msglist.removeChild(msglist.childNodes[0]);
var listlen = document.getElementById('messages').getElementsByTagName('div').length;
};
};
});
});
Now, when the JS file and socketing isn't involved (for example, if I log into the site) the encoding works fine. However, when the textarea holds special characters such as Á (I'll use the word Ángel on both fields as an example), once I hit the send button (not before), the output is wrong.
Current output: {'chatroom': {'code': 'ouAY7mxCvhXb', 'name': 'UTF'}, 'author': 'Ángel', 'message': 'Ã\x81ngel'}
Expected output: {'chatroom': {'code': 'ouAY7mxCvhXb', 'name': 'UTF'}, 'author': 'Ángel', 'message': 'Ángel'}
The accepted answer for other questions marking this issue was adding the <meta charset="UTF-8"> line on my HTML, but I'm puzzled now since it hasn't worked. I also checked and my JS file is properly encoded to UTF-8 as well.
EDIT:
I'll add the server side code for the socket since it might as well help understand the output better. As seen here, the problem with the encoding only happens from what I pass from data (which can go wrong only on the message's content and chatroom's name, because the chatroom code is automatically generated and never includes special characters).
#socketio.on("send message")
def send_message(data):
data['message'] = data['message'].strip()
if len(data["message"]) > 0:
message = {'chatroom': data['chatroom'], 'author': session['username'], 'message': data['message']}
print(message)
stored_messages.append(message)
local_messages.append(message)
emit("broadcast message", {'message': message}, broadcast=True)

For future references if someone runs into this problem, I found out a way to deal with it. Instead of passing the raw message through the socket and expecting the program to automatically transform it to UTF-8 with the meta tag, I encoded it by adding an encoding function on the JS file and applying it to the message content.
function encode_utf8(s) {
return encodeURIComponent(s);
}
Former line 4 of JS file now looks like this: const message = encode_utf8(document.querySelector('#message').value);
That allows me send it encoded in a way my backend server can properly decode so it returns the expected output by using the urllib.parse library's unquote_plus.
#socketio.on("send message")
def send_message(data):
data['message'] = data['message'].strip()
print(data)
if len(data["message"]) > 0:
message = {'chatroom': data['chatroom'], 'author': session['username'], 'message': urllib.parse.unquote_plus(data['message'])} # <-- Modified line
print(message)
stored_messages.append(message)
local_messages.append(message)
emit("broadcast message", {'message': message}, broadcast=True)

Related

use javascript to display django object

I want to implement below using javascript so row click it will get index and display object of this index.
in django template this is working.
<div>{{ project.0.customer_name}}</div>
<div>{{ project.1.customer_name}}</div>
but the below javascript are not working even I get the correct ID.
var cell = row.getElementsByTagName("td")[0];
var id= parseInt(cell.innerHTML);
// not working
document.getElementById('lblname').innerHTML = '{{ project.id.customer_name}}';
// this is also working but what I want is dynamic base on row click
document.getElementById('lblname').innerHTML = '{{ project.1.customer_name}}';
display django object using index in javascript.
You have to understand what is happening with your code:
Templates like this are processed on the server:
'{{ project.id.customer_name}}'
I believe you do not have project.id on your server side, so you get None in the above line, and the moustache tag becomes smth like an empty string, and actual JavaScript code is like this:
document.getElementById('lblname').innerHTML = '';
It is only now that JS code is executed, and you can imagine what it will do.
What you want is processing moustache tags after the id variable has been set in JS, which is not how stuff works (at least, if you don't have some crazy tool chain).
One way of achieving what you want is to provide the whole project object (or array) to JavaScript by doing the following:
<script>
const project = {{ project|safe }};
</script>
A complete Django template could look like this (I used <span>s instead of table cells:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>django test</title>
</head>
<body>
{% block content %}
{% for item in project %}
<span data-id="{{ forloop.counter }}">{{ forloop.counter }}</span>
{% endfor %}
<div id="output" style="display: flex; flex-direction: column-reverse;">
</div>
<script>
const project = {{ project|safe }};
const spans = document.getElementsByTagName('span');
const output = document.getElementById('output');
const onSpanClick = (event) => {
const id = parseInt(event.target.getAttribute('data-id'), 10) - 1; // forloop.counter is 1-based, JS arrays are 0-based
const div = document.createElement('div');
div.innerHTML = project[id].customer_name;
output.appendChild(div);
}
Array.from(spans).forEach(span => {
span.addEventListener('click', onSpanClick);
})
</script>
{% endblock %}
</body>
</html>
Another way is the AJAX way: you create an API endpoint on your server side, so that an URL like example.com/api/customer_name/?id=999 responds to you with the name of customer id=999 when you click on some element and trigger an XMLHttpRequest with param id=999.

Error "unexpected char '#' " with rendering Handlebars template

I have a simple 100% working code from CS50 lecture, which represents the usage of Handlebars. Here it is:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
<script id="result" type="text/template">
<li>
You rolled:
{{#each values}}
<img alt="{{ this }}" title="{{ this }}" src="img/{{ this }}.png">
{{/each}}
(Total: {{ total }})
</li>
</script>
<script>
// Template for roll results
const template = Handlebars.compile(document.querySelector('#result').innerHTML);
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('#roll').onclick = () => {
// Generate random rolls.
const counter = parseInt(document.querySelector('#counter').value);
const rolls = [];
let total = 0;
for (let i = 0; i < counter; i++) {
const value = Math.floor(Math.random() * 6) + 1;
rolls.push(value);
total += value;
};
// Add roll results to DOM.
const content = template({'values': rolls, 'total': total});
document.querySelector('#rolls').innerHTML += content;
};
});
</script>
</head>
<body>
<input id="counter" type="number" placeholder="Number of Rolls" min="1" value="1">
<button id="roll">Roll</button>
<ul id="rolls">
</ul>
</body>
</html>
When I try to render it in my browser I get "jinja2.exceptions.TemplateSyntaxError: unexpected char '#'".
Obviously the problem is on my computer side. But how to fix it?
I have searched for this problem in the web. One interesting thing I catch was that it is somehow connected with "my server side templating engine". Here is a thread - https://github.com/wycats/handlebars.js/issues/269 . Another guy here (https://github.com/wycats/handlebars.js/issues/1263) says he had similar error because of Pagespeed.
How can I understand which "templating engine" is installed? I have an ordinary Mac and project in virtual environment.
What might be the problem?
This happens because jinja2 reads curly braces as syntax (as variables or block codes).
So, You have to escape jinja in your HTML code.
the methods to do so are following;
The easiest way is to output a literal variable delimiter ({{) is by using a variable expression.
{{ '{{' }}
For bigger sections, it makes sense to mark a block raw. For example, to include handlebars syntax in a template, you can use this snippet:
{% raw %}
<ul>
{{#each vlaues}}
<li>{{ this }}</li>
{{/endeach}}
</ul>
{% endraw %}
For more info check enter link description here

How to Embed multiple tweets of various users into HTML in django framework?

I am passing a data in the form of a dictionary from views.py to results.html in Django framework. The dictionary has the following format
'tweet': (tweet_analysis, tweet_id)
now in results.html, called by the views.py,
I am trying to Embed all the tweets that are passed to results.html, but the following code only displays one embedded tweet.
dicPositive: This is the dictionary containing all the tweets data
{% for tweet, tweet_feel in dicPositive.items %}
<div id="tweet" tweetID="{{tweet_feel.1}}"></div>
<script sync src="https://platform.twitter.com/widgets.js"></script>
<script>
window.onload = (function(){
var tweet = document.getElementById("tweet");
var id = tweet.getAttribute("tweetID");
twttr.widgets.createTweet(
id, tweet,
{
conversation : 'none', // or all
cards : 'hidden', // or visible
linkColor : '#cc0000', // default is blue
theme : 'light' // or dark
})
.then (function (el) {
el.contentDocument.querySelector(".footer").style.display = "none";
});
});
</script>
<!-- <li>{{tweet}} –> {{tweet_feel.0}} –> {{tweet_feel.1}}</li> -->
{% endfor %}
It is because you have used same id for multiple HTMLElements created while looping.
You must add loop counter to id attribute of div and also when
you are fetching it using getElementById inside script tag
{% for tweet, tweet_feel in dicPositive.items %}
<div id="tweet_{{forloop.counter}}" tweetID="{{tweet_feel.1}}"></div>
<script sync src="https://platform.twitter.com/widgets.js"></script>
<script>
window.onload = (function(){
var tweet = document.getElementById("tweet_{{forloop.counter}}");
# Rest of your code
...
</script>
{% endfor %}

How would I write an if/else statement for something that is undefined with a twig var?

I'm using twig to declare a var user:
<script type="text/javascript">
{% if user is defined %}
var user = {
example: {{ userjson | raw }}
};
{% endif %}
</script>
This checks to see if a user is logged in, if not my console returns this:
Uncaught ReferenceError: user is not defined
I want to be able to display a message if the user is not defined but I can't think of a way to do that. Right now I have this:
if(user){
console.log('hello');
} else {
console.log('undefined');
}
// This checks is the user information
var userId = user.example.id;
console.log(user.example.money);
Ok so I figured this out:
<script type="text/javascript">
{% if user is defined %}
var user = {
example: {{ userjson | raw }}
};
{% else %}
var user = false;
{% endif %}
</script>
Then I just check to see if the var user is false, and if it's not then I do something else.

Make an AJAX request to Flask application

I'm trying to refresh a Jenkins build console output in my Flask web application, however I am having trouble with the jQuery/AJAX to do it.
As you can see below, i'm just trying to get this working using a refresh button. Ideally I want to refresh {{buildinfo}} on a timer.
Currently my test function/jQuery is returning the error: Uncaught TypeError: Illegal invocation.
Heres the (working) function from my app.py I was using before I started down this path:
#app.route('/buildinfo/<job_id>/<job_number>', methods=['GET', 'POST'])
def buildInfo(job_id, job_number):
try:
console_output = server.get_build_console_output(job_id, int(job_number))
return render_template("buildinfo.html", buildinfo=console_output, job_id=job_id, job_number=job_number)
except Exception as e:
return render_template("buildinfo.html", error=str(e))
Here's the test function I have been using to receive the request and send back to the client:
#app.route('/_test')
def foo():
a = request.args.get('a', None, type=str)
b = request.args.get('b', 0, type=int)
bar = server.get_build_console_output(a, int(b))
return jsonify(result=bar)
And here is the buildinfo.html:
{% extends "base.html" %}
{% block content %}
<script type="text/javascript" src="{{ url_for('static', filename='jquery-1.12.0.min.js') }}"></script>
<script type="text/javascript">
var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
$(function() {
$('a#load').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_test', {
a: $('{{job_id}}'),
b: $('{{job_number}}')
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<div class="col-md-10">
<h1>Build Info</h1>
<br>
<p>retrieving {{job_id}}, build number {{job_number}}</p>
<br>
<p>{{buildinfo}}</p>
<br>
<span id="result">?</span>
go
</div>
{% endblock %}
You're feeding a HTML elements to the ajax call, when you should be passing values. Change this:
a: $('{{job_id}}'),
b: $('{{job_number}}')
to this:
a: {{job_id}},
b: {{job_number}},
...
I'm not sure about the types you're sending, if you need to send strings- wrap quotes around the double-moustache, but this should get you going. See here for a similar issue.
For anyone in the same boat, I am able to refresh a Flask value using the following. I may update this answer to include clearInterval() once the build is complete. Thanks to GG_Python for pointing out my mistake.
app.py :
#app.route('/buildinfo/<job_id>/<job_number>', methods=['GET', 'POST'])
def buildInfo(job_id, job_number):
try:
console_output = server.get_build_console_output(job_id, int(job_number))
return render_template("buildinfo.html", buildinfo=console_output, job_id=job_id, job_number=job_number)
except Exception as e:
return render_template("buildinfo.html", error=str(e))
#app.route('/_test')
def foo():
a = request.args.get('a', None, type=str)
b = request.args.get('b', 0, type=int)
bar = server.get_build_console_output(a, int(b))
return jsonify(result=bar)
buildinfo.html :
{% extends "base.html" %}
{% block content %}
<script type="text/javascript" src="{{ url_for('static', filename='jquery-1.12.0.min.js') }}"></script>
<script type="text/javascript">
var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
<script type="text/javascript">
setInterval(function() {
$.getJSON($SCRIPT_ROOT + '/_test', {
a: "{{job_id}}",
b: {{job_number}}
}, function(data) {
$("#result").text(data.result);
});
return false;
}, 3000);
</script>
<div class="col-md-10">
<h1>Build Info</h1>
<br>
<p>retrieving {{job_id}}, build number {{job_number}}</p>
<br>
<p id="result">{{buildinfo}}</p>
<br>
</div>
{% endblock %}

Categories

Resources