TONS of work.

* Got stats page working
* Got main LevelUp page working
* A lot of stat processing stuff
This commit is contained in:
2015-11-05 06:55:49 -06:00
parent 083f9f3901
commit 278d409ad9
13 changed files with 1000 additions and 28 deletions

File diff suppressed because one or more lines are too long

View File

@@ -211,6 +211,60 @@ aside {
background: #1f8dd6;
}
.hidden {
display: none;
}
.bordered {
border: 1px solid #888;
border-radius: 3px;
}
.levelup-user {
border-radius: 3px;
border: 1px solid #888;
margin: 10px;
overflow: hidden;
height: 75px;
background: #AAA;
padding: 5px;
position: relative;
}
.levelup-username {
font-size: 2em;
color: #FFF;
background: #777;
border-radius: 3px;
border-top: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid white;
border-bottom: 1px solid white;
padding: 5px;
position: absolute;
top: 5%;
right: 1%;
}
.levelup-level {
font-size: 7em;
margin-right: 5em;
color: #000;
position: absolute;
left: 1%;
top: 35%;
color: #FFF;
}
.levelup-smalllevel {
position: absolute;
bottom: 20%;
right: 1%;
color: #000;
}
.levelup-xp {
position: absolute;
bottom: 1%;
right: 1%;
}
/* -- Responsive Styles (Media Queries) ------------------------------------- */
/*

276
assets/js/B.js Normal file
View File

@@ -0,0 +1,276 @@
function B(els, attrs) {
// Turn 'this' into an array of passed in elements.
function B(els) {
if(typeof els === "string") {
els = this.brb.create(els);
}
for(var i = 0; i < els.length; i++) {
this[i] = els[i];
}
this.length = els.length;
// return this;
}
// Map a function to all elements in 'this'
B.prototype.map = function(callback) {
var results = [], i = 0;
for(;i<this.length;i++){
results.push(callback.call(this,this[i],i));
}
return results;
};
// Foreach through all elements in 'this'
B.prototype.forEach = function(callback) {
this.map(callback);
return this;
};
// Map a function to the first element in 'this'
B.prototype.mapOne = function(callback) {
var m = this.map(callback);
return m.length > 1 ? m[0] : m;
};
// Update css for each element in 'this'
B.prototype.css = function(css_opt_var, css_opt_val) {
if(typeof css_opt_var !== "string") {
for(css_var in css_opt_var) {
this.forEach(function(el){el.style[css_var]=css_opt_var[css_var];});
}
return this;
} else {
if(typeof css_opt_val !== "undefined") {
return this.forEach(function(el){el.style[css_opt_var]=css_opt_val;});
} else {
return this.mapOne(function(el){return el.style[css_opt_var];});
}
}
};
// Update the innerText for each element in 'this'
B.prototype.html = function(text) {
if(typeof text !== "undefined") {
return this.forEach(function(el){el.innerHTML=text;});
} else {
return this.mapOne(function(el){return el.innerHTML;});
}
};
// Check if the first element has the requested class
B.prototype.hasClass = function(cls) {
return this.mapOne(function(el){
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
});
}
// Add a class to each element in 'this'
B.prototype.addClass = function(classes) {
var className = "";
if(typeof classes !== "string") {
for(var i=0;i<classes.length;i++) {
className+=" "+classes[i];
}
} else {
className=" "+classes;
}
return this.forEach(function(el){el.className+=className;});
};
// Remove a class from each element in 'this'
B.prototype.removeClass = function(remove_class) {
return this.forEach(function(el){
var cs = el.className.split(" "), i;
while((i=cs.indexOf(remove_class))>-1){
cs = cs.slice(0,i).concat(cs.slice(++i));
}
el.className=cs.join(" ");
});
};
// Set an attribute for each element in 'this'
B.prototype.attr = function(attr,val){
if(typeof val!=="undefined"){
if(this[0].tagName=="INPUT" && attr.toUpperCase()=="VALUE") {
// If we're setting the 'VALUE' then it's actually .value
return this.forEach(function(el){
el.value=val;
});
} else {
// Otherwise use .setAttribute
return this.forEach(function(el){
el.setAttribute(attr,val);
});
}
} else {
// And getting the value
if(this[0].tagName=="INPUT" && attr.toUpperCase()=="VALUE") {
return this.mapOne(function(el){
return el.value;
});
} else {
return this.mapOne(function(el){
return el.getAttribute(attr);
});
}
}
};
// Actually set a value on each element (can be done with attr too.)
B.prototype.val = function(new_val) {
if(typeof new_val!=="undefined"){
return this.forEach(function(el){
el.value = new_val;
});
} else {
// Just retrieve the value for the first element
return this.mapOne(function(el) {
return el.value;
});
}
}
// Append an element to the DOM after each element in 'this'
B.prototype.append = function(els) {
this.forEach(function(parEl, i) {
els.forEach(function(childEl) {
if(i>0) {
childEl=childEl.cloneNode(true);
}
parEl.appendChild(childEl);
});
});
};
// Prepend an element to the DOM before each element in 'this'
B.prototype.prepend = function(els) {
return this.forEach(function(parEl, i) {
for(var j = els.length-1; j>-1; j--) {
childEl=(i>0)?els[j].cloneNode(true):els[j];
parEl.insertBefore(childEl, parEl.firstChild);
}
});
};
// Remove all elements in 'this' from the DOM
B.prototype.remove = function() {
return this.forEach(function(el){
return el.parentNode.removeChild(el);
});
};
// Find children that match selector
B.prototype.children = function(selector) {
var results = [];
this.forEach(function(el) {
var sub_r = el.querySelectorAll(selector);
for(var i = 0; i < sub_r.length; i++) {
results.push(sub_r[i]);
}
});
return results;
}
B.prototype.firstChild = function(selector) {
return this.children(selector)[0];
}
// Add an event listener to each element in 'this'
B.prototype.on = (function(){
// Browser compatibility...
if(document.addEventListener) {
return function(evt,fn) {
return this.forEach(function(el){
el.addEventListener(evt, fn, false);
});
};
} else if(document.attachEvent) {
return function(evt,fn) {
return this.forEach(function(el){
el.attachEvent("on"+evt,fn);
});
};
} else {
return function(evt, fn) {
return this.forEach(function(el){
el["on"+evt]=fn;
});
};
}
}());
// Disable event listeners on elements in 'this'
B.prototype.off = (function(){
// Browser compatibility...
if(document.removeEventListener) {
return function(evt, fn) {
return this.forEach(function(el) {
el.removeEventListener(evt, fn, false);
});
};
} else if(document.detachEvent) {
return function(evt, fn) {
return this.forEach(function(el) {
el.detachEvent("on"+evt, fn);
});
};
} else {
return function(evt, fn) {
return this.forEach(function(el){
el["on"+evt]=null;
});
};
}
}());
// The actual framework object, yay!
var brb = {
// Get an element
get: function(selector) {
var els;
if(typeof selector === "string") {
els = document.querySelectorAll(selector);
} else if(selector.length) {
els = selector;
} else {
els = [selector];
}
return new B(els);
},
// Create a new element
create: function(tagName, attrs) {
var el = new B([document.createElement(tagName)]);
// Set attributes on new element
if(attrs) {
if(attrs.className) {
// Classes
el.addClass(attrs.className);
delete attrs.classname;
}
if(attrs.text) {
// Text
el.text(attrs.text);
delete attrs.text;
}
for(var key in attrs) {
// All other Attributes
if(attrs.hasOwnProperty(key)) {
el.attr(key, attrs[key]);
}
}
}
return el;
}
};
if(els.match) {
var match_tags = els.match(/<([^>\s\/]*)\s?\/?>/);
}
if(match_tags && match_tags.length > 0) {
// It's a 'create tag' command
return brb.create(match_tags[1], attrs);
} else {
// Just search for matches
return brb.get(els);
}
};
// And there is our minimalist framework. Who needs jquery?

101
assets/js/levelup_main.js Normal file
View File

@@ -0,0 +1,101 @@
document.addEventListener("DOMContentLoaded", function(event) {
sortByAlpha();
buildList();
B('#btnAlphaSort').on('click', function() {
if(sortMode == "ALPHA") {
sortMode = "ALPHA-REV";
B('#btnAlphaSort>i').removeClass('fa-sort-asc').addClass('fa-sort-desc');
} else {
sortMode = "ALPHA";
B('#btnAlphaSort>i').removeClass('fa-sort-desc').addClass('fa-sort-asc');
}
sortUsers();
buildList();
});
B('#btnXpSort').on('click', function() {
if(sortMode == "XP") {
sortMode = "XP-REV";
B('#btnXpSort>i').removeClass('fa-sort-desc').addClass('fa-sort-asc');
} else {
sortMode = "XP";
B('#btnXpSort>i').removeClass('fa-sort-asc').addClass('fa-sort-desc');
}
sortUsers();
buildList();
});
})
var sortMode = "ALPHA";
function clearList() {
B(".levelup-user").remove();
}
function sortUsers() {
switch(sortMode) {
case "ALPHA-REV": sortByAlphaRev(); break;
case "XP": sortByXP(); break;
case "XP-REV": sortByXPRev(); break;
default: sortByAlpha(); break;
}
}
function sortByXP() {
levelUpStats.users.sort(function(a, b){
if(a.xp > b.xp) { return -1; }
if(a.xp < b.xp) { return 1; }
return 0;
});
}
function sortByXPRev() {
levelUpStats.users.sort(function(a, b){
if(a.xp > b.xp) { return 1; }
if(a.xp < b.xp) { return -1; }
return 0;
});
}
function sortByAlpha() {
levelUpStats.users.sort(function(a, b){
if(a.name > b.name) { return 1; }
if(a.name < b.name) { return -1; }
return 0;
});
}
function sortByAlphaRev() {
levelUpStats.users.sort(function(a, b){
if(a.name > b.name) { return -1; }
if(a.name < b.name) { return 1; }
return 0;
});
}
function buildList() {
clearList();
for(var i = 0; i < levelUpStats.users.length; i++) {
// Figure out the users current level & percentage through it.
var xp = levelUpStats.users[i].xp,
toNext = 100,
tstLvl = 1;
while(xp - toNext > 0) {
xp -= toNext;
tstLvl++;
toNext = tstLvl*100;
}
var donePct = (xp / toNext)*100,
userLevel = tstLvl,
userName = levelUpStats.users[i].name,
idName = userName.replace('.','_');
B("#userXpList").append(B('<div>').attr('id',"levelup-user-"+idName).addClass('levelup-user pure-u-1 pure-u-md-1-3').attr('data-username',userName));
var userNameSpan = B("<span>").attr('id','levelup-user-'+idName+'-name').addClass('levelup-username');
B("#levelup-user-"+idName).append(B("<div>").attr('id','levelup-user-'+idName+'-level').addClass('levelup-level').html(userLevel));
B("#levelup-user-"+idName).append(userNameSpan.html(userName));
B("#levelup-user-"+idName).append(B("<div>").attr('id','levelup-user-'+idName+'-smalllevel').addClass('levelup-smalllevel').html("Level "+userLevel));
B("#levelup-user-"+idName).append(B("<div>").attr('id','levelup-user-'+idName+'-xp').addClass('levelup-xp').html("("+xp+"/"+toNext+")"));
B('.levelup-user').on('click', function() {
console.log("Load User Profile: "+B(this).attr('data-username'));
});
}
}

View File

@@ -1,11 +1,52 @@
document.addEventListener("DOMContentLoaded", function(event) {
B("#btnChannelStats").on('click', function(e) {
B(this).addClass('pure-button-disabled');
B('#btnMessageStats').removeClass('pure-button-disabled');
B('#btnUserStats').removeClass('pure-button-disabled');
B('#channelStats').removeClass('hidden');
B('#userStats').addClass('hidden');
B('#messageStats').addClass('hidden');
});
B('#btnMessageStats').on('click', function(e) {
B(this).addClass('pure-button-disabled');
B('#btnChannelStats').removeClass('pure-button-disabled');
B('#channelStats').addClass('hidden');
B('#btnUserStats').removeClass('pure-button-disabled');
B('#userStats').addClass('hidden');
B('#messageStats').removeClass('hidden');
});
B('#btnUserStats').on('click', function(e) {
B(this).addClass('pure-button-disabled');
B('#btnChannelStats').removeClass('pure-button-disabled');
B('#channelStats').addClass('hidden');
B('#btnMessageStats').removeClass('pure-button-disabled');
B('#messageStats').addClass('hidden');
B('#userStats').removeClass('hidden');
});
B('#btnUsrHrlyStats').on('click', function(e) {
B(this).addClass('pure-button-disabled');
B('#btnUsrDowStats').removeClass('pure-button-disabled');
B('#userHourlyStats').removeClass('hidden');
B('#userDowStats').addClass('hidden');
});
B('#btnUsrDowStats').on('click', function(e) {
B(this).addClass('pure-button-disabled');
B('#btnUsrHrlyStats').removeClass('pure-button-disabled');
B('#userHourlyStats').addClass('hidden');
B('#userDowStats').removeClass('hidden');
});
var main_channel_names = ['general','random'],
main_member_numbers = [],
main_message_numbers = [],
other_channel_names = [],
other_member_numbers = [],
other_message_numbers = [];
for(var i = 0; i < stats.channels.length; i++){
for(var i = 0; i < stats.channels.length; i++) {
if(stats.channels[i].name == "general") {
main_member_numbers[0] = stats.channels[i].member_count;
main_message_numbers[0] = stats.channels[i].message_count;
@@ -101,13 +142,212 @@ document.addEventListener("DOMContentLoaded", function(event) {
name: 'Messages',
color: 'rgba(126,86,134,0.9)',
data: other_message_numbers,
pointPadding: 0.3
pointPadding: 0.0
},{
name: 'Members',
color: 'rgba(165,170,217,1)',
data: other_member_numbers,
pointPadding: 0.4,
pointPadding: 0.2,
yAxis: 1
}]
});
var msg1 = [{name: 'Messages', data: []}];
for(var i = 0; i < 24; i++) {
msg1[0].data.push(stats.messages.hours[i]);
}
var msgHourlyChart = new Highcharts.Chart({
chart: {
renderTo: 'msgHourlyStats'
},
title: {
text: 'Hourly Stats'
},
xAxis: {
categories: [
'00:00', '01:00', '02:00', '03:00', '04:00', '05:00',
'06:00', '07:00', '08:00', '09:00', '10:00', '11:00',
'12:00', '13:00', '14:00', '15:00', '16:00', '17:00',
'18:00', '19:00', '20:00', '21:00', '22:00', '23:00'
]
},
yAxis: {
title: { text: '' },
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
series: msg1
});
var msg2 = [{name: 'Messages', data: []}];
msg2[0].data.push(stats.messages.dow['Sun']);
msg2[0].data.push(stats.messages.dow['Mon']);
msg2[0].data.push(stats.messages.dow['Tue']);
msg2[0].data.push(stats.messages.dow['Wed']);
msg2[0].data.push(stats.messages.dow['Thu']);
msg2[0].data.push(stats.messages.dow['Fri']);
msg2[0].data.push(stats.messages.dow['Sat']);
var msgDowChart = new Highcharts.Chart({
chart: {
renderTo: 'msgDowStats'
},
title: {
text: 'Day of Week Stats'
},
xAxis: {
categories: [
'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'
]
},
yAxis: {
title: { text: '' },
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
series: msg2
});
var numCharts = 0,
usrHourlyCharts = [],
usrDowCharts = [],
maxMessages = 0;
stats.users.sort(function(a, b){
if(a.name < b.name) { return -1; }
if(a.name > b.name) { return 1; }
return 0;
});
for(var i = 0; i < stats.users.length; i++) {
for(var j = 0; j < 24; j++) {
if(stats.users[i].messages.hours[j] > maxMessages) {
maxMessages = stats.users[i].messages.hours[j];
}
}
}
while((numCharts*10) < stats.users.length) {
var usrList = [],
usrHourlyActivity = [],
usrDowActivity = [];
for(var i = (numCharts*10); i < 10+(numCharts*10); i++) {
if(i < stats.users.length) {
usrList.push(stats.users[i].name);
for(var j = 0; j < 24; j++) {
usrHourlyActivity.push({
x: j,
y: (i - (numCharts*10)),
value: stats.users[i].messages.hours[j]
});
}
dowArr=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
for(var dowI = 0; dowI < 7; dowI++) {
usrDowActivity.push({
x: dowI,
y: (i - (numCharts*10)),
value: stats.users[i].messages.dow[dowArr[dowI]]
});
}
}
}
B("#userHourlyStats").append(B("<div>").attr('id','usrHourlyStats-'+numCharts));
B("#userDowStats").append(B("<div>").attr('id','usrDowStats-'+numCharts));
usrHourlyCharts.push(new Highcharts.Chart({
chart: {
type: 'heatmap',
renderTo: 'usrHourlyStats-'+numCharts
},
title: {
text: 'Hourly Activity by User'
},
xAxis: {
categories: [
'00:00','01:00','02:00','03:00','04:00','05:00',
'06:00','07:00','08:00','09:00','10:00','11:00',
'12:00','13:00','14:00','15:00','16:00','17:00',
'18:00','19:00','20:00','21:00','22:00','23:00'
]
},
yAxis: {
categories: usrList,
title: null
},
colorAxis: {
stops: [
[0, '#3060cf'],
[0.5, '#fffbbc'],
[0.9, '#c4463a']
],
min: 0,
max: maxMessages
},
series: [{
borderWidth: 1,
data: usrHourlyActivity,
name: "Messages",
tooltip: {
pointFormatter: function() {
if(this.x < 10) {
return this.series.yAxis.categories[this.y] + " - 0"+this.x+":00 ("+this.value+" messages)";
}
return this.series.yAxis.categories[this.y] + " - "+this.x+":00 ("+this.value+" messages)";
}
}
}]
}));
usrDowCharts.push(new Highcharts.Chart({
chart: {
type: 'heatmap',
renderTo: 'usrDowStats-'+numCharts
},
title: {
text: 'Day Of Week Activity by User'
},
xAxis: {
categories: [
'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
]
},
yAxis: {
categories: usrList,
title: null
},
colorAxis: {
stops: [
[0, '#3060cf'],
[0.5, '#fffbbc'],
[0.9, '#c4463a']
],
min: 0,
max: maxMessages
},
series: [{
borderWidth: 1,
data: usrDowActivity,
name: "Messages",
tooltip: {
pointFormatter: function() {
if(this.x < 10) {
return this.series.yAxis.categories[this.y] + " - 0"+this.x+":00 ("+this.value+" messages)";
}
return this.series.yAxis.categories[this.y] + " - "+this.x+":00 ("+this.value+" messages)";
}
}
}]
}));
numCharts++;
}
B('#userStats').addClass('hidden');
B('#messageStats').addClass('hidden');
B('#userDowStats').addClass('hidden');
});