Commit 2f87425cf091e8a39b63633e024c6810ed9fdb1c

Authored by Victor Costa
1 parent 6403ada6

New proposal ranking

index.html
... ... @@ -298,21 +298,23 @@
298 298 <td class="abstract-text">Propostas</td>
299 299 <td class="votes-for"></td>
300 300 <td class="votes-against"></td>
301   - {{!-- <td class="apoio">Apoio</td> --}}
302   - {{!-- <td class="participacao">Participacao</td> --}}
303   - <td class="score">Pontuação</td>
  301 + <td class="support">Apoio</td>
  302 + <td class="participation">Participação</td>
304 303 </tr>
305   - {{#each articles}}
  304 + {{#each proposals}}
306 305 <tr>
307 306 <td class="abstract-text"><div class="truncate"><p class="truncated">{{abstract}}</p></div></td>
308   - <td class="votes-for">{{votes_for}}</td>
309   - <td class="votes-against">{{votes_against}}</td>
310   - {{!-- <td class="apoio">{{#apoio .}}{{/apoio}}</td> --}}
311   - {{!-- <td class="participacao">{{#participacao .}}{{/participacao}}</td> --}}
312   - <td class="score">{{#score .}}{{/score}}</td>
  307 + <td class="votes-for value">{{votes_for}}</td>
  308 + <td class="votes-against value">{{votes_against}}</td>
  309 + <td class="support value">{{#round effective_support}}{{/round}}</td>
  310 + <td class="participation value">{{#round effective_participation}}{{/round}}</td>
313 311 </tr>
314 312 {{/each}}
315 313 </table>
  314 + <div class="updated-at">
  315 + <span>Atualizado </span>
  316 + <span class="timeago" title="{{updated_at}}"></span>
  317 + </div>
316 318 </div>
317 319 </script>
318 320  
... ...
js/handlebars-helpers.js
... ... @@ -60,21 +60,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
60 60 Handlebars.registerHelper('replace', function(string, to_replace, replacement) {
61 61 return (string || '').replace(new RegExp(to_replace, 'g'), replacement);
62 62 });
63   -
64   - Handlebars.registerHelper('score', function(article) {
65   - return article.votes_for - article.votes_against;
66   - });
67   -
68   - Handlebars.registerHelper('apoio', function(article) {
69   - // return (article.votes_for - article.votes_against)/(article.countViews);
70   - return 0;
71   - });
72   -
73   - Handlebars.registerHelper('participacao', function(article) {
74   - // return (article.votes_for + article.votes_against)/(article.countViews);
75   - return 0;
76   - });
77   -
  63 +
78 64 Handlebars.registerHelper('select_proposal', function(proposals, category_slug, selected_id) {
79 65 var ret = '<label for="proposal-selection" class="sr-only">Selecione o programa</label>'
80 66 ret = ret + '<select id="proposal-selection" name="proposal-selection" data-proposal="'+selected_id+'" title="Selecione o programa" class="proposal-selection">';
... ... @@ -101,6 +87,10 @@ define([&#39;handlebars&#39;], function(Handlebars){
101 87 }
102 88 });
103 89  
  90 + Handlebars.registerHelper('round', function(num) {
  91 + return +(Math.round(num + "e+2") + "e-2");
  92 + });
  93 +
104 94 function proposal_has_category(proposal, category_slug) {
105 95 for(var i=0; i<proposal.categories.length; i++) {
106 96 if(proposal.categories[i].slug == category_slug)
... ...
js/jquery.timeago.js 0 → 100644
... ... @@ -0,0 +1,221 @@
  1 +/**
  2 + * Timeago is a jQuery plugin that makes it easy to support automatically
  3 + * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
  4 + *
  5 + * @name timeago
  6 + * @version 1.4.1
  7 + * @requires jQuery v1.2.3+
  8 + * @author Ryan McGeary
  9 + * @license MIT License - http://www.opensource.org/licenses/mit-license.php
  10 + *
  11 + * For usage and examples, visit:
  12 + * http://timeago.yarp.com/
  13 + *
  14 + * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
  15 + */
  16 +
  17 +(function (factory) {
  18 + if (typeof define === 'function' && define.amd) {
  19 + // AMD. Register as an anonymous module.
  20 + define(['jquery'], factory);
  21 + } else {
  22 + // Browser globals
  23 + factory(jQuery);
  24 + }
  25 +}(function ($) {
  26 + $.timeago = function(timestamp) {
  27 + if (timestamp instanceof Date) {
  28 + return inWords(timestamp);
  29 + } else if (typeof timestamp === "string") {
  30 + return inWords($.timeago.parse(timestamp));
  31 + } else if (typeof timestamp === "number") {
  32 + return inWords(new Date(timestamp));
  33 + } else {
  34 + return inWords($.timeago.datetime(timestamp));
  35 + }
  36 + };
  37 + var $t = $.timeago;
  38 +
  39 + $.extend($.timeago, {
  40 + settings: {
  41 + refreshMillis: 60000,
  42 + allowPast: true,
  43 + allowFuture: false,
  44 + localeTitle: false,
  45 + cutoff: 0,
  46 + strings: {
  47 + prefixAgo: null,
  48 + prefixFromNow: null,
  49 + suffixAgo: "ago",
  50 + suffixFromNow: "from now",
  51 + inPast: 'any moment now',
  52 + seconds: "less than a minute",
  53 + minute: "about a minute",
  54 + minutes: "%d minutes",
  55 + hour: "about an hour",
  56 + hours: "about %d hours",
  57 + day: "a day",
  58 + days: "%d days",
  59 + month: "about a month",
  60 + months: "%d months",
  61 + year: "about a year",
  62 + years: "%d years",
  63 + wordSeparator: " ",
  64 + numbers: []
  65 + }
  66 + },
  67 +
  68 + inWords: function(distanceMillis) {
  69 + if(!this.settings.allowPast && ! this.settings.allowFuture) {
  70 + throw 'timeago allowPast and allowFuture settings can not both be set to false.';
  71 + }
  72 +
  73 + var $l = this.settings.strings;
  74 + var prefix = $l.prefixAgo;
  75 + var suffix = $l.suffixAgo;
  76 + if (this.settings.allowFuture) {
  77 + if (distanceMillis < 0) {
  78 + prefix = $l.prefixFromNow;
  79 + suffix = $l.suffixFromNow;
  80 + }
  81 + }
  82 +
  83 + if(!this.settings.allowPast && distanceMillis >= 0) {
  84 + return this.settings.strings.inPast;
  85 + }
  86 +
  87 + var seconds = Math.abs(distanceMillis) / 1000;
  88 + var minutes = seconds / 60;
  89 + var hours = minutes / 60;
  90 + var days = hours / 24;
  91 + var years = days / 365;
  92 +
  93 + function substitute(stringOrFunction, number) {
  94 + var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
  95 + var value = ($l.numbers && $l.numbers[number]) || number;
  96 + return string.replace(/%d/i, value);
  97 + }
  98 +
  99 + var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
  100 + seconds < 90 && substitute($l.minute, 1) ||
  101 + minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
  102 + minutes < 90 && substitute($l.hour, 1) ||
  103 + hours < 24 && substitute($l.hours, Math.round(hours)) ||
  104 + hours < 42 && substitute($l.day, 1) ||
  105 + days < 30 && substitute($l.days, Math.round(days)) ||
  106 + days < 45 && substitute($l.month, 1) ||
  107 + days < 365 && substitute($l.months, Math.round(days / 30)) ||
  108 + years < 1.5 && substitute($l.year, 1) ||
  109 + substitute($l.years, Math.round(years));
  110 +
  111 + var separator = $l.wordSeparator || "";
  112 + if ($l.wordSeparator === undefined) { separator = " "; }
  113 + return $.trim([prefix, words, suffix].join(separator));
  114 + },
  115 +
  116 + parse: function(iso8601) {
  117 + var s = $.trim(iso8601);
  118 + s = s.replace(/\.\d+/,""); // remove milliseconds
  119 + s = s.replace(/-/,"/").replace(/-/,"/");
  120 + s = s.replace(/T/," ").replace(/Z/," UTC");
  121 + s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
  122 + s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
  123 + return new Date(s);
  124 + },
  125 + datetime: function(elem) {
  126 + var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
  127 + return $t.parse(iso8601);
  128 + },
  129 + isTime: function(elem) {
  130 + // jQuery's `is()` doesn't play well with HTML5 in IE
  131 + return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
  132 + }
  133 + });
  134 +
  135 + // functions that can be called via $(el).timeago('action')
  136 + // init is default when no action is given
  137 + // functions are called with context of a single element
  138 + var functions = {
  139 + init: function(){
  140 + var refresh_el = $.proxy(refresh, this);
  141 + refresh_el();
  142 + var $s = $t.settings;
  143 + if ($s.refreshMillis > 0) {
  144 + this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
  145 + }
  146 + },
  147 + update: function(time){
  148 + var parsedTime = $t.parse(time);
  149 + $(this).data('timeago', { datetime: parsedTime });
  150 + if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
  151 + refresh.apply(this);
  152 + },
  153 + updateFromDOM: function(){
  154 + $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
  155 + refresh.apply(this);
  156 + },
  157 + dispose: function () {
  158 + if (this._timeagoInterval) {
  159 + window.clearInterval(this._timeagoInterval);
  160 + this._timeagoInterval = null;
  161 + }
  162 + }
  163 + };
  164 +
  165 + $.fn.timeago = function(action, options) {
  166 + var fn = action ? functions[action] : functions.init;
  167 + if(!fn){
  168 + throw new Error("Unknown function name '"+ action +"' for timeago");
  169 + }
  170 + // each over objects here and call the requested function
  171 + this.each(function(){
  172 + fn.call(this, options);
  173 + });
  174 + return this;
  175 + };
  176 +
  177 + function refresh() {
  178 + //check if it's still visible
  179 + if(!$.contains(document.documentElement,this)){
  180 + //stop if it has been removed
  181 + $(this).timeago("dispose");
  182 + return this;
  183 + }
  184 +
  185 + var data = prepareData(this);
  186 + var $s = $t.settings;
  187 +
  188 + if (!isNaN(data.datetime)) {
  189 + if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
  190 + $(this).text(inWords(data.datetime));
  191 + }
  192 + }
  193 + return this;
  194 + }
  195 +
  196 + function prepareData(element) {
  197 + element = $(element);
  198 + if (!element.data("timeago")) {
  199 + element.data("timeago", { datetime: $t.datetime(element) });
  200 + var text = $.trim(element.text());
  201 + if ($t.settings.localeTitle) {
  202 + element.attr("title", element.data('timeago').datetime.toLocaleString());
  203 + } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
  204 + element.attr("title", text);
  205 + }
  206 + }
  207 + return element.data("timeago");
  208 + }
  209 +
  210 + function inWords(date) {
  211 + return $t.inWords(distance(date));
  212 + }
  213 +
  214 + function distance(date) {
  215 + return (new Date().getTime() - date.getTime());
  216 + }
  217 +
  218 + // fix for IE6 suckage
  219 + document.createElement("abbr");
  220 + document.createElement("time");
  221 +}));
... ...
js/jquery.timeago.pt-br.js 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +// Brazilian Portuguese
  2 +jQuery.timeago.settings.strings = {
  3 + prefixAgo: "há",
  4 + prefixFromNow: "em",
  5 + suffixAgo: null,
  6 + suffixFromNow: null,
  7 + seconds: "alguns segundos",
  8 + minute: "um minuto",
  9 + minutes: "%d minutos",
  10 + hour: "uma hora",
  11 + hours: "%d horas",
  12 + day: "um dia",
  13 + days: "%d dias",
  14 + month: "um mês",
  15 + months: "%d meses",
  16 + year: "um ano",
  17 + years: "%d anos"
  18 +};
... ...
js/main.js
... ... @@ -17,7 +17,7 @@ define([&#39;handlebars&#39;, &#39;fastclick&#39;, &#39;handlebars_helpers&#39;], function(Handlebars, F
17 17  
18 18 var loginButton;
19 19  
20   - var participa = true;
  20 + var participa = false;
21 21 if(participa){
22 22 var host = 'http://www.participa.br';
23 23 var private_token = '375bee7e17d0021af7160ce664874618'; //participa
... ... @@ -102,12 +102,13 @@ define([&#39;handlebars&#39;, &#39;fastclick&#39;, &#39;handlebars_helpers&#39;], function(Handlebars, F
102 102 $resultsContainer.find('.loading').show();
103 103 $resultsContainer.find('.results-content').hide();
104 104  
105   - var url = host + '/api/v1/articles/' + topic_id + '/children' + '?private_token=' + private_token + '&limit=10&order=votes_score&fields=id,name,abstract,votes_for,votes_against&content_type=ProposalsDiscussionPlugin::Proposal';
  105 + var url = host + '/api/v1/proposals_discussion_plugin/' + topic_id + '/ranking' + '?private_token=' + private_token + '&limit=10';
106 106 $.getJSON(url).done(function( data ) {
107 107  
108 108 $resultsContainer.html(resultsTemplate(data));
109 109 $resultsContainer.find('.loading').hide();
110 110 $resultsContainer.find('.results-content').show();
  111 + $(".timeago").timeago();
111 112 $resultsContainer.show();
112 113  
113 114 // scroll to the end
... ...
js/requirejs-config.js
... ... @@ -12,6 +12,8 @@ requirejs.config({
12 12 jquery_xdomainrequest: 'jquery.xdomainrequest.min',
13 13 jquery_ui: 'jquery-ui-1.11.4.custom/jquery-ui.min',
14 14 jquery_cookie: 'jquery.cookie',
  15 + jquery_timeago: 'jquery.timeago',
  16 + jquery_timeago_pt: 'jquery.timeago.pt-br',
15 17 handlebars: 'handlebars-v3.0.1',
16 18 handlebars_helpers: 'handlebars-helpers',
17 19 jquery_maxlength: 'jquery.maxlength.min',
... ... @@ -29,6 +31,12 @@ requirejs.config({
29 31 'jquery_xdomainrequest': {
30 32 deps: ['jquery']
31 33 },
  34 + 'jquery_timeago': {
  35 + deps: ['jquery']
  36 + },
  37 + 'jquery_timeago_pt': {
  38 + deps: ['jquery_timeago']
  39 + },
32 40 'jquery_ui': {
33 41 deps: ['jquery']
34 42 },
... ... @@ -54,5 +62,5 @@ requirejs.config({
54 62 }
55 63 });
56 64  
57   -requirejs(['jquery', 'proposal_app', 'jquery_ui', 'jquery_xdomainrequest', 'handlebars_helpers']);
  65 +requirejs(['jquery', 'proposal_app', 'jquery_ui', 'jquery_xdomainrequest', 'jquery_timeago_pt', 'handlebars_helpers']);
58 66 requirejs(['slick', 'fastclick', 'jquery_maxlength', 'layout','main']);
... ...
sass/_proposal_detail.scss
... ... @@ -135,10 +135,18 @@
135 135 display: block;
136 136 float: right;
137 137 }
  138 + .updated-at {
  139 + font-size: 12px;
  140 + color: gray;
  141 + float: right;
  142 + }
138 143 }
139 144 table {
140 145 clear: both;
141 146 margin-top: 20px;
  147 + td {
  148 + padding: 0 5px;
  149 + }
142 150 tbody tr:nth-child(odd) {
143 151 background-color: #e5e5e5;
144 152 }
... ... @@ -146,18 +154,12 @@
146 154 .abstract-text {
147 155 width: 80%;
148 156 }
149   - .votes-for {
  157 + .value {
150 158 width: 10%;
151 159 text-align: center;
152 160 }
153   - .votes-against {
154   - @extend .votes-for;
155   - }
156   - .score {
157   - @extend .votes-for;
158   - }
159 161 .header {
160   - line-height: 32px;
  162 + line-height: 22px;
161 163 font-weight: bold;
162 164 text-align: center;
163 165 color: rgb(68, 68, 68);
... ... @@ -168,11 +170,9 @@
168 170 background-position: center;
169 171 }
170 172 .votes-against {
  173 + @extend .votes-for;
171 174 background-image: url(./images/result-dislike.png);
172 175 }
173   - .score {
174   - background-image: none;
175   - }
176 176 }
177 177  
178 178 .truncate {
... ...