@@ -0,0 +1,237 @@
integerPattern = "^[0-9]{1,}$";
nIntegerPattern = "^-?[0-9]{1,}$";
floatPattern = "^[0-9]{1,}\.?[0-9]{0,}$";
percentagePattern = "^[1-9][0-9]?$|^100$"
multiplierPattern = "^0(\.\d+)?|1\.0$"

// This is super hacky, but it's better than copy pasting
// the modal frame work for every fucking setting. I know
// there has to be a better way, but this works for now.
//
// This seems to always be my experience with javascript. Ugh.
formCallback = null;
formElement = null;

$(document).ready(function() {
$.ajax({
url: "admin/setting",
method: "GET",
sucess: function(response) {
$.each(response.settings, function(key, val) {
// For most things, we can just set it literally.
// For others, we need to do some stuff first
switch (key) {
case "filterBuffered":
if (val) {
val = "Enabled"
} else {
val = "Disabled"
}
break;
case "inactivityThreshold":
val = val + " minute(s)";
break;
case "skipThreshold":
val = 100 * val + "%";
break;
}

$("#" + key).text(val);
})
},
error: function(result) {
// TODO: Could do a badge, but fuck it, more things to do
alert("Error retrieving settings.");
}
});
});

function createModal(title, form, callback) {
$("#settings-title").val(title);

$("#settings-body").empty();
$("#settings-body").append(form);

formCallback = callback;
formElement = form;
form.show();
}

function createTextForm(label, pattern, initial) {
var form = $("#form-text").clone();
form.attr("pattern", pattern);
form.find(".control-label").text(label);
form.find(".value").val(initial);
form.removeClass("has-error");
return form;
}

function createBooleanForm(label, enabled) {
var form = $("#form-boolean").clone();
form.find(".control-label").text(label);
form.find(".bool").prop("checked", enabled);
form.removeClass("has-error");
return form;
}

function postSetting(setting, value) {
$.ajax({
url: "admin/setting",
method: "POST",
data: { key: setting, value: value },
sucess: function(response) {
// TODO: could do a badge, but fuck it, more things to do
alert("Setting saved!");
window.location.reload();
},
error: function(result) {
// TODO: Could do a badge, but fuck it, more things to do
alert("Error saving setting: " + result);
window.location.reload();
}
});
}

$("#btn-save").click(function() {
// Retrieve validation pattern from div.
var pattern = formElement.attr("pattern");
var value = null

if (formElement.find(".bool").length > 0) {
value = formElement.find(".bool").is(":checked");
} else {
value = formElement.find(".value").val();
}

if (pattern) {
pattern = new RegExp(pattern);
if (pattern.test(value)) {
formCallback(value);
} else {
$("#form-text").addClass("has-error");
return;
}
} else {
formCallback(value);
}

$("#settingsModal").modal("hide");
formCallback = null;
formElement = null;
});

/***************************************
* DB Settings *
****************************************/
$("#filterBuffered").click(function() {
var enabled = $("#filterBuffered").text() == "Enabled";
createModal("Filter Buffered", createBooleanForm("Enabled", enabled), function(val) {
postSetting("filterBuffered", val);

if (val) {
$("#filterBuffered").text("Enabled");
} else {
$("#filterBuffered").text("Disabled");
}
});
});

$("#inactivityThreshold").click(function() {
var initial = $("#inactivityThreshold").text();
initial = initial.split(" ")[0];

createModal("Inactivity Threshold", createTextForm("Threshold (minutes)",integerPattern, initial), function(val) {
postSetting("inactivityThreshold", val * 60 * 1000);
$("#inactivityThreshold").text(val + " minute(s)");
});
});

$("#resultLimit").click(function() {
createModal("Result Limit", createTextForm("Result Limit", nIntegerPattern, $("#resultLimit").text()), function(val) {
postSetting("resultLimit", val);
$("#resultLimit").text(val);
});
});


/***************************************
* Qeueu Settings *
****************************************/
$("#session-name").click(function() {
createModal("Session Name", createTextForm("Session Name", "^[a-zA-Z0-9]{1,20}$", $("#session-name").text()), function(val) {
postSetting("sessionName", val);
$("#session-name").text(val);
});
});

$("#queueSize").click(function() {
createModal("Queue Size", createTextForm("Queue Size", integerPattern, $("#queueSize").text()), function(val) {
postSetting("queueSize", queueSize);
$("#queueSize").text(val);
});
});

$("#trendingArtistsSize").click(function() {
createModal("Trending Artists Size", createTextForm("Trending Artists Size", integerPattern, $("#trendingArtistsSize").text()), function(val) {
postSetting("trendingArtistsSize", val);
$("#trendingArtistsSize").text(val);
});
});

$("#skipThreshold").click(function() {
createModal("Skip Threshold", createTextForm("Skip Threshold [0, 100]", percentagePattern, $("#skipThreshold").text()), function(val) {
postSetting("skipThreshold", val/100.0);
$("#skipThreshold").text(val + "%");
});
});

/***************************************
* Algo Settings *
****************************************/
$("#countWeight").click(function() {
createModal("Count Weight", createTextForm("Count Weight [0.0, 1.0]", multiplierPattern, $("#countWeight").text()), function(val) {
postSetting("countWeight", val);
$("#countWeight").text(val);
});
});

$("#voteWeight").click(function() {
createModal("Vote Weight", createTextForm("Vote Weight [0.0, 1.0]", multiplierPattern, $("#voteWeight").text()), function(val) {
postSetting("voteWeight", val);
$("#voteWeight").text(val);
});
});

