Navigation Placeholder

The Anime List Gets An Upgrade...

The Anime List has been an integral part of the Anime Rating Guide since its inception over twenty years ago. My latest adventure being a geek involved upgrading the Anime List. Previously, I used MS Access to generate lists for two static pages -- a list of anime sorted by title and a second list of anime sorted by rating. I really wanted to combine these two pages into one page that would allow filtering and sorting on demand. So, taking inspiration from Stuart Langridge's Sortable, I used MS Access to generate one list of anime sorted by title, and let the Sortable gadget work its magic. Then, I wrote some custom code to handle filtering on-the-fly and viola, the new Anime List was born.

Everything went reasonably well, but, there were a couple of issues using the Sortable gadget. First, since the list is already sorted, I wanted Sortable to indicate the current (sorted) status of the list to the user. That involved poking around in the code to get Sortable to do what I wanted. I came up with the following JavaScript fragment to handle that issue:
var th = document.getElementsByTagName("th");
var defaultHeader = th[1];
defaultHeader.className="sorttable_sorted";
var sortfwdind = document.createElement('span');
sortfwdind.id = "sorttable_sortfwdind";
sortfwdind.innerHTML = ' ▾';
defaultHeader.appendChild(sortfwdind);
The code above gets the second column header (th[1]) (i.e.: Titles) and sets the class to indicate that the column is already sorted (sorttable_sorted). Finally, the arrow (25BE) is added in a span.

The second issue involved sorting. I needed to sort the titles alphabetically without special characters. That required making a minor change to the code for the Sortable gadget itself. I changed the following from:
sort_alpha: function(a,b) {
if (a[0].toLowerCase()==b[0].toLowerCase()) return 0;
if (a[0].toLowerCase()<b[0].toLowerCase()) return -1;
return 1;
},
...to the following:
sort_alpha: function(a,b) {
if (a[0].toLowerCase().replace(/[^a-z0-9]/g,'')==b[0].toLowerCase().replace(/[^a-z0-9]/g,'')) return 0;
if (a[0].toLowerCase().replace(/[^a-z0-9]/g,'')<b[0].toLowerCase().replace(/[^a-z0-9]/g,'')) return -1;
return 1;
},
The code above adds a replace function to the sorting comparison with a regular expression to remove any characters that are not letters or numbers before comparing two pieces of text (a,b).

After many trips to the Sortable page and many iterations trying to get Sortable to jump through all of my hoops -- I ended up with the Anime List. One other issue is that when Sortable was installed as a gadget in the layout of the main blog, the entire blog would slow down for several seconds after the page loaded. This was not a good outcome. However, since I only needed Sortable on one page, my solution was to copy and paste the code into the Anime List page itself. That, greatly improved the performance of the blog. All things considered, it took a few days, with a late night or two, to get everything just the way I wanted it.

Then, I thought to myself, you know what would be really nice here -- a pie chart! Using Kseso's CSS Pie Chart as a starting point, I spent a couple more days expanding the pie from 4 to 7 segments and reworked the layout to have the values on the left rather than below. I also decided to change the colors to use HTML Color Names.

The most complicated part was getting all of the start and end points for each arc in the pie chart. First, I find the percentage of each slice out of 100%. The size of each arc in degrees, is the slice percentage multiplied by 360. The first segment starts at 0 degrees and the starting point for each following segment is the cumulative size of all of the preceding segments. The CSS for the pie chart starts at about line 245 in Kseso's CSS Pie Chart and the area in the CSS where the angles have to be adjusted is at about line 284.

