")
+ .text(T("Nothing here. Yet.")));
+ $("#load-more-achievements").remove();
+ return;
+ }
+
+ var displayAchievements = function(limit, achievedOnly) {
+ var $ach = $("#achievements").empty();
+ limit = limit < 0 ? achievements.length : limit;
+ var shown = 0;
+ for (var i = 0; i < achievements.length; i++) {
+ var ach = achievements[i];
+ if (shown >= limit || (achievedOnly && !ach.achieved)) {
+ continue;
+ }
+ shown++;
+ $ach.append(
+ $("
").append(
+ $("
").popup({
+ title: ach.name,
+ content: ach.description,
+ position: "bottom center",
+ distanceAway: 10
+ })
+ )
+ );
+ }
+ // if we've shown nothing, and achievedOnly is enabled, try again
+ // this time disabling it.
+ if (shown == 0 && achievedOnly) {
+ displayAchievements(limit, false);
+ }
+ };
+
+ // only 8 achievements - we can remove the button completely, because
+ // it won't be used (no more achievements).
+ // otherwise, we simply remove the disabled class and add the click handler
+ // to activate it.
+ if (achievements.length <= 8) {
+ $("#load-more-achievements").remove();
+ } else {
+ $("#load-more-achievements")
+ .removeClass("disabled")
+ .click(function() {
+ $(this).remove();
+ displayAchievements(-1, false);
+ });
+ }
+ displayAchievements(8, true);
+ });
+}
+
+function initialiseFriends() {
+ var b = $("#add-friend-button");
+ if (b.length == 0) return;
+ api('friends/with', {id: userID}, setFriendOnResponse);
+ b.click(friendClick);
+}
+function setFriendOnResponse(r) {
+ var x = 0;
+ if (r.friend) x++;
+ if (r.mutual) x++;
+ setFriend(x);
+}
+function setFriend(i) {
+ var b = $("#add-friend-button");
+ b.removeClass("loading green blue red");
+ switch (i) {
+ case 0:
+ b
+ .addClass("blue")
+ .attr("title", T("Add friend"))
+ .html("
");
+ break;
+ case 1:
+ b
+ .addClass("green")
+ .attr("title", T("Remove friend"))
+ .html("
");
+ break;
+ case 2:
+ b
+ .addClass("red")
+ .attr("title", T("Unmutual friend"))
+ .html("
");
+ break;
+ }
+ b.attr("data-friends", i > 0 ? 1 : 0)
+}
+function friendClick() {
+ var t = $(this);
+ if (t.hasClass("loading")) return;
+ t.addClass("loading");
+ api("friends/" + (t.attr("data-friends") == 1 ? "del" : "add"), {user: userID}, setFriendOnResponse, true);
+}
+
+var defaultScoreTable;
+function setDefaultScoreTable() {
+ defaultScoreTable = $("
")
+ .append(
+ $("
").append(
+ $("
").append(
+ $("
" + T("General info") + " "),
+ $("
"+ T("Score") + " ")
+ )
+ )
+ )
+ .append(
+ $("
")
+ )
+ .append(
+ $("
").append(
+ $("
").append(
+ $("
").append(
+ $("").append(
+ $("
" + T("Load more") + " ").click(loadMoreClick)
+ )
+ )
+ )
+ )
+ )
+ ;
+}
+i18next.on('loaded', function(loaded) {
+ setDefaultScoreTable();
+});
+function initialiseScores(el, mode) {
+ el.attr("data-loaded", "1");
+ var best = defaultScoreTable.clone(true).addClass("orange");
+ var recent = defaultScoreTable.clone(true).addClass("blue");
+ best.attr("data-type", "best");
+ recent.attr("data-type", "recent");
+ recent.addClass("no bottom margin");
+ el.append($("
").append(
+ $("
").append("", best),
+ $("
").append("", recent)
+ ));
+ loadScoresPage("best", mode);
+ loadScoresPage("recent", mode);
+};
+function loadMoreClick() {
+ var t = $(this);
+ if (t.hasClass("disabled"))
+ return;
+ t.addClass("disabled");
+ var type = t.parents("table[data-type]").data("type");
+ var mode = t.parents("div[data-mode]").data("mode");
+ loadScoresPage(type, mode);
+}
+// currentPage for each mode
+var currentPage = {
+ 0: {best: 0, recent: 0},
+ 1: {best: 0, recent: 0},
+ 2: {best: 0, recent: 0},
+ 3: {best: 0, recent: 0},
+};
+var scoreStore = {};
+function loadScoresPage(type, mode) {
+ var table = $("#scores-zone div[data-mode=" + mode + "] table[data-type=" + type + "] tbody");
+ var page = ++currentPage[mode][type];
+ console.log("loadScoresPage with", {
+ page: page,
+ type: type,
+ mode: mode,
+ });
+ api("users/scores/ap/" + type, {
+ mode: mode,
+ p: page,
+ l: 20,
+ id: userID,
+ }, function(r) {
+ if (r.scores == null) {
+ disableLoadMoreButton(type, mode);
+ return;
+ }
+ r.scores.forEach(function(v, idx){
+ scoreStore[v.id] = v;
+ if (v.completed == 0){
+ var scoreRank = "failed";
+ }else{
+ var scoreRank = getRank(mode, v.mods, v.accuracy, v.count_300, v.count_100, v.count_50, v.count_miss);
+ }
+
+ table.append($("
").append(
+ $(
+ "
" +
+ escapeHTML(v.beatmap.song_name) + " " + getScoreMods(v.mods) + " (" + v.accuracy.toFixed(2) + "%) " +
+ "" + v.time + "
"
+ ),
+ $("
" + ppOrScore(v.pp, v.score) + " " + weightedPP(type, page, idx, v.pp) + (v.completed == 3 ? " " + downloadStar(v.id) : "") + "")
+ ));
+ });
+ $(".new.timeago").timeago().removeClass("new");
+ $(".new.score-row").click(viewScoreInfo).removeClass("new");
+ $(".new.downloadstar").click(function(e) {
+ e.stopPropagation();
+ }).removeClass("new");
+ var enable = true;
+ if (r.scores.length != 20)
+ enable = false;
+ disableLoadMoreButton(type, mode, enable);
+ });
+}
+function downloadStar(id) {
+ return "
" + T("Download") + "";
+}
+function weightedPP(type, page, idx, pp) {
+ if (type != "best" || pp == 0)
+ return "";
+ var perc = Math.pow(0.95, ((page - 1) * 20) + idx);
+ var wpp = pp * perc;
+ return "
(" + wpp.toFixed(2) + "pp) ";
+}
+function disableLoadMoreButton(type, mode, enable) {
+ var button = $("#scores-zone div[data-mode=" + mode + "] table[data-type=" + type + "] .load-more-button");
+ if (enable) button.removeClass("disabled");
+ else button.addClass("disabled");
+}
+function viewScoreInfo() {
+ var scoreid = $(this).data("scoreid");
+ if (!scoreid && scoreid !== 0) return;
+ var s = scoreStore[scoreid];
+ if (s === undefined) return;
+
+ // data to be displayed in the table.
+ var data = {
+ "Points": addCommas(s.score),
+ "PP": addCommas(s.pp),
+ "Beatmap": "
" + escapeHTML(s.beatmap.song_name) + " ",
+ "Accuracy": s.accuracy + "%",
+ "Max combo": addCommas(s.max_combo) + "/" + addCommas(s.beatmap.max_combo)
+ + (s.full_combo ? " " + T("(full combo)") : ""),
+ "Difficulty": T("{{ stars }} star", {
+ stars: s.beatmap.difficulty2[modesShort[s.play_mode]],
+ count: Math.round(s.beatmap.difficulty2[modesShort[s.play_mode]]),
+ }),
+ "Mods": getScoreMods(s.mods, true),
+ };
+
+ // hits data
+ var hd = {};
+ var trans = modeTranslations[s.play_mode];
+ [
+ s.count_300,
+ s.count_100,
+ s.count_50,
+ s.count_geki,
+ s.count_katu,
+ s.count_miss,
+ ].forEach(function(val, i) {
+ hd[trans[i]] = val;
+ });
+
+ data = $.extend(data, hd, {
+ "Ranked?": T(s.completed == 3 ? "Yes" : "No"),
+ "Achieved": s.time,
+ "Mode": modes[s.play_mode],
+ });
+
+ var els = [];
+ $.each(data, function(key, value) {
+ els.push(
+ $("
").append(
+ $("
" + T(key) + " "),
+ $("
" + value + " ")
+ )
+ );
+ });
+
+ $("#score-data-table tr").remove();
+ $("#score-data-table").append(els);
+ $(".ui.modal").modal("show");
+}
+
+var modeTranslations = [
+ [
+ "300s",
+ "100s",
+ "50s",
+ "Gekis",
+ "Katus",
+ "Misses"
+ ],
+ [
+ "GREATs",
+ "GOODs",
+ "50s",
+ "GREATs (Gekis)",
+ "GOODs (Katus)",
+ "Misses"
+ ],
+ [
+ "Fruits (300s)",
+ "Ticks (100s)",
+ "Droplets",
+ "Gekis",
+ "Droplet misses",
+ "Misses"
+ ],
+ [
+ "300s",
+ "200s",
+ "50s",
+ "Max 300s",
+ "100s",
+ "Misses"
+ ]
+];
+
+function getRank(gameMode, mods, acc, c300, c100, c50, cmiss) {
+ var total = c300+c100+c50+cmiss;
+
+ // Hidden | Flashlight | FadeIn
+ var hdfl = (mods & (1049608)) > 0;
+
+ var ss = hdfl ? "SSHD" : "SS";
+ var s = hdfl ? "SHD" : "S";
+
+ switch(gameMode) {
+ case 0:
+ case 1:
+ var ratio300 = c300 / total;
+ var ratio50 = c50 / total;
+
+ if (ratio300 == 1)
+ return ss;
+
+ if (ratio300 > 0.9 && ratio50 <= 0.01 && cmiss == 0)
+ return s;
+
+ if ((ratio300 > 0.8 && cmiss == 0) || (ratio300 > 0.9))
+ return "A";
+
+ if ((ratio300 > 0.7 && cmiss == 0) || (ratio300 > 0.8))
+ return "B";
+
+ if (ratio300 > 0.6)
+ return "C";
+
+ return "D";
+
+ case 2:
+ if (acc == 100)
+ return ss;
+
+ if (acc > 98)
+ return s;
+
+ if (acc > 94)
+ return "A";
+
+ if (acc > 90)
+ return "B";
+
+ if (acc > 85)
+ return "C";
+
+ return "D";
+
+ case 3:
+ if (acc == 100)
+ return ss;
+
+ if (acc > 95)
+ return s;
+
+ if (acc > 90)
+ return "A";
+
+ if (acc > 80)
+ return "B";
+
+ if (acc > 70)
+ return "C";
+
+ return "D";
+ }
+}
+
+function ppOrScore(pp, score) {
+ if (pp != 0)
+ return addCommas(pp.toFixed(2)) + "pp";
+ return addCommas(score);
+}
+
+function beatmapLink(type, id) {
+ if (type == "s")
+ return "
" + id + ' ';
+ return "
" + id + ' ';
+}
diff --git a/static/profile_relax.js b/static/profile_relax.js
new file mode 100644
index 0000000..6f8ca2c
--- /dev/null
+++ b/static/profile_relax.js
@@ -0,0 +1,442 @@
+// code that is executed on every user profile
+$(document).ready(function() {
+ var wl = window.location;
+ var newPathName = wl.pathname;
+ // userID is defined in profile.html
+ if (newPathName.split("/")[2] != userID) {
+ newPathName = "/u/" + userID;
+ }
+ // if there's no mode parameter in the querystring, add it
+ if (wl.search.indexOf("mode=") === -1)
+ window.history.replaceState('', document.title, newPathName + "?mode=" + favouriteMode + wl.hash);
+ else if (wl.pathname != newPathName)
+ window.history.replaceState('', document.title, newPathName + wl.search + wl.hash);
+ setDefaultScoreTable();
+ // when an item in the mode menu is clicked, it means we should change the mode.
+ $("#mode-menu>.item").click(function(e) {
+ e.preventDefault();
+ if ($(this).hasClass("active"))
+ return;
+ var m = $(this).data("mode");
+ $("[data-mode]:not(.item):not([hidden])").attr("hidden", "");
+ $("[data-mode=" + m + "]:not(.item)").removeAttr("hidden");
+ $("#mode-menu>.active.item").removeClass("active");
+ var needsLoad = $("#scores-zone>[data-mode=" + m + "][data-loaded=0]");
+ if (needsLoad.length > 0)
+ initialiseScores(needsLoad, m);
+ $(this).addClass("active");
+ window.history.replaceState('', document.title, wl.pathname + "?mode=" + m + wl.hash);
+ });
+ initialiseAchievements();
+ initialiseFriends();
+ // load scores page for the current favourite mode
+ var i = function(){initialiseScores($("#scores-zone>div[data-mode=" + favouriteMode + "]"), favouriteMode)};
+ if (i18nLoaded)
+ i();
+ else
+ i18next.on("loaded", function() {
+ i();
+ });
+});
+
+function initialiseAchievements() {
+ api('users/achievements' + (currentUserID == userID ? '?all' : ''),
+ {id: userID}, function (resp) {
+ var achievements = resp.achievements;
+ // no achievements -- show default message
+ if (achievements.length === 0) {
+ $("#achievements")
+ .append($("
")
+ .text(T("Nothing here. Yet.")));
+ $("#load-more-achievements").remove();
+ return;
+ }
+
+ var displayAchievements = function(limit, achievedOnly) {
+ var $ach = $("#achievements").empty();
+ limit = limit < 0 ? achievements.length : limit;
+ var shown = 0;
+ for (var i = 0; i < achievements.length; i++) {
+ var ach = achievements[i];
+ if (shown >= limit || (achievedOnly && !ach.achieved)) {
+ continue;
+ }
+ shown++;
+ $ach.append(
+ $("
").append(
+ $("
").popup({
+ title: ach.name,
+ content: ach.description,
+ position: "bottom center",
+ distanceAway: 10
+ })
+ )
+ );
+ }
+ // if we've shown nothing, and achievedOnly is enabled, try again
+ // this time disabling it.
+ if (shown == 0 && achievedOnly) {
+ displayAchievements(limit, false);
+ }
+ };
+
+ // only 8 achievements - we can remove the button completely, because
+ // it won't be used (no more achievements).
+ // otherwise, we simply remove the disabled class and add the click handler
+ // to activate it.
+ if (achievements.length <= 8) {
+ $("#load-more-achievements").remove();
+ } else {
+ $("#load-more-achievements")
+ .removeClass("disabled")
+ .click(function() {
+ $(this).remove();
+ displayAchievements(-1, false);
+ });
+ }
+ displayAchievements(8, true);
+ });
+}
+
+function initialiseFriends() {
+ var b = $("#add-friend-button");
+ if (b.length == 0) return;
+ api('friends/with', {id: userID}, setFriendOnResponse);
+ b.click(friendClick);
+}
+function setFriendOnResponse(r) {
+ var x = 0;
+ if (r.friend) x++;
+ if (r.mutual) x++;
+ setFriend(x);
+}
+function setFriend(i) {
+ var b = $("#add-friend-button");
+ b.removeClass("loading green blue red");
+ switch (i) {
+ case 0:
+ b
+ .addClass("blue")
+ .attr("title", T("Add friend"))
+ .html("
");
+ break;
+ case 1:
+ b
+ .addClass("green")
+ .attr("title", T("Remove friend"))
+ .html("
");
+ break;
+ case 2:
+ b
+ .addClass("red")
+ .attr("title", T("Unmutual friend"))
+ .html("
");
+ break;
+ }
+ b.attr("data-friends", i > 0 ? 1 : 0)
+}
+function friendClick() {
+ var t = $(this);
+ if (t.hasClass("loading")) return;
+ t.addClass("loading");
+ api("friends/" + (t.attr("data-friends") == 1 ? "del" : "add"), {user: userID}, setFriendOnResponse, true);
+}
+
+var defaultScoreTable;
+function setDefaultScoreTable() {
+ defaultScoreTable = $("
")
+ .append(
+ $("
").append(
+ $("
").append(
+ $("
" + T("General info") + " "),
+ $("
"+ T("Score") + " ")
+ )
+ )
+ )
+ .append(
+ $("
")
+ )
+ .append(
+ $("
").append(
+ $("
").append(
+ $("
").append(
+ $("").append(
+ $("
" + T("Load more") + " ").click(loadMoreClick)
+ )
+ )
+ )
+ )
+ )
+ ;
+}
+i18next.on('loaded', function(loaded) {
+ setDefaultScoreTable();
+});
+function initialiseScores(el, mode) {
+ el.attr("data-loaded", "1");
+ var best = defaultScoreTable.clone(true).addClass("orange");
+ var recent = defaultScoreTable.clone(true).addClass("blue");
+ best.attr("data-type", "best");
+ recent.attr("data-type", "recent");
+ recent.addClass("no bottom margin");
+ el.append($("
").append(
+ $("
").append("", best),
+ $("
").append("", recent)
+ ));
+ loadScoresPage("best", mode);
+ loadScoresPage("recent", mode);
+};
+function loadMoreClick() {
+ var t = $(this);
+ if (t.hasClass("disabled"))
+ return;
+ t.addClass("disabled");
+ var type = t.parents("table[data-type]").data("type");
+ var mode = t.parents("div[data-mode]").data("mode");
+ loadScoresPage(type, mode);
+}
+// currentPage for each mode
+var currentPage = {
+ 0: {best: 0, recent: 0},
+ 1: {best: 0, recent: 0},
+ 2: {best: 0, recent: 0},
+ 3: {best: 0, recent: 0},
+};
+var scoreStore = {};
+function loadScoresPage(type, mode) {
+ var table = $("#scores-zone div[data-mode=" + mode + "] table[data-type=" + type + "] tbody");
+ var page = ++currentPage[mode][type];
+ console.log("loadScoresPage with", {
+ page: page,
+ type: type,
+ mode: mode,
+ });
+ api("users/scores/relax/" + type, {
+ mode: mode,
+ p: page,
+ l: 20,
+ id: userID,
+ }, function(r) {
+ if (r.scores == null) {
+ disableLoadMoreButton(type, mode);
+ return;
+ }
+ r.scores.forEach(function(v, idx){
+ scoreStore[v.id] = v;
+ if (v.completed == 0){
+ var scoreRank = "failed";
+ }else{
+ var scoreRank = getRank(mode, v.mods, v.accuracy, v.count_300, v.count_100, v.count_50, v.count_miss);
+ }
+
+ table.append($("
").append(
+ $(
+ "
" +
+ escapeHTML(v.beatmap.song_name) + " " + getScoreMods(v.mods) + " (" + v.accuracy.toFixed(2) + "%) " +
+ "" + v.time + "
"
+ ),
+ $("
" + ppOrScore(v.pp, v.score) + " " + weightedPP(type, page, idx, v.pp) + (v.completed == 3 ? " " + downloadStar(v.id) : "") + "")
+ ));
+ });
+ $(".new.timeago").timeago().removeClass("new");
+ $(".new.score-row").click(viewScoreInfo).removeClass("new");
+ $(".new.downloadstar").click(function(e) {
+ e.stopPropagation();
+ }).removeClass("new");
+ var enable = true;
+ if (r.scores.length != 20)
+ enable = false;
+ disableLoadMoreButton(type, mode, enable);
+ });
+}
+function downloadStar(id) {
+ return "
" + T("Download") + "";
+}
+function weightedPP(type, page, idx, pp) {
+ if (type != "best" || pp == 0)
+ return "";
+ var perc = Math.pow(0.95, ((page - 1) * 20) + idx);
+ var wpp = pp * perc;
+ return "
(" + wpp.toFixed(2) + "pp) ";
+}
+function disableLoadMoreButton(type, mode, enable) {
+ var button = $("#scores-zone div[data-mode=" + mode + "] table[data-type=" + type + "] .load-more-button");
+ if (enable) button.removeClass("disabled");
+ else button.addClass("disabled");
+}
+function viewScoreInfo() {
+ var scoreid = $(this).data("scoreid");
+ if (!scoreid && scoreid !== 0) return;
+ var s = scoreStore[scoreid];
+ if (s === undefined) return;
+
+ // data to be displayed in the table.
+ var data = {
+ "Points": addCommas(s.score),
+ "PP": addCommas(s.pp),
+ "Beatmap": "
" + escapeHTML(s.beatmap.song_name) + " ",
+ "Accuracy": s.accuracy + "%",
+ "Max combo": addCommas(s.max_combo) + "/" + addCommas(s.beatmap.max_combo)
+ + (s.full_combo ? " " + T("(full combo)") : ""),
+ "Difficulty": T("{{ stars }} star", {
+ stars: s.beatmap.difficulty2[modesShort[s.play_mode]],
+ count: Math.round(s.beatmap.difficulty2[modesShort[s.play_mode]]),
+ }),
+ "Mods": getScoreMods(s.mods, true),
+ };
+
+ // hits data
+ var hd = {};
+ var trans = modeTranslations[s.play_mode];
+ [
+ s.count_300,
+ s.count_100,
+ s.count_50,
+ s.count_geki,
+ s.count_katu,
+ s.count_miss,
+ ].forEach(function(val, i) {
+ hd[trans[i]] = val;
+ });
+
+ data = $.extend(data, hd, {
+ "Ranked?": T(s.completed == 3 ? "Yes" : "No"),
+ "Achieved": s.time,
+ "Mode": modes[s.play_mode],
+ });
+
+ var els = [];
+ $.each(data, function(key, value) {
+ els.push(
+ $("
").append(
+ $("
" + T(key) + " "),
+ $("
" + value + " ")
+ )
+ );
+ });
+
+ $("#score-data-table tr").remove();
+ $("#score-data-table").append(els);
+ $(".ui.modal").modal("show");
+}
+
+var modeTranslations = [
+ [
+ "300s",
+ "100s",
+ "50s",
+ "Gekis",
+ "Katus",
+ "Misses"
+ ],
+ [
+ "GREATs",
+ "GOODs",
+ "50s",
+ "GREATs (Gekis)",
+ "GOODs (Katus)",
+ "Misses"
+ ],
+ [
+ "Fruits (300s)",
+ "Ticks (100s)",
+ "Droplets",
+ "Gekis",
+ "Droplet misses",
+ "Misses"
+ ],
+ [
+ "300s",
+ "200s",
+ "50s",
+ "Max 300s",
+ "100s",
+ "Misses"
+ ]
+];
+
+function getRank(gameMode, mods, acc, c300, c100, c50, cmiss) {
+ var total = c300+c100+c50+cmiss;
+
+ // Hidden | Flashlight | FadeIn
+ var hdfl = (mods & (1049608)) > 0;
+
+ var ss = hdfl ? "SSHD" : "SS";
+ var s = hdfl ? "SHD" : "S";
+
+ switch(gameMode) {
+ case 0:
+ case 1:
+ var ratio300 = c300 / total;
+ var ratio50 = c50 / total;
+
+ if (ratio300 == 1)
+ return ss;
+
+ if (ratio300 > 0.9 && ratio50 <= 0.01 && cmiss == 0)
+ return s;
+
+ if ((ratio300 > 0.8 && cmiss == 0) || (ratio300 > 0.9))
+ return "A";
+
+ if ((ratio300 > 0.7 && cmiss == 0) || (ratio300 > 0.8))
+ return "B";
+
+ if (ratio300 > 0.6)
+ return "C";
+
+ return "D";
+
+ case 2:
+ if (acc == 100)
+ return ss;
+
+ if (acc > 98)
+ return s;
+
+ if (acc > 94)
+ return "A";
+
+ if (acc > 90)
+ return "B";
+
+ if (acc > 85)
+ return "C";
+
+ return "D";
+
+ case 3:
+ if (acc == 100)
+ return ss;
+
+ if (acc > 95)
+ return s;
+
+ if (acc > 90)
+ return "A";
+
+ if (acc > 80)
+ return "B";
+
+ if (acc > 70)
+ return "C";
+
+ return "D";
+ }
+}
+
+function ppOrScore(pp, score) {
+ if (pp != 0)
+ return addCommas(pp.toFixed(2)) + "pp";
+ return addCommas(score);
+}
+
+function beatmapLink(type, id) {
+ if (type == "s")
+ return "
" + id + ' ';
+ return "
" + id + ' ';
+}
diff --git a/templates/clanmembers.html b/templates/clanmembers.html
index 215ce3d..bcfa3d0 100644
--- a/templates/clanmembers.html
+++ b/templates/clanmembers.html
@@ -1,7 +1,7 @@
{{/*###
NoCompile=true
-*/}}
{{ define "clanMembers" }}
+*/}}
{ with . }}
{{ $teamJSON := teamJSON }}
diff --git a/templates/profile_auto.html b/templates/profile_auto.html
new file mode 100644
index 0000000..a2fb14e
--- /dev/null
+++ b/templates/profile_auto.html
@@ -0,0 +1,264 @@
+{{ define "tpl" }}
+
+ {{ if .UserID }}
+ {{ $gqm := atoi (.Gin.Query "mode") }}
+ {{ $global := . }}
+ {{ with (.Get "users/full?id=%d" .UserID) }}
+ {{ $favouritemode := _or $gqm .favourite_mode }}
+
+ {{ if after .silence_info.end }}
+
{{ $global.T "User is silenced for %s, expires %s." (.silence_info.reason | htmlescaper) (time .silence_info.end) | html }}
+ {{ end }}
+
+ {{ $sarah := has .id 1193 }}
+ {{ $alicia := has .id 1000 }}
+ {{ $catherine := has .id 999 }}
+
+ {{ $super := has .privileges 7340031 }}
+ {{ $dev := has .privileges 11534335 }}
+ {{ $donor := has .privileges 7 }}
+ {{ $admin := has .privileges 3049983 }}
+ {{ $chatmod := has .privileges 786763 }}
+ {{ $bn := has .privileges 267 }}
+
+ {{ if hasAdmin $global.Context.User.Privileges }}
+ {{ $restr := not (has .privileges 1) }}
+ {{ $disab := not (has .privileges 2) }}
+ {{ $pend := has .privileges 1048576 }}
+ {{ if and $disab $restr }}
+ {{ if $pend }}
+
{{ $global.T "User is %s " "pending verification" | html }}.
+ {{ else }}
+
{{ $global.T "User is %s " "banned" | html }}.
+ {{ end }}
+ {{ else if $restr }}
+
{{ $global.T "User is %s " "restricted" | html }}.
+ {{ else if $disab }}
+
{{ $global.T "User is %s " "locked" | html }}.
+ {{ end }}
+ {{ end }}
+ {{ with $global.Get "users/userpage?id=%.0f" .id }}
+ {{ if .userpage }}
+ {{ with parseUserpage .userpage }}
+
+ {{ html . }}
+
+ {{ end }}
+ {{ end }}
+ {{ end }}
+
+
+
+
+
+ {{ if .username_aka }}
+
+ {{ $global.T "(aka %s )" (.username_aka | htmlescaper) | html }}
+
+ {{ end }}
+ {{ with bget "isOnline?id=%.0f" .id }}
+
+
+ {{ if .result }}{{ $global.T "Online" }}{{ else }}{{ $global.T "Offline" }}{{ end }}
+
+ {{ end }}
+
+
+
+
+ {{ range $k, $v := (slice .std .taiko .ctb .mania) }}
+
{{ with and $v $v.global_leaderboard_rank }}#{{ . }}{{ else }}{{ $global.T "Unknown" }}{{ end }}
+ {{ end }}
+
+ {{ if and (ne $global.Context.User.ID $global.UserID) (ne $global.Context.User.ID 0) }}
+
+
+
+ {{ end }}
+ {{ if eq $global.Context.User.ID $global.UserID }}
+
+
+
+ {{ end }}
+ {{ if hasAdmin $global.Context.User.Privileges }}
+
+
+
+ {{ end }}
+
+
+
+
+ {{ $user := . }}
+
+
+
+
+
+ {{if $super }}
+ {{ $global.T "%s " .username | html }}
+ is an Owner
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $dev}}
+ {{ $global.T "%s " .username | html }}
+ is a Developer
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $admin}}
+ {{ $global.T "%s " .username | html }}
+ is an Administrator
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $chatmod}}
+ {{ $global.T "%s " .username | html }}
+ is a Chat Mod
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $bn}}
+ {{ $global.T "%s " .username | html }}
+ is a Nominator
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $donor }}
+ {{ $global.T "%s " .username | html }}
+ is a Supporter
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{ else }}
+ {{ $global.T "%s is a player from %s ." .username (country .country true) | html }}
+ {{ end }}
+
+ {{ $global.T "They signed up on Yozora %s." (time $user.registered_on) | html }}
+ {{ $global.T "Last seen: %s." (time $user.latest_activity) | html }}
+ {{ with playstyle .play_style $global }}{{ $global.T "They play with %s." . }}{{ end }}
+
+
+ {{ if and (not .badges) (not .custom_badge) }}
+ {{ $global.T "This user hasn't got any badges!" }}
+ {{ else }}
+
+ {{ range .badges }}
+
+
+ {{ .name }}
+
+ {{ end }}
+ {{ with .custom_badge }}
+
+
+ {{ .name }}
+
+ {{ end }}
+
+ {{ end }}
+
+
+ {{ range $k, $v := (slice .std .taiko .ctb .mania) }}
+
+
+
+ {{ with .global_leaderboard_rank }}
+
+ {{ $global.T "Global rank" }}
+ #{{ . }}
+
+ {{ end }}
+ {{ with .country_leaderboard_rank }}
+
+ {{ $global.T "Country rank" }} {{ country $user.country false }}
+ #{{ . }}
+
+ {{ end }}
+
+ {{ $global.T "PP" }}
+ {{ humanize .pp }}
+
+
+ {{ $global.T "Ranked score" }}
+ {{ humanize .ranked_score }}
+
+
+ {{ $global.T "Total score" }}
+ {{ humanize .total_score }}
+
+
+ {{ $global.T "Playcount" }}
+ {{ humanize .playcount }}
+
+
+ {{ $global.T "Replays watched" }}
+ {{ humanize .replays_watched }}
+
+
+ {{ $global.T "Total hits" }}
+ {{ humanize .total_hits }}
+
+ {{ $global.T "Accuracy" }}
+ {{ printf "%.2f" .accuracy }}%
+
+
+
+
+
+
{{ levelPercent .level }}%
+
+
{{ $global.T "Level %s" (level .level) }}
+
+
+ {{ end }}
+
+
+
+
+
+ {{ range _range 4 }}
+
+
+ {{ end }}
+
+
+
+
+
+
+
+
+ {{ $global.T "Load more" }}
+
+
+
+
+ {{ end }}
+ {{ end }}
+
+{{ end }}
diff --git a/templates/profile_relax.html b/templates/profile_relax.html
new file mode 100644
index 0000000..a2fb14e
--- /dev/null
+++ b/templates/profile_relax.html
@@ -0,0 +1,264 @@
+{{ define "tpl" }}
+
+ {{ if .UserID }}
+ {{ $gqm := atoi (.Gin.Query "mode") }}
+ {{ $global := . }}
+ {{ with (.Get "users/full?id=%d" .UserID) }}
+ {{ $favouritemode := _or $gqm .favourite_mode }}
+
+ {{ if after .silence_info.end }}
+
{{ $global.T "User is silenced for %s, expires %s." (.silence_info.reason | htmlescaper) (time .silence_info.end) | html }}
+ {{ end }}
+
+ {{ $sarah := has .id 1193 }}
+ {{ $alicia := has .id 1000 }}
+ {{ $catherine := has .id 999 }}
+
+ {{ $super := has .privileges 7340031 }}
+ {{ $dev := has .privileges 11534335 }}
+ {{ $donor := has .privileges 7 }}
+ {{ $admin := has .privileges 3049983 }}
+ {{ $chatmod := has .privileges 786763 }}
+ {{ $bn := has .privileges 267 }}
+
+ {{ if hasAdmin $global.Context.User.Privileges }}
+ {{ $restr := not (has .privileges 1) }}
+ {{ $disab := not (has .privileges 2) }}
+ {{ $pend := has .privileges 1048576 }}
+ {{ if and $disab $restr }}
+ {{ if $pend }}
+
{{ $global.T "User is %s " "pending verification" | html }}.
+ {{ else }}
+
{{ $global.T "User is %s " "banned" | html }}.
+ {{ end }}
+ {{ else if $restr }}
+
{{ $global.T "User is %s " "restricted" | html }}.
+ {{ else if $disab }}
+
{{ $global.T "User is %s " "locked" | html }}.
+ {{ end }}
+ {{ end }}
+ {{ with $global.Get "users/userpage?id=%.0f" .id }}
+ {{ if .userpage }}
+ {{ with parseUserpage .userpage }}
+
+ {{ html . }}
+
+ {{ end }}
+ {{ end }}
+ {{ end }}
+
+
+
+
+
+ {{ if .username_aka }}
+
+ {{ $global.T "(aka %s )" (.username_aka | htmlescaper) | html }}
+
+ {{ end }}
+ {{ with bget "isOnline?id=%.0f" .id }}
+
+
+ {{ if .result }}{{ $global.T "Online" }}{{ else }}{{ $global.T "Offline" }}{{ end }}
+
+ {{ end }}
+
+
+
+
+ {{ range $k, $v := (slice .std .taiko .ctb .mania) }}
+
{{ with and $v $v.global_leaderboard_rank }}#{{ . }}{{ else }}{{ $global.T "Unknown" }}{{ end }}
+ {{ end }}
+
+ {{ if and (ne $global.Context.User.ID $global.UserID) (ne $global.Context.User.ID 0) }}
+
+
+
+ {{ end }}
+ {{ if eq $global.Context.User.ID $global.UserID }}
+
+
+
+ {{ end }}
+ {{ if hasAdmin $global.Context.User.Privileges }}
+
+
+
+ {{ end }}
+
+
+
+
+ {{ $user := . }}
+
+
+
+
+
+ {{if $super }}
+ {{ $global.T "%s " .username | html }}
+ is an Owner
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $dev}}
+ {{ $global.T "%s " .username | html }}
+ is a Developer
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $admin}}
+ {{ $global.T "%s " .username | html }}
+ is an Administrator
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $chatmod}}
+ {{ $global.T "%s " .username | html }}
+ is a Chat Mod
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $bn}}
+ {{ $global.T "%s " .username | html }}
+ is a Nominator
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{else if $donor }}
+ {{ $global.T "%s " .username | html }}
+ is a Supporter
+ {{ $global.T " from %s ." (country .country true) | html }}
+ {{ else }}
+ {{ $global.T "%s is a player from %s ." .username (country .country true) | html }}
+ {{ end }}
+
+ {{ $global.T "They signed up on Yozora %s." (time $user.registered_on) | html }}
+ {{ $global.T "Last seen: %s." (time $user.latest_activity) | html }}
+ {{ with playstyle .play_style $global }}{{ $global.T "They play with %s." . }}{{ end }}
+
+
+ {{ if and (not .badges) (not .custom_badge) }}
+ {{ $global.T "This user hasn't got any badges!" }}
+ {{ else }}
+
+ {{ range .badges }}
+
+
+ {{ .name }}
+
+ {{ end }}
+ {{ with .custom_badge }}
+
+
+ {{ .name }}
+
+ {{ end }}
+
+ {{ end }}
+
+
+ {{ range $k, $v := (slice .std .taiko .ctb .mania) }}
+
+
+
+ {{ with .global_leaderboard_rank }}
+
+ {{ $global.T "Global rank" }}
+ #{{ . }}
+
+ {{ end }}
+ {{ with .country_leaderboard_rank }}
+
+ {{ $global.T "Country rank" }} {{ country $user.country false }}
+ #{{ . }}
+
+ {{ end }}
+
+ {{ $global.T "PP" }}
+ {{ humanize .pp }}
+
+
+ {{ $global.T "Ranked score" }}
+ {{ humanize .ranked_score }}
+
+
+ {{ $global.T "Total score" }}
+ {{ humanize .total_score }}
+
+
+ {{ $global.T "Playcount" }}
+ {{ humanize .playcount }}
+
+
+ {{ $global.T "Replays watched" }}
+ {{ humanize .replays_watched }}
+
+
+ {{ $global.T "Total hits" }}
+ {{ humanize .total_hits }}
+
+ {{ $global.T "Accuracy" }}
+ {{ printf "%.2f" .accuracy }}%
+
+
+
+
+
+
{{ levelPercent .level }}%
+
+
{{ $global.T "Level %s" (level .level) }}
+
+
+ {{ end }}
+
+
+
+
+
+ {{ range _range 4 }}
+
+
+ {{ end }}
+
+
+
+
+
+
+
+
+ {{ $global.T "Load more" }}
+
+
+
+
+ {{ end }}
+ {{ end }}
+
+{{ end }}