private static string RenderHitObjectBackgroundStyle(HitObject aHitObject, Beatmap aBeatmap)
        {
            if (aBeatmap.generalSettings.mode == Beatmap.Mode.Taiko)
            {
                // drumroll
                if (aHitObject is Slider)
                {
                    return("background-color:rgba(252,191,31,0.5);");
                }
                // kat
                if (aHitObject.HasHitSound(HitObject.HitSound.Clap) || aHitObject.HasHitSound(HitObject.HitSound.Whistle))
                {
                    return("background-color:rgba(68,141,171,0.5);");
                }
                // don
                return("background-color:rgba(235,69,44,0.5);");
            }

            int colourIndex = aBeatmap.GetComboColourIndex(aHitObject.time);

            if (aBeatmap.colourSettings.combos.Count() > colourIndex)
            {
                return
                    ("background-color:rgba(" +
                     aBeatmap.colourSettings.combos[colourIndex].X + "," +
                     aBeatmap.colourSettings.combos[colourIndex].Y + "," +
                     aBeatmap.colourSettings.combos[colourIndex].Z + ", 0.5);");
            }
            else
            {
                // Should no custom combo colours exist, objects will simply be gray.
                return
                    ("background-color:rgba(125,125,125, 0.5);");
            }
        }
        private static string RenderHitObjectSizeStyle(HitObject aHitObject, Beatmap aBeatmap, bool aIsSliderPath = false)
        {
            if (aBeatmap.generalSettings.mode == Beatmap.Mode.Taiko)
            {
                if (aHitObject.HasHitSound(HitObject.HitSound.Finish))
                {
                    // big don/kat
                    return
                        ("height:30px;" +
                         (aIsSliderPath ?
                          "border-radius:15px;" :
                          "width:30px;") +
                         "margin-left:-15.5px;");
                }

                return
                    ("height:20px;" +
                     (aIsSliderPath ?
                      "border-radius:10px;" :
                      "width:20px;") +
                     "margin-left:-10.5px;" +
                     "margin-bottom:-2px;");
            }

            return("");
        }
        private IEnumerable <string> GetChanges(HitObject addedObject, HitObject removedObject)
        {
            if (addedObject.Position != removedObject.Position)
            {
                yield return("Moved from " + removedObject.Position.X + "; " + removedObject.Position.Y +
                             " to " + addedObject.Position.X + "; " + addedObject.Position.Y + ".");
            }

            if (addedObject.hitSound != removedObject.hitSound)
            {
                foreach (HitObject.HitSound hitSound in Enum.GetValues(typeof(HitObject.HitSound)))
                {
                    if (addedObject.HasHitSound(hitSound) && !removedObject.HasHitSound(hitSound))
                    {
                        yield return("Added " + Enum.GetName(typeof(HitObject.HitSound), hitSound)?.ToLower() + ".");
                    }

                    if (!addedObject.HasHitSound(hitSound) && removedObject.HasHitSound(hitSound))
                    {
                        yield return("Removed " + Enum.GetName(typeof(HitObject.HitSound), hitSound)?.ToLower() + ".");
                    }
                }
            }

            if (addedObject.sampleset != removedObject.sampleset)
            {
                yield return("Sampleset changed from " +
                             removedObject.sampleset.ToString().ToLower() + " to " +
                             addedObject.sampleset.ToString().ToLower() + ".");
            }

            if (addedObject.addition != removedObject.addition)
            {
                yield return("Addition changed from " +
                             removedObject.addition.ToString().ToLower() + " to " +
                             addedObject.addition.ToString().ToLower() + ".");
            }

            if ((addedObject.customIndex ?? 0) != (removedObject.customIndex ?? 0))
            {
                yield return("Custom sampleset index override changed from " +
                             (removedObject.customIndex?.ToString() ?? "default") + " to " +
                             (addedObject.customIndex?.ToString() ?? "default") + ".");
            }

            if (addedObject.type.HasFlag(HitObject.Type.NewCombo) && !removedObject.type.HasFlag(HitObject.Type.NewCombo))
            {
                yield return("Added new combo.");
            }

            if (!addedObject.type.HasFlag(HitObject.Type.NewCombo) && removedObject.type.HasFlag(HitObject.Type.NewCombo))
            {
                yield return("Removed new combo.");
            }

            int addedComboSkip = 0;

            if (addedObject.type.HasFlag(HitObject.Type.ComboSkip1))
            {
                addedComboSkip += 1;
            }
            if (addedObject.type.HasFlag(HitObject.Type.ComboSkip2))
            {
                addedComboSkip += 2;
            }
            if (addedObject.type.HasFlag(HitObject.Type.ComboSkip3))
            {
                addedComboSkip += 4;
            }

            int removedComboSkip = 0;

            if (removedObject.type.HasFlag(HitObject.Type.ComboSkip1))
            {
                removedComboSkip += 1;
            }
            if (removedObject.type.HasFlag(HitObject.Type.ComboSkip2))
            {
                removedComboSkip += 2;
            }
            if (removedObject.type.HasFlag(HitObject.Type.ComboSkip3))
            {
                removedComboSkip += 4;
            }

            if (addedComboSkip != removedComboSkip)
            {
                yield return("Changed skipped combo amount from " + removedComboSkip + " to " +
                             addedComboSkip + ".");
            }

            if (addedObject.filename != removedObject.filename)
            {
                yield return("Hit sound filename changed from " + removedObject.filename + " to " +
                             addedObject.filename + ".");
            }

            if (addedObject.volume != removedObject.volume)
            {
                yield return("Hit sound volume changed from " + (removedObject.volume?.ToString() ?? "inherited") + " to " +
                             (addedObject.volume?.ToString() ?? "inherited") + ".");
            }

            string type = addedObject.GetObjectType();

            if (type == "Slider")
            {
                Slider addedSlider   = new Slider(addedObject.code.Split(','), null);
                Slider removedSlider = new Slider(removedObject.code.Split(','), null);

                if (addedSlider.curveType != removedSlider.curveType)
                {
                    yield return("Curve type changed from " + removedSlider.curveType + " to " +
                                 addedSlider.curveType + ".");
                }

                if (addedSlider.edgeAmount != removedSlider.edgeAmount)
                {
                    yield return("Reverse amount changed from " + (removedSlider.edgeAmount - 1) + " to " +
                                 (addedSlider.edgeAmount - 1) + ".");
                }

                if (addedSlider.endSampleset != removedSlider.endSampleset)
                {
                    yield return("Tail sampleset changed from " +
                                 removedSlider.endSampleset.ToString().ToLower() + " to " +
                                 addedSlider.endSampleset.ToString().ToLower() + ".");
                }

                if (addedSlider.endAddition != removedSlider.endAddition)
                {
                    yield return("Tail addition changed from " +
                                 removedSlider.endAddition.ToString().ToLower() + " to " +
                                 addedSlider.endAddition.ToString().ToLower() + ".");
                }

                if (addedSlider.endHitSound != removedSlider.endHitSound)
                {
                    foreach (HitObject.HitSound hitSound in Enum.GetValues(typeof(HitObject.HitSound)))
                    {
                        if (addedSlider.HasHitSound(hitSound) && !removedSlider.HasHitSound(hitSound))
                        {
                            yield return("Added " + Enum.GetName(typeof(HitObject.HitSound), hitSound)?.ToLower() +
                                         " to tail.");
                        }

                        if (!addedSlider.HasHitSound(hitSound) && removedSlider.HasHitSound(hitSound))
                        {
                            yield return("Removed " + Enum.GetName(typeof(HitObject.HitSound), hitSound)?.ToLower() +
                                         " from tail.");
                        }
                    }
                }

                if (addedSlider.pixelLength.AlmostEqual(removedSlider.pixelLength))
                {
                    yield return("Pixel length changed from " + removedSlider.pixelLength +
                                 " to " + addedSlider.pixelLength + ".");
                }

                if (addedSlider.nodePositions.Count == removedSlider.nodePositions.Count)
                {
                    // The first node is the start, which we already checked.
                    for (int i = 1; i < addedSlider.nodePositions.Count; ++i)
                    {
                        if (addedSlider.nodePositions[i] != removedSlider.nodePositions[i])
                        {
                            yield return("Node " + (i + 1) + " moved from " +
                                         removedSlider.nodePositions[i].X + "; " + removedSlider.nodePositions[i].Y + " to " +
                                         addedSlider.nodePositions[i].X + "; " + addedSlider.nodePositions[i].Y + ".");
                        }
                    }
                }
                else
                {
                    yield return("Node count changed from " + removedSlider.nodePositions.Count +
                                 " to " + addedSlider.nodePositions.Count + " (possibly positions as well).");
                }

                if (addedSlider.edgeAmount == removedSlider.edgeAmount)
                {
                    for (int i = 0; i < addedSlider.reverseSamplesets.Count; ++i)
                    {
                        if (addedSlider.reverseSamplesets.ElementAtOrDefault(i) !=
                            removedSlider.reverseSamplesets.ElementAtOrDefault(i))
                        {
                            yield return("Reverse #" + (i + 1) + " sampleset changed from " +
                                         removedSlider.reverseSamplesets.ElementAtOrDefault(i).ToString().ToLower() + " to " +
                                         addedSlider.reverseSamplesets.ElementAtOrDefault(i).ToString().ToLower() + ".");
                        }
                    }

                    for (int i = 0; i < addedSlider.reverseAdditions.Count; ++i)
                    {
                        if (addedSlider.reverseAdditions.ElementAtOrDefault(i) !=
                            removedSlider.reverseAdditions.ElementAtOrDefault(i))
                        {
                            yield return("Reverse #" + (i + 1) + " addition changed from " +
                                         removedSlider.reverseAdditions.ElementAtOrDefault(i).ToString().ToLower() + " to " +
                                         addedSlider.reverseAdditions.ElementAtOrDefault(i).ToString().ToLower() + ".");
                        }
                    }

                    for (int i = 0; i < addedSlider.reverseAdditions.Count; ++i)
                    {
                        if (addedSlider.reverseHitSounds.ElementAtOrDefault(i) !=
                            removedSlider.reverseHitSounds.ElementAtOrDefault(i))
                        {
                            foreach (HitObject.HitSound hitSound in Enum.GetValues(typeof(HitObject.HitSound)))
                            {
                                if (addedSlider.reverseHitSounds.ElementAtOrDefault(i).HasFlag(hitSound) &&
                                    !removedSlider.reverseHitSounds.ElementAtOrDefault(i).HasFlag(hitSound))
                                {
                                    yield return("Added " +
                                                 Enum.GetName(typeof(HitObject.HitSound), hitSound).ToLower() +
                                                 " to reverse #" + (i + 1) + ".");
                                }

                                if (!addedSlider.reverseHitSounds.ElementAtOrDefault(i).HasFlag(hitSound) &&
                                    removedSlider.reverseHitSounds.ElementAtOrDefault(i).HasFlag(hitSound))
                                {
                                    yield return("Removed " + Enum.GetName(typeof(HitObject.HitSound), hitSound).ToLower() +
                                                 " from reverse #" + (i + 1) + ".");
                                }
                            }
                        }
                    }
                }

                if (addedSlider.startHitSound != removedSlider.startHitSound)
                {
                    foreach (HitObject.HitSound hitSound in Enum.GetValues(typeof(HitObject.HitSound)))
                    {
                        if (addedSlider.startHitSound.HasFlag(hitSound) &&
                            !removedSlider.startHitSound.HasFlag(hitSound))
                        {
                            yield return("Added " + Enum.GetName(typeof(HitObject.HitSound), hitSound).ToLower() +
                                         " to head.");
                        }

                        if (!addedSlider.startHitSound.HasFlag(hitSound) &&
                            removedSlider.startHitSound.HasFlag(hitSound))
                        {
                            yield return("Removed " + Enum.GetName(typeof(HitObject.HitSound), hitSound).ToLower() +
                                         " from head.");
                        }
                    }
                }

                if (addedSlider.startSampleset != removedSlider.startSampleset)
                {
                    yield return("Head sampleset changed from " +
                                 removedSlider.startSampleset.ToString().ToLower() + " to " +
                                 addedSlider.startSampleset.ToString().ToLower() + ".");
                }

                if (addedSlider.startAddition != removedSlider.startAddition)
                {
                    yield return("Head addition changed from " +
                                 removedSlider.startAddition.ToString().ToLower() + " to " +
                                 addedSlider.startAddition.ToString().ToLower() + ".");
                }
            }

            if (type == "Spinner")
            {
                Spinner addedSpinner   = new Spinner(addedObject.code.Split(','), null);
                Spinner removedSpinner = new Spinner(removedObject.code.Split(','), null);

                if (addedSpinner.endTime.AlmostEqual(removedSpinner.endTime))
                {
                    yield return("End time changed from " + removedSpinner.endTime + " to " +
                                 addedSpinner.endTime + ".");
                }
            }

            if (type == "Hold note")
            {
                HoldNote addedNote   = new HoldNote(addedObject.code.Split(','), null);
                HoldNote removedNote = new HoldNote(removedObject.code.Split(','), null);

                if (addedNote.endTime.AlmostEqual(removedNote.endTime))
                {
                    yield return("End time changed from " + removedNote.endTime + " to " +
                                 addedNote.endTime + ".");
                }
            }
        }