Commit 915a24bda633a3584beb6a195270f99bc10f2cc6

Authored by Miguel Barão
1 parent 8062daef
Exists in master and in 1 other branch dev

- moved javascript and css out of html to external files

1 1
2 # BUGS 2 # BUGS
3 3
  4 +- remover learn.css uma vez que nao é usado em lado nenhum?
4 - detect questions in questions.yaml without ref -> error ou generate default. 5 - detect questions in questions.yaml without ref -> error ou generate default.
5 - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores. 6 - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores.
6 - Criar outra estrutura organizada em capítulos (conjuntos de tópicos). Permitir capítulos de capítulos, etc. talvez usar grafos de grafos... 7 - Criar outra estrutura organizada em capítulos (conjuntos de tópicos). Permitir capítulos de capítulos, etc. talvez usar grafos de grafos...
@@ -8,7 +9,6 @@ @@ -8,7 +9,6 @@
8 - session management. close after inactive time. 9 - session management. close after inactive time.
9 - generators not working: bcrypt (ver blog) 10 - generators not working: bcrypt (ver blog)
10 - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question. 11 - tabelas nas perguntas radio/checkbox não ocupam todo o espaço como em question.
11 -- mover javascript para ficheiros externos e carregar com <script defer src='...'>  
12 12
13 # TODO 13 # TODO
14 14
@@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
32 32
33 # FIXED 33 # FIXED
34 34
  35 +- mover javascript para ficheiros externos e carregar com script defer src
35 - implementar xsrf. Ver [http://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection]() 36 - implementar xsrf. Ver [http://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection]()
36 - se refs de um topic estao invalidos, nao carrega esse topico. devia haver um error nos logs a indicar qual o ref invalido. 37 - se refs de um topic estao invalidos, nao carrega esse topico. devia haver um error nos logs a indicar qual o ref invalido.
37 - link directo para topico nao valida se topico esta unlocked. 38 - link directo para topico nao valida se topico esta unlocked.
static/css/maintopics.css 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +body {
  2 + margin: 0;
  3 + padding-top: 100px;
  4 + margin-bottom: 80px; /* Margin bottom by footer height */
  5 +}
static/css/topic.css 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +.progress {
  2 + height: 20px;
  3 + border-radius: 0px;
  4 +}
  5 +body {
  6 + margin: 0;
  7 + padding-top: 70px;
  8 + margin-bottom: 80px; /* Margin bottom by footer height */
  9 +}
  10 +.footer {
  11 + position: absolute;
  12 + bottom: 0;
  13 + width: 100%;
  14 + /* Set the fixed height of the footer here */
  15 + height: 60px;
  16 + line-height: 60px; /* Vertically center the text there */
  17 + background-color: #f5f5f5;
  18 +}
  19 +html {
  20 + position: relative;
  21 + min-height: 100%;
  22 +}
  23 +textarea {
  24 + font-family: monospace;
  25 +}
static/js/maintopics.js 0 → 100644
@@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
  1 +function notify(msg) {
  2 + $("#notifications").html(msg);
  3 + $("#notifications").fadeIn(250).delay(5000).fadeOut(500);
  4 +}
  5 +
  6 +function getCookie(name) {
  7 + var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
  8 + return r ? r[1] : undefined;
  9 +}
  10 +
  11 +function change_password() {
  12 + var token = getCookie('_xsrf');
  13 + $.ajax({
  14 + type: "POST",
  15 + url: "/change_password",
  16 + headers: {'X-XSRFToken' : token },
  17 + data: {
  18 + "new_password": $("#new_password").val(),
  19 + },
  20 + dataType: "json",
  21 + success: function(r) {
  22 + notify(r['msg']);
  23 + },
  24 + error: function(r) {
  25 + notify(r['msg']);
  26 + },
  27 + });
  28 +}
  29 +
  30 +$(document).ready(function() {
  31 + $("#change_password").click(change_password);
  32 +});
static/js/topic.js 0 → 100644
@@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
  1 +$.fn.extend({
  2 + animateCSS: function (animation) {
  3 + var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
  4 + this.addClass('animated ' + animation).one(animationEnd, function() {
  5 + $(this).removeClass('animated ' + animation);
  6 + });
  7 + }
  8 +});
  9 +
  10 +// Process response given by the server
  11 +function updateQuestion(response){
  12 + switch (response["method"]) {
  13 + case "new_question":
  14 + $("#question_div").html(response["params"]["question"]);
  15 + $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]);
  16 +
  17 + MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]);
  18 +
  19 + // enable shift+enter to submit and tab to spaces conversion
  20 + $("input:text, input:radio, input:checkbox").keydown(function (e) {
  21 + if (e.keyCode == 13) {
  22 + e.preventDefault();
  23 + if (e.shiftKey) postQuestion();
  24 + return false;
  25 + }});
  26 + $("textarea").keydown(function (e) {
  27 + if (e.keyCode == 13 && e.shiftKey) { // shift enter
  28 + e.preventDefault();
  29 + postQuestion();
  30 + }
  31 + else if (e.keyCode == 9) { // tab
  32 + e.preventDefault(); // prevent loosing focus
  33 + // get caret position/selection
  34 + var start = this.selectionStart;
  35 + var end = this.selectionEnd;
  36 + var value = $(this).val();
  37 +
  38 + // set textarea value to: text before caret + tab + text after caret
  39 + $(this).val(value.substring(0, start) + " " + " " + " " + " " + value.substring(end));
  40 + // put caret at right position again (add one for the tab)
  41 + this.selectionStart = this.selectionEnd = start + 4;
  42 + }});
  43 + $('#question_div').animateCSS('zoomIn');
  44 + break;
  45 +
  46 + case "shake":
  47 + $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]);
  48 + $('#question_div').animateCSS('shake');
  49 + break;
  50 +
  51 + case "finished_topic":
  52 + $('#submit').css("visibility", "hidden");
  53 + $("#content").html(response["params"]["question"]);
  54 + $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100);
  55 + $("#content").animateCSS('tada');
  56 + setTimeout(function(){ window.location.replace('/'); }, 2000);
  57 + break;
  58 + }
  59 +}
  60 +
  61 +// Get current question
  62 +function getQuestion() {
  63 + $.ajax({
  64 + type: "GET",
  65 + url: "/question",
  66 + dataType: "json", // expected from server
  67 + success: updateQuestion,
  68 + error: function() {alert("O servidor não responde.");}
  69 + });
  70 +}
  71 +
  72 +// Send answer and receive a response.
  73 +// The response can be a new_question or a shake if the answer is wrong.
  74 +function postQuestion() {
  75 + $.ajax({
  76 + type: "POST",
  77 + url: "/question",
  78 + data: $("#question_form").serialize(), // {'a':10,'b':20},
  79 + dataType: "json", // expected from server
  80 + success: updateQuestion,
  81 + error: function() {alert("O servidor não responde.");}
  82 + });
  83 +}
  84 +
  85 +$(document).ready(function() {
  86 + getQuestion();
  87 + $("#submit").click(postQuestion);
  88 +});
