A FilterInfo object contains information on a game filter, which is essentially a selection rule that selects a subset of games to show in the wheel UI. The system provides a number of predefined filters, such as the era filters ("1970s games"), manufacturer filters ("Gottlieb games"), and system filters ("Visual Pinball X games"). You can also create your own custom filters, which we'll explain below.
The main wheel UI lets the user select which filter to use at any given time via the menu system. Whenever the selected filter changes, a filterselect event is fired. You can use this to do additional work as needed when the filter selection changes.
You don't create FilterInfo objects directly. You get them by calling gameList.getFilterInfo(). That function returns a FilterInfo object populated with details about the filter you specified.
If you want to create a custom filter, see Custom filters below.
cmd: An integer command ID value. This will always be in the range from command.FilterFirst to command.FilterLast (see Commands). (The command ID is assigned for the current session only, so it shouldn't be saved externally.)
getGames(): Returns an array of GameInfo objects listing all games that the filter currently selects.
group: A string giving the menu group for the filter. System filters all have groups with names enclosed in [square brackets]. These are never displayed to the user; the system uses localized menu titles for the groups instead. User-defined filters have optional group names defined when the filter was created; these can refer to system groups, or can be given as the actual menu title to show in the main menu. This is omitted if the filter isn't part of a menu group.
groupCmd: An integer giving the command ID associated with the group. This is the command used for the menu item shown in the main menu, if any, that opens the group submenu. This is undefined if the filter isn't part of a group, or it's part of one of the top-level groups that doesn't have an associated command ([Top], [Op]).
id: The ID of the filter (see Filter IDs). This is a permanent ID for the filter, so it can be stored in external files or settings.
testGame(game): Determines whether the filter includes or excludes the given game. game is a GameInfo object specifying the game to test. Returns true if the filter includes the game in its selection list, false if the filter excludes the game. (If game doesn't refer to a valid game, the return value is null, which for conditional purposes is equivalent to false, but can be distinguished with the Javascript === operator if the distinction is important to your code for some reason.)
title: A string giving the display title of the filter as it appears in menus. Note that this might be localized, and might change across versions, so you shouldn't use it to recognize a particular filter; use the id value for that.
Each filter defined by the system has a unique ID string. This ID string is designed to be permanent across sessions, so it can be stored in external files and saved settings.
Each class of predefined system filters uses an ID that follows a particular format. You can test filter IDs against these formats if you want to recognize specific filters or specific classes of filters. Here are the ID formats for the system's filters:
You can define your own custom filters via gameList.createFilter(desc). This function creates a new filter with a selection rule you define via a Javascript function, which can express just about any kind of selection criteria you can think of.
The return value from gameList.createFilter() is a command ID that the system assigns for use by the filter. This is used if the filter is automatically added to system menus (see Automatic menu placement below). You can also use this command ID if you want to create an item for this filter in any custom menus. The command will always be in the range from command.FilterFirst to command.FilterLast, which is shared with the built-in system filters. The newly assigned command will always be chosen to be unique, so it won't conflict with any system filters.
The desc argument to createFilter() is an object describing the new filter. It contains the following properties:
The system automatically adds the prefix "User." to the ID string you provide. If you retrieve the FilterInfo object for the filter from gameList.getCurFilter(), for example, its id property will include the added prefix.
a and b are GameInfo objects. The function compares the two and returns a number: a negative value if a comes before b in the desired sorting order, 0 if they're equal, and a positive value if a comes after b. This makes the comparison a sort of "subtraction" function: if you think about the games as integer values, this is the result you get by simply computing a minus b. If you were sorting by manufacturing year, for example, you could do literally that, by returning a.year - b.year.
When the filter is activated, the system will call this function on pairs of games to be included in the filter to sort them into the correct order. This function will be called repeatedly during the sorting process each time the filter is activated, so it should be simple and fast.
For many custom sorts, you'll probably want to sort games alphabetically if they're not otherwise distinguishable for your sort criteria. For example, if you're sorting by original manufacturing year, chances are that you'll have multiple games released during the same year for many of the years. In those cases, you'll probably want to use alphabetical sorting as a secondary rule. Here's how you can do this using Javascript's localeCompare() method, which returns the same sort of "subtraction" value we need after comparing two strings alphabetically:
That first tests to see if you can distinguish the games by year, and if so, returns the year difference, which satisfies our "subtraction" rule for the return value. If the years are the same, we fall back on the titles to determine the relative order of the two games by using localeCompare().
The sorting function is optional. If you omit it, the standard alphabetical sorting is used. If you provide a custom sorting function, you will also usually want to define a custom pageGroup function, since the default grouping by first letter of the title probably won't make sense if the games aren't sorted alphabetically.
game is a GameInfo object. The return value is an integer giving the group number for the game. The group number value is entirely up to your function to define; all that matters is that all games that belong to the same paging group must have the same group number. For example, for the default grouping by first letter of the title, the group number could simply be the Unicode character code of the first letter of the title (and that's just what the system's default grouping function uses). If you wanted to group games by decade, you could return first year of the decade that the game was manufactured in; e.g., return 1980 for a game made in 1983.
The page grouping doesn't affect the sorting order of the games. The sorting order is controlled entirely by the custom sorting function. The grouping only controls how far the wheel turns on a Next/Previous Page command. Those commands are processed simply by advancing the wheel in the selected direction until finding a game with a different group from the current game. That's why the sorting function and grouping function have to be designed to work together: the sorting function has to sort the games into an order such that they group together into the paging groups naturally. If you're grouping games by first letter of title, they must be sorted in some kind of alphabetical order - not necessarily A-Z, but some order where the A's are all grouped together, etc.
There's one "special" group number: 0, the un-group. If the grouping function returns 0 for a given game, the wheel will never stop on that game. This lets you specify that certain games should be skipped during Next/Previous Page commands. For example, if you were grouping alphabetically, but you always wanted to skip over games with non-alphabetic titles (such as "2001" or "4-Square"), you could return 0 for those games.
The paging group function is optional. If you omit it, the standard grouping by first letter of the title is used. That default grouping usually wouldn't make sense if you change the sort order, though, so custom sorting and custom grouping usually go hand-in-hand - if you specify a custom sorting function, you should define a corresponding custom grouping function, and vice versa.
Custom filter group menus: If you give the filter a group property, the system automatically creates a new menu item for the group in the top-level menu. Selecting this menu opens a sub-menu showing all of the filters with the same group property. The group menu is added after the standard filter group items in the standard menu (Filter by Era, Filter by Manufacturer, and so on), so this lets you create your own custom filter groups of the same style. The group property string is displayed as the menu title, so you'll probably want to use similar wording ("Filter by something") so that the menu meshes well with the existing items.
Adding the filter to an existing group: Instead of creating a custom filter group, you can add a custom filter to one of the existing groups. This would be appropriate if the filter augments one of those groups. For example, you could add your own "recently played" filter that selects a time period that's not covered in the standard set, or a "manufacturer" filter that selects games from all of the WMS brands. For the predefined system filter groups, though, you don't use the name that appears in the menu. Instead, you have to refer to a special internal name. We use the internal name instead of the name in the menu because the menu name can be localized for different languages; the internal name doesn't have to be translated, since it's never displayed to the user, so you can always count on it being the same. The system filter groups are shown below.
Adding to a top-level menu: There are two special filter groups that go in top-level menus rather than in group submenus, "[Top]" (the main menu), and "[Op]" (the operator menu). If you use one of these strings for the group property, your filter will appear in the corresponding menu. Use this capability sparingly, since the menus will quickly become unusable if you add too many top-level items. If you're creating more than one or two filters that don't belong in obvious groups, you'll probably want to create a "Custom Filters" or "Filter by Misc" group as a catch-all, to avoid too much clutter in the top level menus.
If you want to skip the automatic menu inclusion: Just leave group undefined if you don't want the system to automatically show the filter in any of the standard menus. You can control the menu placement more precisely using the menu customization mechanisms described in Menus. You can also activate the filter entirely under script control via gameList.setCurFilter().
"Played within two weeks" filter: Suppose you wanted to supplement the standard "played within" filters with some more time periods than offered in the standard menus. Here's a filter that selects games played within the last two weeks. The filter is automatically included in the "Filter by Last Played" group because it uses the special system group "[Played]".
Short, medium, and long titles: For this example, we'll create a group of filters that select according to the length of a game's title: short (6 character or fewer), medium (7-15 characters), or long (16+ characters). We'll create these as three filters that appear on a custom submenu, which will be reachable from a new top-level menu item called "Filter by Title Length". This is a pretty contrived example, but it shows how to create a custom filter group and present it in the main menu.
One thing to note here is that we gave each filter a sort key. Sort keys are optional, but in this case, it's nice to include one. Without an explicit sort key, the system would just sort the menu items alphabetically by their labels. That's usually great, but this is one of those cases where the "natural" order that a user would expect isn't alphabetical. The intuitive order here is clearly short, medium, long. The sort keys let us override the default alphabetical sorting and provide our own custom sorting instead. When you provide sort keys, the system sorts the menus alphabetically by the sort keys, rather than alphabetically by the visible label. That lets you put the menus in any order you want, without having to change the labels.
The sort keys aren't visible to the user anywhere, so we can use any arbitrary strings that are convenient for putting things into the right order. In this case, we simply use "A", "B", and "C". If you wanted to leave gaps for inserting more items in the future, you could have made those "A", "M", and "Z", or something more flexible like "1000", "2000", and "3000". But for our fixed list of three items, the simplistic "A", "B", "C" will do just fine.
Note that you can use numeric strings like "1000" and "2000" for sort keys, but remember that they're still treated as strings, meaning they're sorted "lexically" rather than numerically. This has some odd consequences when you use strings that happen to contain digits. For example, the string "10" sorts before the string "2", for the same reason that "A0" sorts before "B". Lexical sorting mean that we compare the strings one character at a time, starting at the left end of the string. So if the very first character of string A sorts before the first character of string B, string A will go before string B, even if A has more characters following that make it look like a larger number when interpreted as a number. Hence "10" goes before "2", simply because "1" sorts before "2". The easy way to deal with this is to make sure that digit strings used as sort keys are all the same number of digits, using leading zeroes if necessary - so "0002" rather than just "2", for example.
Top-rated games: For a more extensive example, see A "Top 10" Filter in the Worked Examples section. That shows how to create several variations on a Top 10 filter that selects the current top-rated games in your collection.