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

BUGS.md
1 1  
2 2 # BUGS
3 3  
  4 +- remover learn.css uma vez que nao é usado em lado nenhum?
4 5 - detect questions in questions.yaml without ref -> error ou generate default.
5 6 - topicos virtuais nao deveriam aparecer. na construção da árvore os sucessores seriam ligados directamente aos predecessores.
6 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 9 - session management. close after inactive time.
9 10 - generators not working: bcrypt (ver blog)
10 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 13 # TODO
14 14  
... ... @@ -32,6 +32,7 @@
32 32  
33 33 # FIXED
34 34  
  35 +- mover javascript para ficheiros externos e carregar com script defer src
35 36 - implementar xsrf. Ver [http://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection]()
36 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 38 - link directo para topico nao valida se topico esta unlocked.
... ...
static/css/maintopics.css 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 3 <!doctype html>
4 4 <html lang="pt-PT">
5 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 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 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 25 </head>
28 26 <!-- ===================================================================== -->
29 27 <body>
30 28 <nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-primary">
31 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 31 <!-- </a> -->
34 32 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
35 33 <span class="navbar-toggler-icon"></span>
36 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 55 </nav>
59 56 <!-- ===================================================================== -->
60 57 <div class="container">
... ... @@ -127,44 +124,5 @@
127 124 </div><!-- /.modal-dialog -->
128 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 127 </body>
170 128 </html>
... ...
templates/topic.html
... ... @@ -25,36 +25,12 @@
25 25 <link rel="stylesheet" href="/static/css/github.css">
26 26  
27 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 34 </head>
59 35 <!-- ===================================================================== -->
60 36 <body>
... ... @@ -114,107 +90,5 @@
114 90 </div>
115 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 93 </body>
220 94 </html>
... ...