templates/maintopics.html
@@ -3,58 +3,55 @@ @@ -3,58 +3,55 @@
3 <!doctype html> 3 <!doctype html>
4 <html lang="pt-PT"> 4 <html lang="pt-PT">
5 <head> 5 <head>
6 - <title>iLearn</title>  
7 - <link rel="icon" href="/static/favicon.ico"> 6 + <title>iLearn</title>
  7 + <link rel="icon" href="/static/favicon.ico">
8 8
9 - <meta charset="utf-8">  
10 - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">  
11 - <meta name="author" content="Miguel Barão"> 9 + <meta charset="utf-8">
  10 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  11 + <meta name="author" content="Miguel Barão">
12 12
13 <!-- Bootstrap, Fontawesome --> 13 <!-- Bootstrap, Fontawesome -->
14 - <link rel="stylesheet" href="/static/bootstrap/css/bootstrap-material.min.css">  
15 - <link rel="stylesheet" href="/static/font-awesome/css/font-awesome.min.css"> 14 + <link rel="stylesheet" href="/static/bootstrap/css/bootstrap-material.min.css">
  15 + <link rel="stylesheet" href="/static/font-awesome/css/font-awesome.min.css">
16 16
17 <!-- Other --> 17 <!-- Other -->
18 - <!-- <link rel="stylesheet" href="/static/css/learn.css"> --> 18 + <link rel="stylesheet" href="/static/css/maintopics.css">
19 19
20 - <style>  
21 - body {  
22 - margin: 0;  
23 - padding-top: 100px;  
24 - }  
25 - </style> 20 + <script defer src="/static/js/jquery.min.js"></script>
  21 + <script defer src="/static/popper/umd/popper.min.js"></script>
  22 + <script defer src="/static/bootstrap/js/bootstrap.min.js"></script>
  23 + <script defer src="/static/js/maintopics.js"></script>
26 24
27 </head> 25 </head>
28 <!-- ===================================================================== --> 26 <!-- ===================================================================== -->
29 <body> 27 <body>
30 <nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-primary"> 28 <nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-primary">
31 <!-- <a class="navbar-brand" href="#"> --> 29 <!-- <a class="navbar-brand" href="#"> -->
32 - <img src="/static/logo_horizontal10.png" class="navbar-brand" alt="UEvora"> 30 + <img src="/static/logo_horizontal10.png" class="navbar-brand" alt="UEvora">
33 <!-- </a> --> 31 <!-- </a> -->
34 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> 32 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
35 <span class="navbar-toggler-icon"></span> 33 <span class="navbar-toggler-icon"></span>
36 </button> 34 </button>
37 35
  36 + <div class="collapse navbar-collapse" id="navbarText">
  37 + <ul class="navbar-nav mr-auto"></ul>
