From be7768e03b46da5cc206bf2f23f6079e05127701 Mon Sep 17 00:00:00 2001 From: Andy Janata Date: Sun, 26 Jan 2014 23:22:37 -0800 Subject: [PATCH] Refactor preferences code out to its own file. Add game list filters, allowing users to ban and require card sets for a game to be in their game list. --- WebContent/cah.css | 18 +++++ WebContent/game.jsp | 62 ++++++++++---- WebContent/index.jsp | 4 + WebContent/js/cah.app.js | 131 ++++++++++++++++++++++-------- WebContent/js/cah.cardset.js | 24 +++++- WebContent/js/cah.constants.js | 2 +- WebContent/js/cah.gamelist.js | 36 +++++++-- WebContent/js/cah.preferences.js | 134 +++++++++++++++++++++++++++++++ 8 files changed, 354 insertions(+), 57 deletions(-) create mode 100644 WebContent/js/cah.preferences.js diff --git a/WebContent/cah.css b/WebContent/cah.css index e4d6199..1cf3971 100644 --- a/WebContent/cah.css +++ b/WebContent/cah.css @@ -203,6 +203,24 @@ h2,h3,h4 { font-size: 14px !important; } +.cardset_filter_list { + display: inline-block; + width: 33%; +} + +.cardset_filter_list select { + height: 150px; + width: 100%; +} + +.cardset_filter_list span { + font-weight: bold; +} + +.cardset_filter_list .buttons { + text-align: center; +} + .log { width: 100%; height: 198px; diff --git a/WebContent/game.jsp b/WebContent/game.jsp index 0dbe6de..e68ff6d 100644 --- a/WebContent/game.jsp +++ b/WebContent/game.jsp @@ -55,6 +55,7 @@ HttpSession hSession = request.getSession(true); + @@ -67,18 +68,6 @@ HttpSession hSession = request.getSession(true); -<%-- -
-
-

Pretend You're Xyzzy is known to have graphical glitches in - $BROWSER_NAME. The game should work, - but it looks much better in Google Chrome.

-

We will not bug you about this again after you dismiss this dialog.

- -
-
---%> -

Pretend You're
  • The game host has a "stop game" button. If this is abused, it may be changed to only work in the first few rounds of a game.
  • +
  • You can filter which games to display based on what card sets they are using. Under the + Game List Filters tab, you can assign each card set to one of three statuses: Banned, Neutral, + and Required. If a game uses any of your banned sets, it will not be shown. If + a game does not use all of your required sets, it also will not be shown.
  • No further custom card sets will be accepted. Minor updates to existing ones may still be submitted, but I do not guarantee I will get to it in a timely manner. It is taking too much of my time to administer the custom cards sets; I'd rather focus the time on @@ -159,11 +152,12 @@ HttpSession hSession = request.getSession(true);
    - - + +
    @@ -171,6 +165,46 @@ HttpSession hSession = request.getSession(true);
    +
    + You will have to click Refresh Games after saving any changes here. +
    + + +
    +
    + Card set filters +
    + + Do not show any games with these card sets: + + +
    + +
    +
    +
    + Do not require or ban these card sets: + +
    + + +
    +
    +
    + + Only show games with these card sets: + + +
    + +
    +
    +
    +
    diff --git a/WebContent/index.jsp b/WebContent/index.jsp index 194e05e..8947758 100644 --- a/WebContent/index.jsp +++ b/WebContent/index.jsp @@ -62,6 +62,10 @@ to, for instance, display the number of connected players.
  • The game host has a "stop game" button. If this is abused, it may be changed to only work in the first few rounds of a game.
  • +
  • You can filter which games to display based on what card sets they are using. Under the + Game List Filters tab, you can assign each card set to one of three statuses: Banned, Neutral, + and Required. If a game uses any of your banned sets, it will not be shown. If + a game does not use all of your required sets, it also will not be shown.
  • 22 December 2013:
    • What I have received so far of the Holiday Bullshit has been added. I will continue to add diff --git a/WebContent/js/cah.app.js b/WebContent/js/cah.app.js index 8d15bd5..257d512 100644 --- a/WebContent/js/cah.app.js +++ b/WebContent/js/cah.app.js @@ -51,7 +51,7 @@ $(document).ready(function() { // $(window).bind("beforeunload", window_beforeunload); $("#logout").click(logout_click); - load_preferences(); + cah.Preferences.load(); $("#tabs").tabs(); $("#button-global").click(); @@ -232,48 +232,109 @@ function preferences_click() { } function load_preferences() { - if ($.cookie("hide_connect_quit")) { - $("#hide_connect_quit").attr('checked', 'checked'); - } else { - $("#hide_connect_quit").removeAttr('checked'); - } - - if ($.cookie("ignore_list")) { - $("#ignore_list").val($.cookie("ignore_list")); - } else { - $("#ignore_list").val(""); - } - - apply_preferences(); + // FIXME remove these after making sure everything calls the new way. + debugger; + cah.Preferences.load(); } function save_preferences() { - if ($("#hide_connect_quit").attr("checked")) { - $.cookie("hide_connect_quit", true, { - domain : cah.COOKIE_DOMAIN, - expires : 365 - }); - } else { - $.removeCookie("hide_connect_quit"); - } - - $.cookie("ignore_list", $("#ignore_list").val(), { - domain : cah.COOKIE_DOMAIN, - expires : 365 - }); - - apply_preferences(); + // FIXME remove these after making sure everything calls the new way. + debugger; + cah.Preferences.save(); } function apply_preferences() { - cah.hideConnectQuit = !!$("#hide_connect_quit").attr("checked"); - - cah.ignoreList = {}; - $($('#ignore_list').val().split('\n')).each(function() { - cah.ignoreList[this] = true; - }); + // FIXME remove these after making sure everything calls the new way. + debugger; + cah.Preferences.apply(); } +/** + * Add selected items from sourceList to destList, ignoring duplicates. + */ +cah.transferItems = function(sourceListId, destListId, idPrefix) { + cah.transferItems(sourceListId, destListId, idPrefix, function(a, b) { + return Number(a.value) - Number(b.value); + }); +}; + +cah.transferItems = function(sourceListId, destListId, idPrefix, sortFunc) { + $('#' + sourceListId + ' option').filter(':selected').each(function() { + var existing = $('#' + idPrefix + '_' + this.value); + if (existing.length == 0) { + cah.addItem(destListId, this.value, this.text, idPrefix); + } + }); + $('#' + destListId + ' option').sort(sortFunc).appendTo('#' + destListId); + + cah.removeItems(sourceListId); +}; + +/** + * Add an item to a list. + * + * @param listId + * {String} Id of the select element. + * @param value + * {String} Value attribute of the item to insert into the list. + * @param text + * {String} The display text to insert into the list. + * @param idPrefix + * {String} The prefix for the id of the item to insert into the list. + */ +cah.addItem = function(listId, value, text, idPrefix) { + $('#' + listId).append( + ''); +}; + +/** + * Remove selected items from list. + * + * @param listId + * {String} Id of the list from which to remove selected items. + */ +cah.removeItems = function(listId) { + $('#' + listId + ' option').filter(':selected').each(function() { + this.parentElement.removeChild(this); + }); +}; + +/** + * Set a cookie. + * + * @param {String} + * name The name of the cookie. + * @param {Any} + * value The value of the cookie. + */ +cah.setCookie = function(name, value) { + return $.cookie(name, value, { + // domain : cah.COOKIE_DOMAIN, + expires : 365 + }); +}; + +/** + * Remove a cookie. + * + * @param {String} + * name The name of the cookie. + */ +cah.removeCookie = function(name) { + $.removeCookie(name); +}; + +/** + * Get a cookie. + * + * @param {String} + * name The name of the cookie. + * @returns The value of the cookie, or {@code undefined} if the cookie is not set. + */ +cah.getCookie = function(name) { + return $.cookie(name); +}; + /** * Handle a window resize event. Resize the chat and info areas to fit vertically and horizontally. * This was tested extensively in Chrome. It may not be pixel-perfect in other browsers. diff --git a/WebContent/js/cah.cardset.js b/WebContent/js/cah.cardset.js index 11e4e67..44751fe 100644 --- a/WebContent/js/cah.cardset.js +++ b/WebContent/js/cah.cardset.js @@ -56,10 +56,12 @@ cah.CardSet.byWeight = {}; * blackCardCount The number of black cards in this CardSet * @param {Number} * whiteCardCount The number of white cards in this CardSet. + * @param {Number} + * weight The sort weight of this CardSet. * @constructor * @private */ -cah.CardSet = function(id, name, description, baseDeck, blackCardCount, whiteCardCount) { +cah.CardSet = function(id, name, description, baseDeck, blackCardCount, whiteCardCount, weight) { /** * CardSet's database/wire ID. * @@ -107,6 +109,14 @@ cah.CardSet = function(id, name, description, baseDeck, blackCardCount, whiteCar * @private */ this.whiteCardCount_ = whiteCardCount; + + /** + * The sort weight of this CardSet. + * + * @type {Number} + * @private + */ + this.weight_ = weight; }; /** @@ -151,6 +161,13 @@ cah.CardSet.prototype.getWhiteCardCount = function() { return this.whiteCardCount_; }; +/** + * @returns {Number} The sort weight of this CardSet. + */ +cah.CardSet.prototype.getWeight = function() { + return this.weight_; +}; + /** * Populate the internal list of CardSets from data provided by the server. * @@ -167,8 +184,11 @@ cah.CardSet.populateCardSets = function(cardSets) { cardSetData[cah.$.CardSetData.CARD_SET_DESCRIPTION], cardSetData[cah.$.CardSetData.BASE_DECK], cardSetData[cah.$.CardSetData.BLACK_CARDS_IN_DECK], - cardSetData[cah.$.CardSetData.WHITE_CARDS_IN_DECK]); + cardSetData[cah.$.CardSetData.WHITE_CARDS_IN_DECK], cardSetData[cah.$.CardSetData.WEIGHT]); cah.CardSet.list[cardSet.getId()] = cardSet; cah.CardSet.byWeight[cardSetData[cah.$.CardSetData.WEIGHT]] = cardSet; } + + // not sure if there's a better way to call this... + cah.Preferences.updateCardSetFilters(); }; diff --git a/WebContent/js/cah.constants.js b/WebContent/js/cah.constants.js index 6f89712..f2656d2 100644 --- a/WebContent/js/cah.constants.js +++ b/WebContent/js/cah.constants.js @@ -153,9 +153,9 @@ cah.$.ErrorCode_msg['nitg'] = "You are not in that game."; cah.$.ErrorCode_msg['nep'] = "There are not enough players to start the game."; cah.$.ErrorCode_msg['tf'] = "You are chatting too fast. Wait a few seconds and try again."; cah.$.ErrorCode_msg['nyt'] = "It is not your turn to play a card."; +cah.$.ErrorCode_msg['gf'] = "That game is full. Join another."; cah.$.ErrorCode_msg['aS'] = "The game has already stopped."; cah.$.ErrorCode_msg['mtl'] = "Messages cannot be longer than 200 characters."; -cah.$.ErrorCode_msg['gf'] = "That game is full. Join another."; cah.$.ErrorCode_msg['br'] = "Bad request."; cah.$.ErrorCode_msg['ngs'] = "No game specified."; cah.$.ErrorCode_msg['ic'] = "Invalid card specified."; diff --git a/WebContent/js/cah.gamelist.js b/WebContent/js/cah.gamelist.js index 9d68bb5..18218cf 100644 --- a/WebContent/js/cah.gamelist.js +++ b/WebContent/js/cah.gamelist.js @@ -115,8 +115,32 @@ cah.GameList.prototype.processUpdate = function(gameData) { var games = notPassworded.concat(passworded); + var bannedSets = cah.Preferences.getBannedCardSetIds(); + var requiredSets = cah.Preferences.getRequiredCardSetIds(); + for ( var i = 0; i < games.length; i++) { var game = games[i]; + + var hasBanned = false; + $(bannedSets).each(function(index, value) { + if (-1 !== $.inArray(value, game[cah.$.GameInfo.CARD_SETS])) { + hasBanned = true; + return false; + } + }); + + var missingRequired = false; + $(requiredSets).each(function(index, value) { + if (-1 === $.inArray(value, game[cah.$.GameInfo.CARD_SETS])) { + missingRequired = true; + return false; + } + }); + + if (hasBanned || missingRequired) { + continue; + } + var lobby = new cah.GameListLobby(this.element_, game); this.games_[game[cah.$.GameInfo.ID]] = lobby; } @@ -236,10 +260,11 @@ cah.GameListLobby = function(parentElem, data) { $(this.element_).attr( "aria-label", data[cah.$.GameInfo.HOST] + "'s game, with " + data[cah.$.GameInfo.PLAYERS].length + " of " - + data[cah.$.GameInfo.PLAYER_LIMIT] + " players, and " + data[cah.$.GameInfo.SPECTATORS].length - + " of " + data[cah.$.GameInfo.SPECTATOR_LIMIT] + "spectators. " + statusMessage + ". Goal is " - + data[cah.$.GameInfo.SCORE_LIMIT] + " Awesome Points. Using " + cardSetNames.length - + " card set" + (cardSetNames.length == 1 ? "" : "s") + ". " + + data[cah.$.GameInfo.PLAYER_LIMIT] + " players, and " + + data[cah.$.GameInfo.SPECTATORS].length + " of " + data[cah.$.GameInfo.SPECTATOR_LIMIT] + + "spectators. " + statusMessage + ". Goal is " + data[cah.$.GameInfo.SCORE_LIMIT] + + " Awesome Points. Using " + cardSetNames.length + " card set" + + (cardSetNames.length == 1 ? "" : "s") + ". " + (data[cah.$.GameInfo.HAS_PASSWORD] ? "Has" : "Does not have") + " a password."); }; @@ -275,7 +300,8 @@ cah.GameListLobby.prototype.spectateClick = function() { password = ""; } } - cah.Ajax.build(cah.$.AjaxOperation.SPECTATE_GAME).withGameId(this.id_).withPassword(password).run(); + cah.Ajax.build(cah.$.AjaxOperation.SPECTATE_GAME).withGameId(this.id_).withPassword(password) + .run(); }; /** diff --git a/WebContent/js/cah.preferences.js b/WebContent/js/cah.preferences.js new file mode 100644 index 0000000..d7f5f9c --- /dev/null +++ b/WebContent/js/cah.preferences.js @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2014, Andy Janata + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Preferences manager. + * + * @author Andy Janata (ajanata@socialgamer.net) + */ + +cah.Preferences = {}; + +cah.Preferences.apply = function() { + cah.hideConnectQuit = !!$("#hide_connect_quit").attr("checked"); + + cah.ignoreList = {}; + $($('#ignore_list').val().split('\n')).each(function() { + cah.ignoreList[this] = true; + }); + + // TODO card set filters +}; + +cah.Preferences.load = function() { + if ($.cookie("hide_connect_quit")) { + $("#hide_connect_quit").attr('checked', 'checked'); + } else { + $("#hide_connect_quit").removeAttr('checked'); + } + + if ($.cookie("ignore_list")) { + $("#ignore_list").val($.cookie("ignore_list")); + } else { + $("#ignore_list").val(""); + } + + // If we haven't gotten the list of card sets from the server yet, this should fail silently. + cah.Preferences.updateCardSetFilters(); + + cah.Preferences.apply(); +}; + +cah.Preferences.save = function() { + if ($("#hide_connect_quit").attr("checked")) { + cah.setCookie("hide_connect_quit", true); + } else { + cah.removeCookie("hide_connect_quit"); + } + + cah.setCookie("ignore_list", $("#ignore_list").val()); + + // card set filters + var bannedSets = []; + var requiredSets = []; + var func = function(whichList, setArray) { + $("#cardsets_" + whichList + " option").each(function() { + setArray.push(this.value); + }); + }; + func("banned", bannedSets); + func("required", requiredSets); + cah.setCookie("cardsets_banned", bannedSets.join(",")); + cah.setCookie("cardsets_required", requiredSets.join(",")); + + cah.Preferences.apply(); +}; + +cah.Preferences.getBannedCardSetIds = function() { + var banned = []; + if (cah.getCookie("cardsets_banned")) { + banned = cah.getCookie("cardsets_banned").split(","); + } + for ( var index in banned) { + banned[index] = Number(banned[index]); + } + + return banned; +}; + +cah.Preferences.getRequiredCardSetIds = function() { + var required = []; + if (cah.getCookie("cardsets_required")) { + required = cah.getCookie("cardsets_required").split(","); + } + for ( var index in required) { + required[index] = Number(required[index]); + } + + return required; +}; + +cah.Preferences.updateCardSetFilters = function() { + $("#cardsets_banned").find("option").remove(); + $("#cardsets_neutral").find("option").remove(); + $("#cardsets_required").find("option").remove(); + + var banned = cah.Preferences.getBannedCardSetIds(); + var required = cah.Preferences.getRequiredCardSetIds(); + for ( var weight in cah.CardSet.byWeight) { + var cardSet = cah.CardSet.byWeight[weight]; + var whichList = "neutral"; + if (-1 !== $.inArray(cardSet.getId(), banned)) { + whichList = "banned"; + } else if (-1 !== $.inArray(cardSet.getId(), required)) { + whichList = "required"; + } + cah.addItem("cardsets_" + whichList, cardSet.getId(), cardSet.getName(), whichList); + } +}; + +cah.Preferences.transferCardSets = function(sourceList, destList) { + cah.transferItems("cardsets_" + sourceList, "cardsets_" + destList, destList, function(a, b) { + return cah.CardSet.list[a.value].getWeight() - cah.CardSet.list[b.value].getWeight(); + }); +};