$("#genreWeight").click(function() {
createModal("Genre Weight", createTextForm("Genre Weight [0.0, 1.0]", multiplierPattern, $("#genreWeight").text()), function(val) {
postSetting("genreWeight", val);
$("#genreWeight").text(val);
});
});

$("#artistWeight").click(function() {
createModal("Artist Weight", createTextForm("Artist Weight [0.0, 1.0]", multiplierPattern, $("#artistWeight").text()), function(val) {
postSetting("artistWeight", val);
$("#artistWeight").text(val);
});
});

$("#playedAgainMult").click(function() {
createModal("Played Again Multiplier", createTextForm("Played Again Multiplier [0.0, 1.0]", multiplierPattern, $("#playedAgainMult").text()), function(val) {
postSetting("playedAgainMultiplier", val);
$("#playedAgainMult").text(val);
});
});

$("#minRepeatWindow").click(function() {
var initial = $("#minRepeatWindow").text();
initial = initial.split(" ")[0];

createModal("Minimum Repeat Window", createTextForm("Minimum Repeat Window (Minutes)", integerPattern, initial), function(val) {
postSetting("minRepeatWindow", val);
$("#minRepeatWindow").text(val + " minute(s)");
});
});



This file was deleted.

@@ -0,0 +1,219 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Crowdsound</title>

<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-theme-slate.min.css" rel="stylesheet">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- Top Navbar -->
<nav class="navbar">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Crowdsound</a>
</div>

<div id="navbar">
<ul class="nav navbar-nav navbar-right">
<li class=""><a href="index.html">Dashboard</a></li>
<li class="active"><a href="#">Settings</a></li>
</ul>
</div>
</div>
</nav>


<div class="container">
<div class="row">
<div class="col-md-3">
<div class="panel panel-success">
<div class="panel-heading">Version Info</div>
<ul class="list-group">
<li class="list-group-item"><strong>Crowdsound:</strong> <span id="stat-name">815f820</span></li>
<li class="list-group-item"><strong>Playsource:</strong> <span id="stat-users">815f820</span></li>
<li class="list-group-item"><strong>Algorithm:</strong> <span id="stat-users">815f820</span></li>
<li class="list-group-item"><strong>Skrillex:</strong> <span id="stat-users">815f820</span></li>
<li class="list-group-item"><strong>gRPC:</strong> <span id="stat-users">0.11-1</span></li>
</ul>
</div>
<div class="panel panel-primary">
<div class="panel-heading">DB Stats</div>
<ul id="trending-artists" class="list-group">
<li class="list-group-item"><strong>Songs:</strong> <span id="stat-users">1000</span></li>
<li class="list-group-item"><strong>Artists:</strong> <span id="stat-users">100</span></li>
<li class="list-group-item"><strong>Genres:</strong> <span id="stat-users">10</span></li>
</ul>
</div>
<div class="panel panel-primary">
<div class="panel-heading">User Stats</div>
<ul id="trending-artists" class="list-group">
<li class="list-group-item"><strong>Current Users:</strong> <span id="stat-users">1000</span></li>
<li class="list-group-item"><strong>Total Users:</strong> <span id="stat-users">100</span></li>
</ul>
</div>

</div>
<div class="col-md-9">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">DB Settings</h3>
</div>

<ul id="buffered-queue" class="list-group">
<!-- TODO: Generate these -->
<li class="list-group-item">
Filter Buffered
<div style="float: right;">
<a id="filterBuffered" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Inactivity Threshold
<div style="float: right;">
<a id="inactivityThreshold" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Result Limit
<div style="float: right;">
<a id="resultLimit" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
</ul>
</div>

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Queue Settings</h3>
</div>

<ul id="buffered-queue" class="list-group">
<!-- TODO: Generate these -->
<li class="list-group-item">
Session Name
<div style="float: right;">
<a id="session-name" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Queue Size
<div style="float: right;">
<a id="queueSize" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Trending Artists Size
<div style="float: right;">
<a id="trendingArtistsSize" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Skip Threshold
<div style="float: right;">
<a id="skipThreshold" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
</ul>
</div>

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Algorithm Settings</h3>
</div>

<ul id="buffered-queue" class="list-group">
<!-- TODO: Generate these -->
<li class="list-group-item">
Count Weight
<div style="float: right;">
<a id="countWeight" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Vote Weight
<div style="float: right;">
<a id="voteWeight" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Genre Weight
<div style="float: right;">
<a id="genreWeight" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Artist Weight
<div style="float: right;">
<a id="artistWeight" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Played Again Multiplier
<div style="float: right;">
<a id="playedAgainMult" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
<li class="list-group-item">
Minimum Repeat Window
<div style="float: right;">
<a id="minRepeatWindow" href="#" data-toggle="modal" data-target="#settingsModal">Loading...</a>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>

<!-- Modal Template -->
<div id="settingsModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 id="settings-title" class="modal-title">Settings</h4>
</div>
<div id="settings-body" class="modal-body">
</div>
<div class="modal-footer">
<button id="btn-save" type="button" class="btn btn-success">Save</button>
<button id="btn-cancel" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>

<!-- Text Value Template -->
<div id="form-text" class="form-group" style="display: none;">
<label class="control-label" for="text-input">Text</label>
<input id="text-input" class="form-control value" type="text" required>
</div>

<!-- Boolean Value Template -->
<div id="form-boolean" class="form-group" style="display: none;">
<input id="boolean-input" class="value bool" type="checkbox">
<label class="control-label" for="boolean-input">Text</label>
</label>
</div>

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="js/lib/jquery-1.12.1.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/lib/bootstrap.min.js"></script>
<script src="js/settings.js"></script>
</body>
</html>