38 38
39 - <div class="collapse navbar-collapse" id="navbarText">  
40 - <ul class="navbar-nav mr-auto"></ul>  
41 -  
42 - <ul class="navbar-nav">  
43 - <li class="nav-item dropdown">  
44 - <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">  
45 - <i class="fa fa-user" aria-hidden="true"></i>  
46 - <span id="name">{{ escape(name) }}</span>  
47 - (<span id="number">{{ escape(uid) }}</span>)  
48 - <span class="caret"></span>  
49 - </a>  
50 - <div class="dropdown-menu" aria-labelledby="navbarDropdown">  
51 - <a class="dropdown-item" data-toggle="modal" data-target="#password_modal">Mudar Password</a>  
52 - <div class="dropdown-divider"></div>  
53 - <a class="dropdown-item" href="/logout">Sair</a>  
54 - </div>  
55 - </li>  
56 - </ul>  
57 - </div> 39 + <ul class="navbar-nav">
  40 + <li class="nav-item dropdown">
  41 + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
  42 + <i class="fa fa-user" aria-hidden="true"></i>
  43 + <span id="name">{{ escape(name) }}</span>
  44 + (<span id="number">{{ escape(uid) }}</span>)
  45 + <span class="caret"></span>
  46 + </a>
  47 + <div class="dropdown-menu" aria-labelledby="navbarDropdown">
  48 + <a class="dropdown-item" data-toggle="modal" data-target="#password_modal">Mudar Password</a>
  49 + <div class="dropdown-divider"></div>
  50 + <a class="dropdown-item" href="/logout">Sair</a>
  51 + </div>
  52 + </li>
  53 + </ul>
  54 + </div>
58 </nav> 55 </nav>
59 <!-- ===================================================================== --> 56 <!-- ===================================================================== -->
60 <div class="container"> 57 <div class="container">
@@ -127,44 +124,5 @@ @@ -127,44 +124,5 @@
127 </div><!-- /.modal-dialog --> 124 </div><!-- /.modal-dialog -->
128 </div><!-- /.modal --> 125 </div><!-- /.modal -->
129 126
130 -<!-- Scripts -->  
131 -<script src="/static/js/jquery.min.js"></script>  
132 -<script src="/static/popper/umd/popper.min.js"></script>  
133 -<script src="/static/bootstrap/js/bootstrap.min.js"></script>  
134 -  
135 -<script>  
136 - function notify(msg) {  
137 - $("#notifications").html(msg);  
138 - $("#notifications").fadeIn(250).delay(5000).fadeOut(500);  
139 - }  
140 -  
141 - function getCookie(name) {  
142 - var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");  
143 - return r ? r[1] : undefined;  
144 - }  
145 -  
146 - function change_password() {  
147 - var token = getCookie('_xsrf');  
148 - $.ajax({  
149 - type: "POST",  
150 - url: "/change_password",  
151 - headers: {'X-XSRFToken' : token },  
152 - data: {  
153 - "new_password": $("#new_password").val(),  
154 - },  
155 - dataType: "json",  
156 - success: function(r) {  
157 - notify(r['msg']);  
158 - },  
159 - error: function(r) {  
160 - notify(r['msg']);  
161 - },  
162 - });  
163 - }  
164 -  
165 - $(document).ready(function() {  
166 - $("#change_password").click(change_password);  
167 - });  
168 -</script>  
169 </body> 127 </body>
170 </html> 128 </html>
templates/topic.html
@@ -25,36 +25,12 @@ @@ -25,36 +25,12 @@
25 <link rel="stylesheet" href="/static/css/github.css"> 25 <link rel="stylesheet" href="/static/css/github.css">
26 26
27 <!-- Other --> 27 <!-- Other -->
28 - <!-- <link rel="stylesheet" href="/static/css/learn.css"> -->  
29 -  
30 - <style>  
31 - .progress {  
32 - height: 20px;  
33 - border-radius: 0px;  
34 - }  
35 - body {  
36 - margin: 0;  
37 - padding-top: 70px;  
38 - margin-bottom: 80px; /* Margin bottom by footer height */  
39 - }  
40 - .footer {  
41 - position: absolute;  
42 - bottom: 0;  
43 - width: 100%;  
44 - /* Set the fixed height of the footer here */  
45 - height: 60px;  
46 - line-height: 60px; /* Vertically center the text there */  
47 - background-color: #f5f5f5;  
48 - }  
49 - html {  
50 - position: relative;  
51 - min-height: 100%;  
52 - }  
53 - textarea {  
54 - font-family: monospace;  
55 - }  
56 - </style> 28 + <link rel="stylesheet" href="/static/css/topic.css">
57 29
  30 + <script defer src="/static/js/jquery.min.js"></script>
  31 + <script defer src="/static/popper/umd/popper.min.js"></script>
  32 + <script defer src="/static/bootstrap/js/bootstrap.min.js"></script>
  33 + <script defer src="/static/js/topic.js"></script>
