mirror of
https://github.com/aljazceru/CTFd.git
synced 2026-01-30 03:14:24 +01:00
CTFd code push
This commit is contained in:
84
static/admin/css/style.css
Normal file
84
static/admin/css/style.css
Normal file
@@ -0,0 +1,84 @@
|
||||
#submit-key{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chal > h1{
|
||||
text-align: center
|
||||
}
|
||||
|
||||
#chal > form{
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#chal > form > h3,h4{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#chal > form > input{
|
||||
display: none;
|
||||
}
|
||||
|
||||
table{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*Not sure why foundation needs these two...*/
|
||||
.top-bar input{
|
||||
height: auto;
|
||||
padding-top: 0.35rem;
|
||||
padding-bottom: 0.35rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.top-bar .button{
|
||||
padding-top: 0.45rem;
|
||||
padding-bottom: 0.35rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.dropdown{
|
||||
background-color: #333 !important;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown button{
|
||||
padding-top: 0.45rem;
|
||||
padding-bottom: 0.35rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
#challenges button{
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.row h1{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.textbox{
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.chal-tag{
|
||||
margin: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
#score-graph{
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
#keys-pie-graph{
|
||||
width: 400px;
|
||||
max-height: 330px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#categories-pie-graph{
|
||||
width: 600px;
|
||||
float: left;
|
||||
max-height: 330px;
|
||||
}
|
||||
197
static/admin/js/chalboard.js
Normal file
197
static/admin/js/chalboard.js
Normal file
@@ -0,0 +1,197 @@
|
||||
function loadchal(id) {
|
||||
// $('#chal *').show()
|
||||
// $('#chal > h1').hide()
|
||||
obj = $.grep(challenges['game'], function (e) {
|
||||
return e.id == id;
|
||||
})[0]
|
||||
$('#update-challenge .chal-name').val(obj.name)
|
||||
$('#update-challenge .chal-desc').html(obj.description)
|
||||
$('#update-challenge .chal-value').val(obj.value)
|
||||
$('#update-challenge .chal-category').val(obj.category)
|
||||
$('#update-challenge .chal-id').val(obj.id)
|
||||
$('#update-challenge .chal-delete').attr({
|
||||
'href': '/admin/chal/close/' + (id + 1)
|
||||
})
|
||||
$('#update-challenge').foundation('reveal', 'open');
|
||||
|
||||
}
|
||||
|
||||
function submitkey(chal, key) {
|
||||
$.post("/admin/chal/" + chal, {
|
||||
key: key,
|
||||
nonce: $('#nonce').val()
|
||||
}, function (data) {
|
||||
alert(data)
|
||||
})
|
||||
}
|
||||
|
||||
function loadkeys(chal){
|
||||
$.get('/admin/keys/' + chal, function(data){
|
||||
$('#keys-chal').val(chal)
|
||||
keys = $.parseJSON(JSON.stringify(data));
|
||||
keys = keys['keys']
|
||||
$('#current-keys').empty()
|
||||
for(x=0; x<keys.length; x++){
|
||||
$('#current-keys').append($("<input class='current-key' type='text'>").val(keys[x].key))
|
||||
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="0">Static')
|
||||
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="1">Regex')
|
||||
$('#current-keys input[name="key_type['+x+']"][value="'+keys[x].type+'"]').prop('checked',true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updatekeys(){
|
||||
keys = [];
|
||||
vals = [];
|
||||
chal = $('#keys-chal').val()
|
||||
$('.current-key').each(function(){
|
||||
keys.push($(this).val());
|
||||
})
|
||||
$('#current-keys > input[name*="key_type"]:checked').each(function(){
|
||||
vals.push($(this).val());
|
||||
})
|
||||
$.post('/admin/keys/'+chal, {'keys':keys, 'vals':vals, 'nonce': $('#nonce').val()})
|
||||
loadchal(chal)
|
||||
}
|
||||
|
||||
function loadtags(chal){
|
||||
$('#tags-chal').val(chal)
|
||||
$('#current-tags').empty()
|
||||
$('#chal-tags').empty()
|
||||
$.get('/admin/tags/'+chal, function(data){
|
||||
tags = $.parseJSON(JSON.stringify(data))
|
||||
tags = tags['tags']
|
||||
for (var i = 0; i < tags.length; i++) {
|
||||
tag = "<span class='secondary label chal-tag'><span>"+tags[i].tag+"</span><a name='"+tags[i].id+"'' class='delete-tag'>×</a></span>"
|
||||
$('#current-tags').append(tag)
|
||||
};
|
||||
$('.delete-tag').click(function(e){
|
||||
deletetag(e.target.name)
|
||||
$(e.target).parent().remove()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deletetag(tagid){
|
||||
$.post('/admin/tags/'+tagid+'/delete', {'nonce': $('#nonce').val()});
|
||||
}
|
||||
|
||||
function updatetags(){
|
||||
tags = [];
|
||||
chal = $('#tags-chal').val()
|
||||
$('#chal-tags > span > span').each(function(i, e){
|
||||
tags.push($(e).text())
|
||||
});
|
||||
$.post('/admin/tags/'+chal, {'tags':tags, 'nonce': $('#nonce').val()})
|
||||
loadchal(chal)
|
||||
}
|
||||
|
||||
function loadfiles(chal){
|
||||
$('#update-files > form').attr('action', '/admin/files/'+chal)
|
||||
$.get('/admin/files/' + chal, function(data){
|
||||
$('#files-chal').val(chal)
|
||||
files = $.parseJSON(JSON.stringify(data));
|
||||
files = files['files']
|
||||
$('#current-files').empty()
|
||||
for(x=0; x<files.length; x++){
|
||||
filename = files[x].file.split('/')
|
||||
filename = filename[filename.length - 1]
|
||||
$('#current-files').append('<div data-alert class="alert-box info radius">'+filename+'<a href="#" onclick="deletefile('+chal+','+files[x].id+', $(this))" value="'+files[x].id+'" style="float:right;">Delete</a></div>')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deletefile(chal, file, elem){
|
||||
$.post('/admin/files/' + chal,{
|
||||
'nonce': $('#nonce').val(),
|
||||
'method': 'delete',
|
||||
'file': file
|
||||
}, function (data){
|
||||
if (data == "1") {
|
||||
elem.parent().remove()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadchals(){
|
||||
$.post("/admin/chals", {
|
||||
'nonce': $('#nonce').val()
|
||||
}, function (data) {
|
||||
categories = [];
|
||||
challenges = $.parseJSON(JSON.stringify(data));
|
||||
|
||||
|
||||
for (var i = challenges['game'].length - 1; i >= 0; i--) {
|
||||
if ($.inArray(challenges['game'][i].category, categories) == -1) {
|
||||
categories.push(challenges['game'][i].category)
|
||||
$('#challenges').append($('<tr id="' + challenges['game'][i].category.replace(/ /g,"-") + '"><td class="large-2"><h3>' + challenges['game'][i].category + '</h3></td></tr>'))
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = categories.length - 1; i >= 0; i--) {
|
||||
$('#new-challenge select').append('<option value="' + categories[i] + '">' + categories[i] + '</option>');
|
||||
$('#update-challenge select').append('<option value="' + categories[i] + '">' + categories[i] + '</option>');
|
||||
};
|
||||
|
||||
for (var i = 0; i <= challenges['game'].length - 1; i++) {
|
||||
$('#' + challenges['game'][i].category.replace(/ /g,"-")).append($('<button value="' + challenges['game'][i].id + '">' + challenges['game'][i].value + '</button>'));
|
||||
};
|
||||
|
||||
$('#challenges button').click(function (e) {
|
||||
loadchal(this.value);
|
||||
loadkeys(this.value);
|
||||
loadtags(this.value);
|
||||
loadfiles(this.value);
|
||||
});
|
||||
|
||||
$('tr').append('<button class="create-challenge"><i class="fa fa-plus"></i></button>');
|
||||
|
||||
$('.create-challenge').click(function (e) {
|
||||
$('#new-chal-category').val($($(this).siblings()[0]).text().trim())
|
||||
$('#new-chal-title').text($($(this).siblings()[0]).text().trim())
|
||||
$('#new-challenge').foundation('reveal', 'open');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
$('#submit-key').click(function (e) {
|
||||
submitkey($('#chalid').val(), $('#answer').val())
|
||||
});
|
||||
|
||||
$('#submit-keys').click(function (e) {
|
||||
if (confirm('Updating keys. Are you sure?')){
|
||||
updatekeys()
|
||||
}
|
||||
});
|
||||
|
||||
$('#submit-tags').click(function (e) {
|
||||
updatetags()
|
||||
});
|
||||
|
||||
$(".tag-insert").keyup(function (e) {
|
||||
if (e.keyCode == 13) {
|
||||
tag = $('.tag-insert').val()
|
||||
tag = tag.replace(/'/g, '');
|
||||
if (tag.length > 0){
|
||||
tag = "<span class='secondary label chal-tag'><span>"+tag+"</span><a onclick='$(this).parent().remove()'>×</a></span>"
|
||||
$('#chal-tags').append(tag)
|
||||
}
|
||||
$('.tag-insert').val("")
|
||||
}
|
||||
});
|
||||
|
||||
$('.create-category').click(function (e) {
|
||||
$('#new-category').foundation('reveal', 'open');
|
||||
});
|
||||
count = 1
|
||||
$('#create-key').click(function (e) {
|
||||
$('#current-keys').append("<input class='current-key' type='text' placeholder='Blank Key'>");
|
||||
$('#current-keys').append('<input type="radio" name="key_type['+count+']" value="0">Static');
|
||||
$('#current-keys').append('<input type="radio" name="key_type['+count+']" value="1">Regex');
|
||||
count++
|
||||
});
|
||||
|
||||
$(function(){
|
||||
loadchals()
|
||||
})
|
||||
159
static/admin/js/team.js
Normal file
159
static/admin/js/team.js
Normal file
@@ -0,0 +1,159 @@
|
||||
function teamid (){
|
||||
loc = window.location.pathname
|
||||
return loc.substring(loc.lastIndexOf('/')+1, loc.length);
|
||||
}
|
||||
|
||||
|
||||
function cumulativesum (arr) {
|
||||
var result = arr.concat();
|
||||
for (var i = 0; i < arr.length; i++){
|
||||
result[i] = arr.slice(0, i + 1).reduce(function(p, i){ return p + i; });
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function scoregraph () {
|
||||
var times = []
|
||||
var scores = []
|
||||
var teamname = $('#team-id').text()
|
||||
$.get('/admin/solves/'+teamid(), function( data ) {
|
||||
solves = $.parseJSON(JSON.stringify(data));
|
||||
solves = solves['solves']
|
||||
|
||||
if (solves.length == 0)
|
||||
return
|
||||
|
||||
for (var i = 0; i < solves.length; i++) {
|
||||
times.push(solves[i].time * 1000)
|
||||
scores.push(solves[i].value)
|
||||
};
|
||||
scores = cumulativesum(scores)
|
||||
|
||||
times.unshift('x1')
|
||||
times.push( Math.round(new Date().getTime()) )
|
||||
|
||||
scores.unshift('data1')
|
||||
scores.push( scores[scores.length-1] )
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: "#score-graph",
|
||||
data: {
|
||||
xs: {
|
||||
"data1": 'x1',
|
||||
},
|
||||
columns: [
|
||||
times,
|
||||
scores,
|
||||
],
|
||||
type: "area",
|
||||
labels: true,
|
||||
names : {
|
||||
data1: teamname
|
||||
}
|
||||
},
|
||||
axis : {
|
||||
x : {
|
||||
tick: {
|
||||
format: function (x) {
|
||||
return moment(x).local().format('LLL');
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
y:{
|
||||
label: {
|
||||
text: 'Score'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function adjust_times () {
|
||||
$.each($(".solve-time"), function(i, e){
|
||||
$(e).text( moment(parseInt(e.innerText)).local().format('LLL') )
|
||||
})
|
||||
$(".solve-time").css('color', "#222")
|
||||
}
|
||||
|
||||
|
||||
function keys_percentage_graph(){
|
||||
// Solves and Fails pie chart
|
||||
$.get('/admin/fails/'+teamid(), function(data){
|
||||
res = $.parseJSON(JSON.stringify(data));
|
||||
solves = res['solves']
|
||||
fails = res['fails']
|
||||
total = solves+fails
|
||||
|
||||
if (total == 0)
|
||||
return
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: '#keys-pie-graph',
|
||||
data: {
|
||||
columns: [
|
||||
['Solves', solves],
|
||||
['Fails', fails],
|
||||
],
|
||||
type : 'donut'
|
||||
},
|
||||
color: {
|
||||
pattern: ["#00D140", "#CF2600"]
|
||||
},
|
||||
donut: {
|
||||
title: "Solves vs Fails",
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function category_breakdown_graph(){
|
||||
$.get('/admin/solves/'+teamid(), function(data){
|
||||
solves = $.parseJSON(JSON.stringify(data));
|
||||
solves = solves['solves']
|
||||
|
||||
if (solves.length == 0)
|
||||
return
|
||||
|
||||
categories = []
|
||||
for (var i = 0; i < solves.length; i++) {
|
||||
categories.push(solves[i].category)
|
||||
};
|
||||
|
||||
keys = categories.filter(function(elem, pos) {
|
||||
return categories.indexOf(elem) == pos;
|
||||
})
|
||||
|
||||
data = []
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
temp = []
|
||||
count = 0
|
||||
for (var x = 0; x < categories.length; x++) {
|
||||
if (categories[x] == keys[i]){
|
||||
count++
|
||||
}
|
||||
};
|
||||
temp.push(keys[i])
|
||||
temp.push(count)
|
||||
data.push(temp)
|
||||
};
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: '#categories-pie-graph',
|
||||
data: {
|
||||
columns: data,
|
||||
type : 'donut',
|
||||
labels: true
|
||||
},
|
||||
donut: {
|
||||
title: "Category Breakdown"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
category_breakdown_graph()
|
||||
keys_percentage_graph()
|
||||
adjust_times()
|
||||
scoregraph()
|
||||
105
static/css/style.css
Normal file
105
static/css/style.css
Normal file
@@ -0,0 +1,105 @@
|
||||
#chal > form{
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.reveal-modal{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chal-desc{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*Not sure why foundation needs these two...*/
|
||||
.top-bar input{
|
||||
height: auto;
|
||||
padding-top: 0.35rem;
|
||||
padding-bottom: 0.35rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.top-bar .button{
|
||||
padding-top: 0.45rem;
|
||||
padding-bottom: 0.35rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.dropdown{
|
||||
background-color: #333 !important;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown button{
|
||||
padding-top: 0.45rem;
|
||||
padding-bottom: 0.35rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
#challenges button{
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.row > h1{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#challenges{
|
||||
line-height: 66px;
|
||||
}
|
||||
|
||||
#score-graph{
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.dropdown{
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dropdown input{
|
||||
margin: 5px auto;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.dropdown button{
|
||||
margin: 10px auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#keys-pie-graph{
|
||||
width: 50%;
|
||||
max-height: 330px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#categories-pie-graph{
|
||||
width: 50%;
|
||||
float: left;
|
||||
max-height: 330px;
|
||||
}
|
||||
|
||||
.logo{
|
||||
margin: 0 auto;
|
||||
width: 500px;
|
||||
padding: 50px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 40.063em){
|
||||
.top-bar .dropdown{
|
||||
display: block;
|
||||
padding: 0 15px 5px;
|
||||
width: 200% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar input{
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
BIN
static/img/logo.png
Normal file
BIN
static/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
244
static/js/chalboard.js
Normal file
244
static/js/chalboard.js
Normal file
@@ -0,0 +1,244 @@
|
||||
//http://stackoverflow.com/a/2648463 - wizardry!
|
||||
String.prototype.format = String.prototype.f = function() {
|
||||
var s = this,
|
||||
i = arguments.length;
|
||||
|
||||
while (i--) {
|
||||
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
var challenges;
|
||||
|
||||
function loadchal(id) {
|
||||
obj = $.grep(challenges['game'], function (e) {
|
||||
return e.id == id;
|
||||
})[0]
|
||||
window.location.hash = obj.name
|
||||
$('#chal-window .chal-name').text(obj.name)
|
||||
$('#chal-window .chal-desc').html(marked(obj.description, {'gfm':true, 'breaks':true}))
|
||||
|
||||
for (var i = 0; i < obj.files.length; i++) {
|
||||
filename = obj.files[i].split('/')
|
||||
filename = filename[filename.length - 1]
|
||||
$('#chal-window .chal-desc').append("<a href='"+obj.files[i]+"'>"+filename+"</a><br/>")
|
||||
};
|
||||
|
||||
$('#chal-window .chal-value').text(obj.value)
|
||||
$('#chal-window .chal-category').text(obj.category)
|
||||
$('#chal-window #chal-id').val(obj.id)
|
||||
$('#chal-window .chal-solves').text(obj.solves + " solves")
|
||||
$('#answer').val("")
|
||||
|
||||
$('pre code').each(function(i, block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
$('#chal-window').foundation('reveal', 'open');
|
||||
}
|
||||
|
||||
function loadchalbyname(chalname) {
|
||||
obj = $.grep(challenges['game'], function (e) {
|
||||
return e.name == chalname;
|
||||
})[0]
|
||||
window.location.hash = obj.name
|
||||
$('#chal-window .chal-name').text(obj.name)
|
||||
$('#chal-window .chal-desc').html(marked(obj.description, {'gfm':true, 'breaks':true}))
|
||||
|
||||
for (var i = 0; i < obj.files.length; i++) {
|
||||
filename = obj.files[i].split('/')
|
||||
filename = filename[filename.length - 1]
|
||||
$('#chal-window .chal-desc').append("<a href='"+obj.files[i]+"'>"+filename+"</a><br/>")
|
||||
};
|
||||
|
||||
$('#chal-window .chal-value').text(obj.value)
|
||||
$('#chal-window .chal-category').text(obj.category)
|
||||
$('#chal-window #chal-id').val(obj.id)
|
||||
$('#chal-window .chal-solves').text(obj.solves + " solves")
|
||||
$('#answer').val("")
|
||||
|
||||
$('pre code').each(function(i, block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
|
||||
$('#chal-window').foundation('reveal', 'open');
|
||||
}
|
||||
|
||||
|
||||
$("#answer").keyup(function(event){
|
||||
if(event.keyCode == 13){
|
||||
$("#submit-key").click();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function submitkey(chal, key, nonce) {
|
||||
$.post("/chal/" + chal, {
|
||||
key: key,
|
||||
nonce: nonce
|
||||
}, function (data) {
|
||||
if (data == -1){
|
||||
window.location="/login"
|
||||
}
|
||||
else if (data == 0){ // Incorrect key
|
||||
$('#submit-key').text('Incorrect, sorry')
|
||||
$('#submit-key').css('background-color', 'red')
|
||||
$('#submit-key').prop('disabled', true)
|
||||
}
|
||||
else if (data == 1){ // Challenge Solved
|
||||
$('#submit-key').text('Correct!')
|
||||
$('#submit-key').css('background-color', 'green')
|
||||
$('#submit-key').prop('disabled', true)
|
||||
$('#chal-window .chal-solves').text( (parseInt($('#chal-window .chal-solves').text().split(" ")[0]) + 1 + " solves") )
|
||||
}
|
||||
else if (data == 2){ // Challenge already solved
|
||||
$('#submit-key').text('You already solved this')
|
||||
$('#submit-key').prop('disabled', true)
|
||||
}
|
||||
else if (data == 3){ // Keys per minute too high
|
||||
$('#submit-key').text("You're submitting keys too fast. Slow down.")
|
||||
$('#submit-key').css('background-color', '#e18728')
|
||||
$('#submit-key').prop('disabled', true)
|
||||
}
|
||||
marksolves()
|
||||
updatesolves()
|
||||
setTimeout(function(){
|
||||
$('#submit-key').text('Submit')
|
||||
$('#submit-key').prop('disabled', false)
|
||||
$('#submit-key').css('background-color', '#007095')
|
||||
}, 3000);
|
||||
})
|
||||
}
|
||||
|
||||
function marksolves() {
|
||||
$.get('/solves', function (data) {
|
||||
solves = $.parseJSON(JSON.stringify(data));
|
||||
for (var i = solves['solves'].length - 1; i >= 0; i--) {
|
||||
id = solves['solves'][i].chalid
|
||||
$('#challenges button[value="' + id + '"]').addClass('secondary')
|
||||
$('#challenges button[value="' + id + '"]').css('opacity', '0.3')
|
||||
};
|
||||
if (window.location.hash.length > 0){
|
||||
loadchalbyname(window.location.hash.substring(1))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updatesolves(){
|
||||
$.get('/chals/solves', function (data) {
|
||||
solves = $.parseJSON(JSON.stringify(data));
|
||||
chals = Object.keys(solves);
|
||||
|
||||
for (var i = 0; i < chals.length; i++) {
|
||||
obj = $.grep(challenges['game'], function (e) {
|
||||
return e.name == chals[i];
|
||||
})[0]
|
||||
obj.solves = solves[chals[i]]
|
||||
};
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function getsolves(id){
|
||||
$.get('/chal/'+id+'/solves', function (data) {
|
||||
var teams = data['teams'];
|
||||
var box = $('#chal-solves-names');
|
||||
box.empty();
|
||||
for (var i = 0; i < teams.length; i++) {
|
||||
var id = teams[i].id;
|
||||
var name = teams[i].name;
|
||||
var date = moment(teams[i].date).local().format('LLL');
|
||||
box.append('<tr><td><a href="/team/{0}">{1}</td><td>{2}</td></tr>'.format(id, name, date));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function loadchals() {
|
||||
|
||||
$.get("/chals", function (data) {
|
||||
categories = [];
|
||||
challenges = $.parseJSON(JSON.stringify(data));
|
||||
|
||||
|
||||
for (var i = challenges['game'].length - 1; i >= 0; i--) {
|
||||
challenges['game'][i].solves = 0
|
||||
if ($.inArray(challenges['game'][i].category, categories) == -1) {
|
||||
categories.push(challenges['game'][i].category)
|
||||
$('#challenges').append($('<tr id="' + challenges['game'][i].category.replace(/ /g,"-") + '"><td class="large-2"><h4>' + challenges['game'][i].category + '</h4></td></tr>'))
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i <= challenges['game'].length - 1; i++) {
|
||||
$('#' + challenges['game'][i].category.replace(/ /g,"-")).append($('<button value="' + challenges['game'][i].id + '">' + challenges['game'][i].value + '</button>'));
|
||||
};
|
||||
updatesolves()
|
||||
marksolves()
|
||||
|
||||
$('#challenges button').click(function (e) {
|
||||
loadchal(this.value);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
$('#submit-key').click(function (e) {
|
||||
submitkey($('#chal-id').val(), $('#answer').val(), $('#nonce').val())
|
||||
});
|
||||
|
||||
$('.chal-solves').click(function (e) {
|
||||
getsolves($('#chal-id').val())
|
||||
});
|
||||
|
||||
// $.distint(array)
|
||||
// Unique elements in array
|
||||
$.extend({
|
||||
distinct : function(anArray) {
|
||||
var result = [];
|
||||
$.each(anArray, function(i,v){
|
||||
if ($.inArray(v, result) == -1) result.push(v);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
});
|
||||
function colorhash (x) {
|
||||
color = ""
|
||||
for (var i = 20; i <= 60; i+=20){
|
||||
x += i
|
||||
x *= i
|
||||
color += x.toString(16)
|
||||
};
|
||||
return "#" + color.substring(0, 6)
|
||||
}
|
||||
|
||||
$(document).on('close', '[data-reveal]', function () {
|
||||
window.location.hash = ""
|
||||
});
|
||||
|
||||
// function solves_graph() {
|
||||
// $.get('/graphs/solves', function(data){
|
||||
// solves = $.parseJSON(JSON.stringify(data));
|
||||
// chals = []
|
||||
// counts = []
|
||||
// colors = []
|
||||
// i = 1
|
||||
// $.each(solves, function(key, value){
|
||||
// chals.push(key)
|
||||
// counts.push(value)
|
||||
// colors.push(colorhash(i++))
|
||||
// });
|
||||
|
||||
// });
|
||||
// }
|
||||
|
||||
function update(){
|
||||
$('#challenges').empty()
|
||||
loadchals()
|
||||
solves_graph()
|
||||
}
|
||||
|
||||
$(function() {
|
||||
loadchals()
|
||||
// solves_graph()
|
||||
});
|
||||
|
||||
setInterval(update, 300000);
|
||||
109
static/js/scoreboard.js
Normal file
109
static/js/scoreboard.js
Normal file
@@ -0,0 +1,109 @@
|
||||
//http://stackoverflow.com/a/2648463 - wizardry!
|
||||
String.prototype.format = String.prototype.f = function() {
|
||||
var s = this,
|
||||
i = arguments.length;
|
||||
|
||||
while (i--) {
|
||||
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
function updatescores () {
|
||||
$.get('/scores', function( data ) {
|
||||
teams = $.parseJSON(JSON.stringify(data));
|
||||
$('#scoreboard > tbody').empty()
|
||||
for (var i = 0; i < teams['teams'].length; i++) {
|
||||
row = "<tr><td>{0}</td><td><a href='/team/{1}'>{2}</a></td><td>{3}</td></tr>".format(i+1, teams['teams'][i].id, teams['teams'][i].name, teams['teams'][i].score)
|
||||
$('#scoreboard > tbody').append(row)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function cumulativesum (arr) {
|
||||
var result = arr.concat();
|
||||
for (var i = 0; i < arr.length; i++){
|
||||
result[i] = arr.slice(0, i + 1).reduce(function(p, i){ return p + i; });
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function UTCtoDate(utc){
|
||||
var d = new Date(0)
|
||||
d.setUTCSeconds(utc)
|
||||
return d;
|
||||
}
|
||||
|
||||
function scoregraph () {
|
||||
var times = []
|
||||
var scores = []
|
||||
$.get('/top/10', function( data ) {
|
||||
scores = $.parseJSON(JSON.stringify(data));
|
||||
scores = scores['scores']
|
||||
if (Object.keys(scores).length == 0 ){
|
||||
return;
|
||||
}
|
||||
$('#score-graph').show()
|
||||
teams = Object.keys(scores)
|
||||
|
||||
xs_data = {}
|
||||
column_data = []
|
||||
for (var i = 0; i < teams.length; i++) {
|
||||
times = []
|
||||
team_scores = []
|
||||
for (var x = 0; x < scores[teams[i]].length; x++) {
|
||||
times.push(scores[teams[i]][x].time)
|
||||
team_scores.push(scores[teams[i]][x].value)
|
||||
};
|
||||
team_scores = cumulativesum(team_scores)
|
||||
|
||||
times.unshift("x"+i)
|
||||
times.push( Math.round(new Date().getTime()/1000) )
|
||||
|
||||
team_scores.unshift(teams[i])
|
||||
team_scores.push( team_scores[team_scores.length-1] )
|
||||
|
||||
|
||||
xs_data[teams[i]] = "x"+i
|
||||
column_data.push(times)
|
||||
column_data.push(team_scores)
|
||||
|
||||
};
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: "#score-graph",
|
||||
data: {
|
||||
xs: xs_data,
|
||||
columns: column_data,
|
||||
type: "step",
|
||||
labels: true
|
||||
},
|
||||
axis : {
|
||||
x : {
|
||||
tick: {
|
||||
format: function (x) {
|
||||
return moment(x*1000).local().format('LLL');
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
y:{
|
||||
label: {
|
||||
text: 'Score'
|
||||
}
|
||||
}
|
||||
},
|
||||
// zoom : {
|
||||
// enabled: true
|
||||
// }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function update(){
|
||||
updatescores()
|
||||
scoregraph()
|
||||
}
|
||||
|
||||
setInterval(update, 300000); // Update scores every 5 minutes
|
||||
scoregraph()
|
||||
168
static/js/team.js
Normal file
168
static/js/team.js
Normal file
@@ -0,0 +1,168 @@
|
||||
function teamid (){
|
||||
loc = window.location.pathname
|
||||
return loc.substring(loc.lastIndexOf('/')+1, loc.length);
|
||||
}
|
||||
|
||||
function colorhash (x) {
|
||||
color = ""
|
||||
for (var i = 20; i <= 60; i+=20){
|
||||
x += i
|
||||
x *= i
|
||||
color += x.toString(16)
|
||||
};
|
||||
return "#" + color.substring(0, 6)
|
||||
}
|
||||
|
||||
function cumulativesum (arr) {
|
||||
var result = arr.concat();
|
||||
for (var i = 0; i < arr.length; i++){
|
||||
result[i] = arr.slice(0, i + 1).reduce(function(p, i){ return p + i; });
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function scoregraph () {
|
||||
var times = []
|
||||
var scores = []
|
||||
var teamname = $('#team-id').text()
|
||||
$.get('/solves/'+teamid(), function( data ) {
|
||||
solves = $.parseJSON(JSON.stringify(data));
|
||||
solves = solves['solves']
|
||||
|
||||
console.log(solves)
|
||||
|
||||
if (solves.length == 0)
|
||||
return
|
||||
|
||||
for (var i = 0; i < solves.length; i++) {
|
||||
times.push(solves[i].time * 1000)
|
||||
scores.push(solves[i].value)
|
||||
};
|
||||
scores = cumulativesum(scores)
|
||||
|
||||
times.unshift('x1')
|
||||
// times.push( Math.round(new Date().getTime()) )
|
||||
|
||||
scores.unshift('data1')
|
||||
// scores.push( scores[scores.length-1] )
|
||||
|
||||
console.log(scores)
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: "#score-graph",
|
||||
data: {
|
||||
xs: {
|
||||
"data1": 'x1',
|
||||
},
|
||||
columns: [
|
||||
times,
|
||||
scores,
|
||||
],
|
||||
type: "area-step",
|
||||
colors: {
|
||||
data1: colorhash(teamid()),
|
||||
},
|
||||
labels: true,
|
||||
names : {
|
||||
data1: teamname
|
||||
}
|
||||
},
|
||||
axis : {
|
||||
x : {
|
||||
tick: {
|
||||
format: function (x) {
|
||||
return moment(x).local().format('M/D h:mm:ss');
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
y:{
|
||||
label: {
|
||||
text: 'Score'
|
||||
}
|
||||
}
|
||||
},
|
||||
zoom : {
|
||||
enabled: true
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function keys_percentage_graph(){
|
||||
// Solves and Fails pie chart
|
||||
$.get('/fails/'+teamid(), function(data){
|
||||
res = $.parseJSON(JSON.stringify(data));
|
||||
solves = res['solves']
|
||||
fails = res['fails']
|
||||
total = solves+fails
|
||||
|
||||
if (total == 0)
|
||||
return
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: '#keys-pie-graph',
|
||||
data: {
|
||||
columns: [
|
||||
['Solves', solves],
|
||||
['Fails', fails],
|
||||
],
|
||||
type : 'donut'
|
||||
},
|
||||
color: {
|
||||
pattern: ["#00D140", "#CF2600"]
|
||||
},
|
||||
donut: {
|
||||
title: "Solves vs Fails",
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function category_breakdown_graph(){
|
||||
$.get('/solves/'+teamid(), function(data){
|
||||
solves = $.parseJSON(JSON.stringify(data));
|
||||
solves = solves['solves']
|
||||
if (solves.length == 0)
|
||||
return
|
||||
|
||||
categories = []
|
||||
for (var i = 0; i < solves.length; i++) {
|
||||
categories.push(solves[i].category)
|
||||
};
|
||||
|
||||
keys = categories.filter(function(elem, pos) {
|
||||
return categories.indexOf(elem) == pos;
|
||||
})
|
||||
|
||||
data = []
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
temp = []
|
||||
count = 0
|
||||
for (var x = 0; x < categories.length; x++) {
|
||||
if (categories[x] == keys[i]){
|
||||
count++
|
||||
}
|
||||
};
|
||||
temp.push(keys[i])
|
||||
temp.push(count)
|
||||
data.push(temp)
|
||||
};
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: '#categories-pie-graph',
|
||||
data: {
|
||||
columns: data,
|
||||
type : 'donut',
|
||||
labels: true
|
||||
},
|
||||
donut: {
|
||||
title: "Category Breakdown",
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
category_breakdown_graph()
|
||||
keys_percentage_graph()
|
||||
scoregraph()
|
||||
0
static/uploads/index.html
Normal file
0
static/uploads/index.html
Normal file
Reference in New Issue
Block a user