Go to any page on Leaguepedia, and you will see a banner along the top of the wiki showing a schedule upcoming matches with countdowns, links to the events, the teams playing, and streams. If there are too many matches scheduled concurrently then the code collapses them into a single block that just says “4 matches” or “16 matches” so that this block doesn’t take up the entire screen.
Creating this display is just a single Lua module (plus of course the entire Cargo infrastructure that it relies on); the more interesting part, and topic of this blog post, is how it gets to where it is - this part of the page isn’t typically made available to you for editing by MediaWiki.
Method 1.0 - Cargo query on each page load
The supporting code
Originally, I created a Cargo table called
TopSchedule. This table contained pre-rendered HTML that I processed in a Lua module, also called
TopSchedule. The Lua module queried the next 50 upcoming matches from my
MatchSchedule table, did some parsing for each one, and then stored a separate row into the Cargo table for each match. The module was invoked on a page called
Project:Top Schedule, and blank edited by a crontab every 15 minutes.
The parsing isn’t important, but here’s the store that the Lua module did (this was so long ago that I didn’t have
util_cargo.store written yet!):
Here’s the code that generated the display at the top of the page. It queries the Cargo table
TopSchedule, un-escapes the HTML, and then puts the result after
This method lasted about three weeks - it was uncacheable and not performant AT ALL. Not good.
Method 1.5 - get query on each page load
The appeal of the Cargo query was that I was always inserting precisely the right number of matches - I wanted 15 to show, I was querying 15, it was always 15 upcoming matches. But I clearly needed something cacheable, which meant that I needed a pre-rendered entire page. So I ditched the
TopSchedule Cargo table and instead, in
Module:TopSchedule, built my actual output: an entire array of 50 matches, not 15, tagged each one with a
data-i attribute as well as its start time, and wrote some JS to hide all of the matches that had already started, as well as all of the matches after the first 15.
(Why 50? This is a rather arbitrary number, it’s almost certainly more than I need, as I have a crontab refreshing the page every fifteen minutes, but it’s performant enough, and this way I can guarantee I’ll never run out of matches to show even if say 20 matches start at the same time, or if something stops my crontab from connecting for a couple hours.)
I’ve omitted the Lua code this time, but
Project:Top Schedule now contained the exact contents of what I want to display as the top schedule - no more, no less.
Here’s the JS; the majority of the code handles hiding all but the correct 15 matches:
This method was fine, and it was in place for a bit over a year. But I wanted to do better, and eliminate the API query altogether!
Method 2.0 - Extension HeaderFooter
There’s this lovely extension called Header Footer that I’m now using for my top schedule. But, it’s not quite that simple because nothing on my wikis ever is. Sooooo here’s how to set up a global site header using Header Footer and how to reposition it to actually be a site header instead of a page header!
Making the header global
Header Footer allows for per-namespace customization of messages, which we actually don’t want in this case, so we’ll need to “overwrite” the specific behavior into a global banner.
You need to create the pages
MediaWiki:Hf-nsheader-User for each namespace. You can just do this by hand, but that sucks, so if you install my
river_mwclient library (available on GitHub), which wraps/extends
mwclient with a bunch of utilities, and set up your credentials as described in the README there, you can run the following code:
Of course, replace
TEXT_TO_SAVE with whatever you actually want your banner to be.
Note, the standard library
site.namespaces only gives you the namespace number, and we need namespace name, so I’m using my own
site.namespaces. If it weren’t that annoying by default, I’d just give you code without custom dependencies; if you don’t want to download
river_mwclient then just do a
site.api query of your own. Here’s the code that generates my
site.namespaces (I also have a
Namespace class defined elsewhere in the library):
Making the header a header
The second part of this is turning the header into a site header instead of just a page header.
In this screenshot, I’ve made a page with the text “Hello world” and a header that says “Here is the default header location” - as you can see, the header is just part of the content area, which is not what we want at all. So we need to move it outside of the content area and to the top of the entire webpage, after
So my Lua puts the entire top schedule into a div with id
top-schedule and inline style
display:none;. That way when it’s loaded into the page at first, it’s not flashed to the user in the wrong place. If it doesn’t exist on the page (e.g. some special pages), nothing happens. Otherwise, I detach it from the wrong location, append it after
#mw-head, and then remove the
display:none;. And that’s it, we’re done!
Actually, the full version of the gadget needs to keep the bit that deals with showing only 15 entries, but your site banner probably won’t need all that, so this is sufficient. But for good measure, here’s my current version of topSchedule.js:
I put the above code in a gadget:
Note in particular that this is not enabled in mobile, and if you’re putting this in your
common.js instead of making it a gadget, you should not put it in
mobile.js, however I do recommend using gadgets for code organization.
To make a global site banner, you need:
- Extension Header Footer
- Create your banner on some project page, with an inline
- Use a Python script to create transclusions of that page to the MediaWiki hf page for every namespace on your wiki (or do this by hand)
- Write a gadget (or just a function in common.js) to detach your banner and place it after
#mw-headon page load
If applicable, you may need a crontab to regularly purge cache/blank edit the page your banner is saved on.