Assuming an HTML structure like the following:
<div class="donut-container">
<div class="donut-chart-block">
<div class="donut-chart">
<div class="cutout" id="segment1">
<div class="segment-size color1">
</div>
</div>
<div class="cutout" id="segment2">
<div class="segmentarc color2">
</div>
</div>
<div class="cutout" id="segment3">
<div class="segmentarc color3">
</div>
</div>
<div class="cutout" id="segment4">
<div class="segmentarc color4">
</div>
</div>
<div class="cutout" id="segment5">
<div class="segmentarc color5">
</div>
</div>
<div class="cutout" id="segment6">
<div class="segmentarc color6">
</div>
</div>
<div class="cutout" id="segment7">
<div class="segmentarc color7">
</div>
</div>
<div class="center-cutout">
</div>
</div>
</div>
</div>
The values for each arc based on a predefined set of values, here defined in the totals array, could be computed using the following script:
var grandtotal=0;
var totals=[45,157,125,79,51,29,345];
for (var i=0;i<totals.length;i++){
grandtotal+=totals[i];
}//for i
var d=document;
var maxPie=180;//180=half pie; 360=full pie
var totalarc=0;
for (var i=0;i<totals.length;i++){
var percentOfTotal = totals[i]/grandtotal;
var currentarc=Math.round(maxPie * percentOfTotal);
var currentIndex=i+1;
var outerPie = d.getElementById("segment" + currentIndex.toString());
var innerPie = outerPie.getElementsByTagName("div")[0];
outerPie.style.transform="rotate(" + totalarc.toString() + "deg)";
innerPie.style.transform="rotate(" + currentarc.toString() + "deg)";
if (currentarc==0) {
outerPie.style.display="none";
innerPie.style.display="none";
} else {
outerPie.style.display="block";
innerPie.style.display="block";
}
totalarc+=currentarc;
}//for i
The CSS might look like the following:
.donut-container {
width:100px;//200px for full pie
height:200px;
overflow:hidden;
float:right;
position:relative;
right:-30px;
}
.donut-chart-block {
overflow: hidden;
float: right;
}
.donut-chart {
position: relative;
width: 200px;
height: 200px;
border-radius: 100px;
}
.center-cutout {
background:white;
position: absolute;
top:0;left:0;bottom:0;right:0;
width: 70%;
height: 70%;
margin: auto;
border-radius: 100px;
}
.cutout {
border-radius: 100px;
clip: rect(0px, 200px, 200px, 100px);
height: 100%;
position: absolute;
width: 100%;
}
.segment-size {
border-radius: 100px;
clip: rect(0px, 100px, 200px, 0px);
height: 100%;
position: absolute;
width: 100%;
}
.donut-chart .color1 {
background-color: MediumSlateBlue;
}
.donut-chart .color2 {
background-color: Plum;
}
.donut-chart .color3 {
background-color: Gold;
}
.donut-chart .color4 {
background-color: Wheat;
}
.donut-chart .color5 {
background-color: LightSalmon;
}
.donut-chart .color6 {
background-color: OrangeRed;
}
.donut-chart .color7 {
background-color: DarkSlateGray;
}
Afterwards, I added a second set of values to show the pie with only rated anime (i.e.: totals=[45,157,125,79,51,29,0];) and some additional code to handle zero values by hiding the offending pie segments. At some point, I noticed rounding errors as a result of using whole numbers in the individual values, resulting in the total percent being less than 100%. So, I added some code to ensure that the total percent was always 100%, even if that meant fudging the individual values a bit.

Later, I added some code to pre-populate the array (totals) that drives the pie chart, so, I wouldn't actually have to hard-code the values when the list is updated. The only issue I found using Kseso's CSS Pie Chart is that individual slices greater than 180 degrees do not render correctly, so, to avoid this issue, I only show half a pie. For those with more robust graphing needs, there are a number of graphing libraries floating around, but, since my humble needs only involve one pie on a single page, this is good enough.

Finally, thanks go to Nirmal Kumar for a CSS Scrollable Table With Fixed Header which was instrumental in keeping the footprint of the 800+ entries in the Anime List to a manageable size. And, all of this effort, for a page that hardly anyone bothers to visit. I leave you with the unfriendly RPG trailer for LISA. Enjoy.