58 </head> 34 </head>
59 <!-- ===================================================================== --> 35 <!-- ===================================================================== -->
60 <body> 36 <body>
@@ -114,107 +90,5 @@ @@ -114,107 +90,5 @@
114 </div> 90 </div>
115 </footer> 91 </footer>
116 92
117 -  
118 -  
119 -<!-- ===================================================================== -->  
120 -<!-- JAVASCRIPT -->  
121 -<!-- ===================================================================== -->  
122 -  
123 -<script src="/static/js/jquery.min.js"></script>  
124 -<script src="/static/popper/umd/popper.min.js"></script>  
125 -<script src="/static/bootstrap/js/bootstrap.min.js"></script>  
126 -  
127 -<script>  
128 - $.fn.extend({  
129 - animateCSS: function (animation) {  
130 - var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';  
131 - this.addClass('animated ' + animation).one(animationEnd, function() {  
132 - $(this).removeClass('animated ' + animation);  
133 - });  
134 - }  
135 - });  
136 -  
137 - // Process response given by the server  
138 - function updateQuestion(response){  
139 - switch (response["method"]) {  
140 - case "new_question":  
141 - $("#question_div").html(response["params"]["question"]);  
142 - $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]);  
143 -  
144 - MathJax.Hub.Queue(["Typeset",MathJax.Hub,"question_div"]);  
145 -  
146 - // enable shift+enter to submit and tab to spaces conversion  
147 - $("input:text, input:radio, input:checkbox").keydown(function (e) {  
148 - if (e.keyCode == 13) {  
149 - e.preventDefault();  
150 - if (e.shiftKey) postQuestion();  
151 - return false;  
152 - }});  
153 - $("textarea").keydown(function (e) {  
154 - if (e.keyCode == 13 && e.shiftKey) { // shift enter  
155 - e.preventDefault();  
156 - postQuestion();  
157 - }  
158 - else if (e.keyCode == 9) { // tab  
159 - e.preventDefault(); // prevent loosing focus  
160 - // get caret position/selection  
161 - var start = this.selectionStart;  
162 - var end = this.selectionEnd;  
163 - var value = $(this).val();  
164 -  
165 - // set textarea value to: text before caret + tab + text after caret  
166 - $(this).val(value.substring(0, start) + " " + " " + " " + " " + value.substring(end));  
167 - // put caret at right position again (add one for the tab)  
168 - this.selectionStart = this.selectionEnd = start + 4;  
169 - }});  
170 - $('#question_div').animateCSS('zoomIn');  
171 - break;  
172 -  
173 - case "shake":  
174 - $('#topic_progress').css('width', (100*response["params"]["progress"])+'%').attr('aria-valuenow', 100*response["params"]["progress"]);  
175 - $('#question_div').animateCSS('shake');  
176 - break;  
177 -  
178 - case "finished_topic":  
179 - $('#submit').css("visibility", "hidden");  
180 - $("#content").html(response["params"]["question"]);  
181 - $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100);  
182 - $("#content").animateCSS('tada');  
183 - setTimeout(function(){ window.location.replace('/'); }, 2000);  
184 - break;  
185 - }  
186 - }  
187 -  
188 - // Get current question  
189 - function getQuestion() {  
190 - $.ajax({  
191 - type: "GET",  
192 - url: "/question",  
193 - dataType: "json", // expected from server  
194 - success: updateQuestion,  
195 - error: function() {alert("O servidor não responde.");}  
196 - });  
197 - }  
198 -  
199 - // Send answer and receive a response.  
200 - // The response can be a new_question or a shake if the answer is wrong.  
201 - function postQuestion() {  
202 - $.ajax({  
203 - type: "POST",  
204 - url: "/question",  
205 - data: $("#question_form").serialize(), // {'a':10,'b':20},  
206 - dataType: "json", // expected from server  
207 - success: updateQuestion,  
208 - error: function() {alert("O servidor não responde.");}  
209 - });  
210 - }  
211 -  
212 - $(document).ready(function() {  
213 - getQuestion();  
214 - $("#submit").click(postQuestion);  
215 - });  
216 -</script>  
217 -  
218 -  
219 </body> 93 </body>
220 </html> 94 </html>