From 69e2b8ed1aeb88336b1db3a060af14c4a2379d51 Mon Sep 17 00:00:00 2001 From: Mark Hansen Date: Wed, 1 Jan 2025 15:09:56 +1100 Subject: [PATCH] Make legend artists case-insensitive Move to ES classes for backbone extending, which found some type errors. Fix a type error (array.size() does not exist) in num_requests_pending. --- public/AppController.js | 10 +++---- public/models/AppModel.js | 14 ++++++---- public/models/FetchModel.js | 8 +++--- public/models/LegendModel.js | 20 +++++++++----- public/models/Request.js | 4 +-- public/models/RequestQueue.js | 15 ++++++----- public/models/ScrobbleCollection.js | 21 ++++++++------- public/views/DrawingThrobberView.js | 7 ++--- public/views/FetchThrobberView.js | 8 +++--- public/views/FilterBoxView.js | 23 +++++++++++----- public/views/FlashingScrobbleView.js | 14 ++++++---- public/views/FlotScrobbleGraphView.js | 19 ++++++++----- public/views/LegendView.js | 16 ++++++----- public/views/ToolTipView.js | 14 +++++----- public/views/UsernameView.js | 7 ++--- src/AppController.ts | 10 +++---- src/models/AppModel.ts | 14 ++++++---- src/models/FetchModel.ts | 8 +++--- src/models/LegendModel.ts | 27 +++++++++++++------ src/models/Request.ts | 4 +-- src/models/RequestQueue.ts | 18 ++++++------- src/models/ScrobbleCollection.ts | 21 ++++++++------- src/views/DrawingThrobberView.ts | 7 ++--- src/views/FetchThrobberView.ts | 10 ++++--- src/views/FilterBoxView.ts | 22 +++++++++------ src/views/FlashingScrobbleView.js | 14 ++++++---- ...eGraphView.js => FlotScrobbleGraphView.ts} | 22 +++++++++------ src/views/LegendView.ts | 19 +++++++------ src/views/ToolTipView.js | 16 ++++++----- src/views/UsernameView.ts | 7 ++--- 30 files changed, 250 insertions(+), 169 deletions(-) rename src/views/{FlotScrobbleGraphView.js => FlotScrobbleGraphView.ts} (83%) diff --git a/public/AppController.js b/public/AppController.js index 06d5c43..07debdd 100644 --- a/public/AppController.js +++ b/public/AppController.js @@ -1,8 +1,8 @@ -let appModel = new AppModel; -let fetchModel = new FetchModel; -let graphViewModel = new FlotScrobbleGraphViewModel; -let legendModel = new LegendModel; -let scrobbleCollection = new ScrobbleCollection; +let appModel = new AppModel(); +let fetchModel = new FetchModel(); +let graphViewModel = new FlotScrobbleGraphViewModel(); +let legendModel = new LegendModel(); +let scrobbleCollection = new ScrobbleCollection(); let requestQueue = new RequestQueue({ max_n_reqs_in_progress: 4 }); diff --git a/public/models/AppModel.js b/public/models/AppModel.js index 90306db..e4d0ec0 100644 --- a/public/models/AppModel.js +++ b/public/models/AppModel.js @@ -1,12 +1,16 @@ // Global state that I couldn't find a more specific place for // It's all used to form the path of the URL. -const AppModel = Backbone.Model.extend({ - user() { return this.get("user"); }, +class AppModel extends Backbone.Model { + user() { + return this.get("user"); + } initialize() { this.set({ user: null }); this.set({ filterTerm: "" }); - }, - filterRegex() { return new RegExp(this.get("filterTerm"), "i"); }, + } + filterRegex() { + return new RegExp(this.get("filterTerm"), "i"); + } validate(attrs) { try { new RegExp(attrs.filterTerm, "i"); @@ -16,4 +20,4 @@ const AppModel = Backbone.Model.extend({ return `Whoops! That's not a regular expression: ${error}`; } } -}); +} diff --git a/public/models/FetchModel.js b/public/models/FetchModel.js index 9168d55..958e8a2 100644 --- a/public/models/FetchModel.js +++ b/public/models/FetchModel.js @@ -1,4 +1,4 @@ -const FetchModel = Backbone.Model.extend({ +class FetchModel extends Backbone.Model { initialize() { this.set({ pagesFetched: [], @@ -6,8 +6,8 @@ const FetchModel = Backbone.Model.extend({ lastPageFetched: 0, isFetching: false }); - }, - numPagesFetched() { return this.get("pagesFetched").length; }, + } + numPagesFetched() { return this.get("pagesFetched").length; } fetch_scrobbles(username) { if (!username) { throw "Invalid Username"; @@ -65,4 +65,4 @@ const FetchModel = Backbone.Model.extend({ })(); }); } -}); +} diff --git a/public/models/LegendModel.js b/public/models/LegendModel.js index a0e1614..69c1846 100644 --- a/public/models/LegendModel.js +++ b/public/models/LegendModel.js @@ -15,27 +15,33 @@ const LEGEND_COLORS = [ '#b15928', '#ffff99', ]; -const LegendModel = Backbone.Model.extend({ +class ArtistColor { +} +class LegendModel extends Backbone.Model { initialize() { this.set({ artistColors: {} }); - }, + } + get_artist_color(artist) { + return this.get('artistColors').get(artist.toLowerCase()); + } compute_artist_colors(scrobbles) { let a = _.chain(scrobbles.models) - .groupBy(s => s.artist()) + .groupBy(s => s.artist().toLowerCase()) .toArray() .sortBy('length') .reverse() .value(); - let artistColors = {}; + let artistColors = new Map(); const otherColor = LEGEND_COLORS[0]; for (let i = 0; i < a.length; i++) { let x = a[i]; - artistColors[x[0].artist()] = { + artistColors.set(x[0].artist().toLowerCase(), { + artist: x[0].artist(), color: LEGEND_COLORS[i + 1] || otherColor, count: x.length, showInLegend: !!LEGEND_COLORS[i + 1], - }; + }); } this.set({ artistColors, otherColor }); } -}); +} diff --git a/public/models/Request.js b/public/models/Request.js index d1aa4cb..df5be81 100644 --- a/public/models/Request.js +++ b/public/models/Request.js @@ -1,4 +1,4 @@ -const LastFMRequest = Backbone.Model.extend({ +class LastFMRequest extends Backbone.Model { run() { return $.ajax({ url: "https://ws.audioscrobbler.com/2.0/", @@ -30,4 +30,4 @@ const LastFMRequest = Backbone.Model.extend({ dataType: "jsonp" //, timeout: 20000. Timeout error handling is flawed. }); } -}); +} diff --git a/public/models/RequestQueue.js b/public/models/RequestQueue.js index e50e226..e40fa53 100644 --- a/public/models/RequestQueue.js +++ b/public/models/RequestQueue.js @@ -5,17 +5,18 @@ // rate limited, but not as often, and the rate limiting goes away after a few // seconds. const rate_limit_ms = 500; -const RequestQueue = Backbone.Model.extend({ - initialize() { +class RequestQueue extends Backbone.Model { + constructor() { + super(...arguments); this.queue = []; this.currentlyEmptyingQueue = false; - }, + } add(req) { this.queue.push(req); if (!this.currentlyEmptyingQueue) { this.doAnotherRequest(); } - }, + } doAnotherRequest() { if (this.queue.length === 0) { this.currentlyEmptyingQueue = false; @@ -28,8 +29,8 @@ const RequestQueue = Backbone.Model.extend({ this.doAnotherRequest(); }, rate_limit_ms); } - }, + } numReqsPending() { - return this.queue.size(); + return this.queue.length; } -}); +} diff --git a/public/models/ScrobbleCollection.js b/public/models/ScrobbleCollection.js index 8f97a82..4009e6e 100644 --- a/public/models/ScrobbleCollection.js +++ b/public/models/ScrobbleCollection.js @@ -1,12 +1,15 @@ -const Scrobble = Backbone.Model.extend({ - artist() { return this.get("artist"); }, - album() { return this.get("album"); }, - track() { return this.get("track"); }, - date() { return this.get("date"); }, +class Scrobble extends Backbone.Model { + artist() { return this.get("artist"); } + album() { return this.get("album"); } + track() { return this.get("track"); } + date() { return this.get("date"); } image() { return this.get("image"); } -}); -const ScrobbleCollection = Backbone.Collection.extend({ - model: Scrobble, +} +class ScrobbleCollection extends Backbone.Collection { + constructor() { + super(...arguments); + this.model = Scrobble; + } add_from_lastfm_json(json) { for (let scrobble of json.recenttracks.track) { // Pull out just the information we need, because memory has been known @@ -35,4 +38,4 @@ const ScrobbleCollection = Backbone.Collection.extend({ this.add(my_scrobble, { silent: true }); } } -}); +} diff --git a/public/views/DrawingThrobberView.js b/public/views/DrawingThrobberView.js index b2e76b1..be1ad81 100644 --- a/public/views/DrawingThrobberView.js +++ b/public/views/DrawingThrobberView.js @@ -1,4 +1,4 @@ -const DrawingThrobberView = Backbone.View.extend({ +class DrawingThrobberView extends Backbone.View { render() { if (graphViewModel.get("isDrawing")) { $("#drawingThrobber").show(); @@ -7,7 +7,8 @@ const DrawingThrobberView = Backbone.View.extend({ else { $("#drawingThrobber").hide(); } + return this; } -}); -const drawingThrobberView = new DrawingThrobberView; +} +const drawingThrobberView = new DrawingThrobberView(); graphViewModel.on("change:isDrawing", (model, isDrawing) => drawingThrobberView.render()); diff --git a/public/views/FetchThrobberView.js b/public/views/FetchThrobberView.js index d096f93..801339e 100644 --- a/public/views/FetchThrobberView.js +++ b/public/views/FetchThrobberView.js @@ -1,5 +1,4 @@ -const FetchThrobberView = Backbone.View.extend({ - el: "#fetchThrobber", +class FetchThrobberView extends Backbone.View { render() { if (fetchModel.get("isFetching")) { let n = fetchModel.numPagesFetched(); @@ -11,9 +10,12 @@ const FetchThrobberView = Backbone.View.extend({ else { this.$el.hide(); } + return this; } +} +const fetchThrobberView = new FetchThrobberView({ + el: "#fetchThrobber", }); -const fetchThrobberView = new FetchThrobberView; fetchModel.on("newPageFetched", () => fetchThrobberView.render()); fetchModel.on("change:isFetching", function (model) { if (model.get("isFetching")) { diff --git a/public/views/FilterBoxView.js b/public/views/FilterBoxView.js index c7f51c0..6821409 100644 --- a/public/views/FilterBoxView.js +++ b/public/views/FilterBoxView.js @@ -1,14 +1,23 @@ -const FilterBoxView = Backbone.View.extend({ - el: "#searchForm", - render(isDrawn) { - if (isDrawn) { +class FilterBoxView extends Backbone.View { + constructor() { + super(...arguments); + this.isDrawn = false; + } + render() { + if (this.isDrawn) { this.$el.fadeIn(1000); } else { this.$el.hide(); } - }, + return this; + } val() { return $("#search").val(); } +} +const filterBoxView = new FilterBoxView({ + el: "#searchForm", +}); +graphViewModel.on("change:isDrawn", (model, isDrawn) => { + filterBoxView.isDrawn = isDrawn; + filterBoxView.render(); }); -const filterBoxView = new FilterBoxView; -graphViewModel.on("change:isDrawn", (model, isDrawn) => filterBoxView.render(isDrawn)); diff --git a/public/views/FlashingScrobbleView.js b/public/views/FlashingScrobbleView.js index 1f3a720..d6b8060 100644 --- a/public/views/FlashingScrobbleView.js +++ b/public/views/FlashingScrobbleView.js @@ -1,5 +1,7 @@ -const FlashingScrobbleView = Backbone.View.extend({ - initialize() { this.flashingTimer = null; }, +class FlashingScrobbleView extends Backbone.View { + initialize() { + this.flashingTimer = null; + } render(scrobble) { if (this.flashingTimer != null) { clearInterval(this.flashingTimer); @@ -15,7 +17,8 @@ const FlashingScrobbleView = Backbone.View.extend({ } }; this.flashingTimer = setInterval(flash, 200); - }, + return this; + } remove() { if (this.flashingTimer != null) { clearInterval(this.flashingTimer); @@ -24,9 +27,10 @@ const FlashingScrobbleView = Backbone.View.extend({ if (window.plot != null) { window.plot.unhighlight(); } + return this; } -}); -const flashingScrobbleView = new FlashingScrobbleView; +} +const flashingScrobbleView = new FlashingScrobbleView(); $("#flot_container").on("plothover plotclick", function (event, pos, item) { if (item) { // we're hovering over an data point flashingScrobbleView.render(item.series.scrobble); diff --git a/public/views/FlotScrobbleGraphView.js b/public/views/FlotScrobbleGraphView.js index 64fa75c..8d7c750 100644 --- a/public/views/FlotScrobbleGraphView.js +++ b/public/views/FlotScrobbleGraphView.js @@ -1,4 +1,4 @@ -const FlotScrobbleGraphView = Backbone.View.extend({ +class FlotScrobbleGraphView extends Backbone.View { render() { if (scrobbleCollection.size() === 0) { return; @@ -9,17 +9,20 @@ const FlotScrobbleGraphView = Backbone.View.extend({ setTimeout(function () { legendModel.compute_artist_colors(scrobbleCollection); let re = appModel.filterRegex(); - let filtered_scrobbles = scrobbleCollection.filter(s => re.exec(s.track()) || re.exec(s.artist()) || re.exec(s.album())); + let filtered_scrobbles = scrobbleCollection.filter(s => re.exec(s.track()) != null || + re.exec(s.artist()) != null || + re.exec(s.album()) != null); let flot_series = construct_flot_series(filtered_scrobbles); let minTime = scrobbleCollection.min(scrobble => scrobble.date()).date(); let maxTime = scrobbleCollection.max(scrobble => scrobble.date()).date(); plot_flot_series(flot_series, minTime, maxTime); graphViewModel.set({ isDrawn: true, isDrawing: false }); }, 0); + return this; } -}); +} var construct_flot_series = function (scrobbles) { - window.track_indices = { + const track_indices = window['track_indices'] = { // Here's an example: //"snow patrol#eyes open": { //series_index: 1 @@ -31,7 +34,7 @@ var construct_flot_series = function (scrobbles) { let date = scrobble.date().getTime(); let time = scrobble.date().getHours() + (scrobble.date().getMinutes() / 60); series.push({ - color: legendModel.get("artistColors")[scrobble.artist()].color, + color: legendModel.get_artist_color(scrobble.artist()).color, data: [[date, time]], scrobble }); @@ -47,7 +50,9 @@ var construct_flot_series = function (scrobbles) { }; var plot_flot_series = function (flot_series, minTime, maxTime) { let ONE_DAY_IN_MS = 1000 * 60 * 60 * 24; - plot = $.plot($("#flot_container"), flot_series, { + // Don't have types for this old version of flot. + // @ts-ignore + window['plot'] = $.plot($("#flot_container"), flot_series, { xaxis: { min: minTime, max: maxTime, @@ -93,7 +98,7 @@ var plot_flot_series = function (flot_series, minTime, maxTime) { } }); }; -let flotScrobbleGraphView = new FlotScrobbleGraphView; +let flotScrobbleGraphView = new FlotScrobbleGraphView(); let redraw_on_response_number = 1; appModel.on("change:user", () => redraw_on_response_number = 1); fetchModel.on("newPageFetched", function () { diff --git a/public/views/LegendView.js b/public/views/LegendView.js index 6177955..f42e385 100644 --- a/public/views/LegendView.js +++ b/public/views/LegendView.js @@ -1,13 +1,11 @@ -const LegendView = Backbone.View.extend({ - el: "#legend_wrap", +class LegendView extends Backbone.View { render() { this.$("li").remove(); let artistColors = legendModel.get("artistColors"); - for (let artist in artistColors) { - let color = artistColors[artist]; + for (const color of artistColors.values()) { if (color.showInLegend) { $("
  • ") - .text(`${artist} (${color.count})`) + .text(`${color.artist} (${color.count})`) .css("color", color.color) .appendTo("#legend"); } @@ -17,10 +15,14 @@ const LegendView = Backbone.View.extend({ .css("color", legendModel.get("otherColor")) .appendTo("#legend"); this.$el.show(); - }, + return this; + } remove() { this.$el.hide(); + return this; } +} +const legendView = new LegendView({ + el: "#legend_wrap", }); -const legendView = new LegendView; legendModel.on("change:artistColors", (model, artistColors) => legendView.render()); diff --git a/public/views/ToolTipView.js b/public/views/ToolTipView.js index 01bdb14..52992bb 100644 --- a/public/views/ToolTipView.js +++ b/public/views/ToolTipView.js @@ -1,8 +1,4 @@ -const ToolTipView = Backbone.View.extend({ - tagname: "div", - className: "popover left", - id: "tooltip", - visible: false, +class ToolTipView extends Backbone.View { render(x, y, scrobble) { let template = `\
    @@ -29,9 +25,15 @@ const ToolTipView = Backbone.View.extend({ right: $(document).width() - x }; this.$el.html(tooltip_html).css(css).appendTo("body").fadeIn(200); + return this; } +} +const toolTipView = new ToolTipView({ + tagname: "div", + className: "popover left", + id: "tooltip", + visible: false, }); -const toolTipView = new ToolTipView; let previousPointIndex = null; $("#flot_container").on("plothover plotclick", function (event, pos, item) { if (item) { // we're overing over a data point diff --git a/public/views/UsernameView.js b/public/views/UsernameView.js index 76720e6..4bee58c 100644 --- a/public/views/UsernameView.js +++ b/public/views/UsernameView.js @@ -1,9 +1,10 @@ -const UsernameView = Backbone.View.extend({ +class UsernameView extends Backbone.View { render() { let user = appModel.get("user"); $(".user_link").text(user); $(".user_link").attr("href", `http://www.last.fm/user/${user}`); + return this; } -}); -const usernameView = new UsernameView; +} +const usernameView = new UsernameView(); appModel.on("change:user", () => usernameView.render()); diff --git a/src/AppController.ts b/src/AppController.ts index 506d3b4..334a407 100644 --- a/src/AppController.ts +++ b/src/AppController.ts @@ -1,8 +1,8 @@ -let appModel = new AppModel; -let fetchModel = new FetchModel; -let graphViewModel = new FlotScrobbleGraphViewModel; -let legendModel = new LegendModel; -let scrobbleCollection = new ScrobbleCollection; +let appModel = new AppModel(); +let fetchModel = new FetchModel(); +let graphViewModel = new FlotScrobbleGraphViewModel(); +let legendModel = new LegendModel(); +let scrobbleCollection = new ScrobbleCollection(); let requestQueue = new RequestQueue({ max_n_reqs_in_progress: 4 }); diff --git a/src/models/AppModel.ts b/src/models/AppModel.ts index e034b40..5907e12 100644 --- a/src/models/AppModel.ts +++ b/src/models/AppModel.ts @@ -1,12 +1,16 @@ // Global state that I couldn't find a more specific place for // It's all used to form the path of the URL. -const AppModel = Backbone.Model.extend({ - user() { return this.get("user"); }, +class AppModel extends Backbone.Model { + user() { + return this.get("user"); + } initialize() { this.set({ user: null }); this.set({ filterTerm: "" }); - }, - filterRegex() { return new RegExp(this.get("filterTerm"), "i"); }, + } + filterRegex() { + return new RegExp(this.get("filterTerm"), "i"); + } validate(attrs) { try { new RegExp(attrs.filterTerm, "i"); @@ -15,4 +19,4 @@ const AppModel = Backbone.Model.extend({ return `Whoops! That's not a regular expression: ${error}`; } } -}); +} diff --git a/src/models/FetchModel.ts b/src/models/FetchModel.ts index 70996ee..521d1fb 100644 --- a/src/models/FetchModel.ts +++ b/src/models/FetchModel.ts @@ -1,4 +1,4 @@ -const FetchModel = Backbone.Model.extend({ +class FetchModel extends Backbone.Model { initialize() { this.set({ pagesFetched: [], @@ -6,8 +6,8 @@ const FetchModel = Backbone.Model.extend({ lastPageFetched: 0, isFetching: false }); - }, - numPagesFetched() { return this.get("pagesFetched").length; }, + } + numPagesFetched() { return this.get("pagesFetched").length; } fetch_scrobbles(username) { if (!username) { throw "Invalid Username"; } @@ -69,4 +69,4 @@ const FetchModel = Backbone.Model.extend({ })(); }); } -}); +} diff --git a/src/models/LegendModel.ts b/src/models/LegendModel.ts index f6bacba..9690b0a 100644 --- a/src/models/LegendModel.ts +++ b/src/models/LegendModel.ts @@ -16,29 +16,40 @@ const LEGEND_COLORS = [ '#ffff99', ]; -const LegendModel = Backbone.Model.extend({ +class ArtistColor { + artist: string; + color: string; + count: number; + showInLegend: boolean; +} + +class LegendModel extends Backbone.Model { initialize() { this.set({ artistColors: {} }); - }, - compute_artist_colors(scrobbles) { + } + get_artist_color(artist: string): ArtistColor { + return this.get('artistColors').get(artist.toLowerCase()); + } + compute_artist_colors(scrobbles: ScrobbleCollection) { let a = _.chain(scrobbles.models) - .groupBy(s => s.artist()) + .groupBy(s => s.artist().toLowerCase()) .toArray() .sortBy('length') .reverse() .value(); - let artistColors = {}; + let artistColors: Map = new Map(); const otherColor = LEGEND_COLORS[0]; for (let i = 0; i < a.length; i++) { let x = a[i]; - artistColors[x[0].artist()] = { + artistColors.set(x[0].artist().toLowerCase(), { + artist: x[0].artist(), color: LEGEND_COLORS[i + 1] || otherColor, count: x.length, showInLegend: !!LEGEND_COLORS[i + 1], - }; + }); } this.set({ artistColors, otherColor }); } -}); +} diff --git a/src/models/Request.ts b/src/models/Request.ts index 1ffc555..0b7b0ea 100644 --- a/src/models/Request.ts +++ b/src/models/Request.ts @@ -1,4 +1,4 @@ -const LastFMRequest = Backbone.Model.extend({ +class LastFMRequest extends Backbone.Model { run() { return $.ajax({ url: "https://ws.audioscrobbler.com/2.0/", @@ -27,4 +27,4 @@ const LastFMRequest = Backbone.Model.extend({ dataType: "jsonp" //, timeout: 20000. Timeout error handling is flawed. }); } -}); +} diff --git a/src/models/RequestQueue.ts b/src/models/RequestQueue.ts index 58b235a..79c4b6a 100644 --- a/src/models/RequestQueue.ts +++ b/src/models/RequestQueue.ts @@ -6,17 +6,17 @@ // seconds. const rate_limit_ms = 500; -const RequestQueue = Backbone.Model.extend({ - initialize() { - this.queue = []; - this.currentlyEmptyingQueue = false; - }, +class RequestQueue extends Backbone.Model { + queue = []; + currentlyEmptyingQueue = false; + add(req) { this.queue.push(req); if (!this.currentlyEmptyingQueue) { this.doAnotherRequest(); } - }, + } + doAnotherRequest() { if (this.queue.length === 0) { this.currentlyEmptyingQueue = false; @@ -28,8 +28,8 @@ const RequestQueue = Backbone.Model.extend({ this.doAnotherRequest(); }, rate_limit_ms); } - }, + } numReqsPending() { - return this.queue.size(); + return this.queue.length; } -}); +} diff --git a/src/models/ScrobbleCollection.ts b/src/models/ScrobbleCollection.ts index 743d27c..fbda276 100644 --- a/src/models/ScrobbleCollection.ts +++ b/src/models/ScrobbleCollection.ts @@ -1,13 +1,14 @@ -const Scrobble = Backbone.Model.extend({ - artist() { return this.get("artist"); }, - album() { return this.get("album"); }, - track() { return this.get("track"); }, - date() { return this.get("date"); }, - image() { return this.get("image"); } -}); +class Scrobble extends Backbone.Model { + artist(): string { return this.get("artist"); } + album(): string { return this.get("album"); } + track(): string { return this.get("track"); } + date(): Date { return this.get("date"); } + image(): string { return this.get("image"); } +} + +class ScrobbleCollection extends Backbone.Collection { + model = Scrobble; -const ScrobbleCollection = Backbone.Collection.extend({ - model: Scrobble, add_from_lastfm_json(json) { for (let scrobble of json.recenttracks.track) { // Pull out just the information we need, because memory has been known @@ -37,4 +38,4 @@ const ScrobbleCollection = Backbone.Collection.extend({ this.add(my_scrobble, { silent: true }); } } -}); +} diff --git a/src/views/DrawingThrobberView.ts b/src/views/DrawingThrobberView.ts index c38da59..ffba9d2 100644 --- a/src/views/DrawingThrobberView.ts +++ b/src/views/DrawingThrobberView.ts @@ -1,4 +1,4 @@ -const DrawingThrobberView = Backbone.View.extend({ +class DrawingThrobberView extends Backbone.View { render() { if (graphViewModel.get("isDrawing")) { $("#drawingThrobber").show(); @@ -6,9 +6,10 @@ const DrawingThrobberView = Backbone.View.extend({ } else { $("#drawingThrobber").hide(); } + return this; } -}); +} -const drawingThrobberView = new DrawingThrobberView; +const drawingThrobberView = new DrawingThrobberView(); graphViewModel.on("change:isDrawing", (model, isDrawing) => drawingThrobberView.render()); diff --git a/src/views/FetchThrobberView.ts b/src/views/FetchThrobberView.ts index ce70636..a412d8a 100644 --- a/src/views/FetchThrobberView.ts +++ b/src/views/FetchThrobberView.ts @@ -1,5 +1,4 @@ -const FetchThrobberView = Backbone.View.extend({ - el: "#fetchThrobber", +class FetchThrobberView extends Backbone.View { render() { if (fetchModel.get("isFetching")) { let n = fetchModel.numPagesFetched(); @@ -10,10 +9,13 @@ const FetchThrobberView = Backbone.View.extend({ } else { this.$el.hide(); } + return this; } -}); +} -const fetchThrobberView = new FetchThrobberView; +const fetchThrobberView = new FetchThrobberView({ + el: "#fetchThrobber", +}); fetchModel.on("newPageFetched", () => fetchThrobberView.render()); diff --git a/src/views/FilterBoxView.ts b/src/views/FilterBoxView.ts index daa8130..48c0ce4 100644 --- a/src/views/FilterBoxView.ts +++ b/src/views/FilterBoxView.ts @@ -1,15 +1,21 @@ -const FilterBoxView = Backbone.View.extend({ - el: "#searchForm", - render(isDrawn) { - if (isDrawn) { +class FilterBoxView extends Backbone.View { + isDrawn = false; + render() { + if (this.isDrawn) { this.$el.fadeIn(1000); } else { this.$el.hide(); } - }, + return this; + } val() { return $("#search").val(); } -}); +} -const filterBoxView = new FilterBoxView; +const filterBoxView = new FilterBoxView({ + el: "#searchForm", +}); -graphViewModel.on("change:isDrawn", (model, isDrawn) => filterBoxView.render(isDrawn)); +graphViewModel.on("change:isDrawn", (model, isDrawn) => { + filterBoxView.isDrawn = isDrawn; + filterBoxView.render() +}); diff --git a/src/views/FlashingScrobbleView.js b/src/views/FlashingScrobbleView.js index 7320634..9df5f89 100644 --- a/src/views/FlashingScrobbleView.js +++ b/src/views/FlashingScrobbleView.js @@ -1,5 +1,7 @@ -const FlashingScrobbleView = Backbone.View.extend({ - initialize() { this.flashingTimer = null; }, +class FlashingScrobbleView extends Backbone.View { + initialize() { + this.flashingTimer = null; + } render(scrobble) { if (this.flashingTimer != null) { clearInterval(this.flashingTimer); } let flashOn = false; @@ -13,15 +15,17 @@ const FlashingScrobbleView = Backbone.View.extend({ } }; this.flashingTimer = setInterval(flash, 200); - }, + return this; + } remove() { if (this.flashingTimer != null) { clearInterval(this.flashingTimer); } this.flashingTimer = null; if (window.plot != null) { window.plot.unhighlight(); } + return this; } -}); +} -const flashingScrobbleView = new FlashingScrobbleView; +const flashingScrobbleView = new FlashingScrobbleView(); $("#flot_container").on("plothover plotclick", function (event, pos, item) { if (item) { // we're hovering over an data point diff --git a/src/views/FlotScrobbleGraphView.js b/src/views/FlotScrobbleGraphView.ts similarity index 83% rename from src/views/FlotScrobbleGraphView.js rename to src/views/FlotScrobbleGraphView.ts index af225d0..ac07fd6 100644 --- a/src/views/FlotScrobbleGraphView.js +++ b/src/views/FlotScrobbleGraphView.ts @@ -1,4 +1,4 @@ -const FlotScrobbleGraphView = Backbone.View.extend({ +class FlotScrobbleGraphView extends Backbone.View { render() { if (scrobbleCollection.size() === 0) { return; } graphViewModel.set({ isDrawing: true }); @@ -9,7 +9,10 @@ const FlotScrobbleGraphView = Backbone.View.extend({ legendModel.compute_artist_colors(scrobbleCollection); let re = appModel.filterRegex(); - let filtered_scrobbles = scrobbleCollection.filter(s => re.exec(s.track()) || re.exec(s.artist()) || re.exec(s.album())); + let filtered_scrobbles = scrobbleCollection.filter(s => + re.exec(s.track()) != null || + re.exec(s.artist()) != null || + re.exec(s.album()) != null); let flot_series = construct_flot_series(filtered_scrobbles); @@ -19,11 +22,12 @@ const FlotScrobbleGraphView = Backbone.View.extend({ plot_flot_series(flot_series, minTime, maxTime); graphViewModel.set({ isDrawn: true, isDrawing: false }); }, 0); + return this; } -}); +} -var construct_flot_series = function (scrobbles) { - window.track_indices = { +var construct_flot_series = function (scrobbles: Scrobble[]) { + const track_indices = window['track_indices'] = { // Here's an example: //"snow patrol#eyes open": { //series_index: 1 @@ -37,7 +41,7 @@ var construct_flot_series = function (scrobbles) { let date = scrobble.date().getTime(); let time = scrobble.date().getHours() + (scrobble.date().getMinutes() / 60); series.push({ - color: legendModel.get("artistColors")[scrobble.artist()].color, + color: legendModel.get_artist_color(scrobble.artist()).color, data: [[date, time]], scrobble }); @@ -54,7 +58,9 @@ var construct_flot_series = function (scrobbles) { var plot_flot_series = function (flot_series, minTime, maxTime) { let ONE_DAY_IN_MS = 1000 * 60 * 60 * 24; - plot = $.plot($("#flot_container"), flot_series, { + // Don't have types for this old version of flot. + // @ts-ignore + window['plot'] = $.plot($("#flot_container"), flot_series, { xaxis: { min: minTime, max: maxTime, @@ -96,7 +102,7 @@ var plot_flot_series = function (flot_series, minTime, maxTime) { }); }; -let flotScrobbleGraphView = new FlotScrobbleGraphView; +let flotScrobbleGraphView = new FlotScrobbleGraphView(); let redraw_on_response_number = 1; diff --git a/src/views/LegendView.ts b/src/views/LegendView.ts index 31bcdb7..06d04d4 100644 --- a/src/views/LegendView.ts +++ b/src/views/LegendView.ts @@ -1,13 +1,11 @@ -const LegendView = Backbone.View.extend({ - el: "#legend_wrap", +class LegendView extends Backbone.View { render() { this.$("li").remove(); let artistColors = legendModel.get("artistColors"); - for (let artist in artistColors) { - let color = artistColors[artist]; + for (const color of artistColors.values()) { if (color.showInLegend) { $("
  • ") - .text(`${artist} (${color.count})`) + .text(`${color.artist} (${color.count})`) .css("color", color.color) .appendTo("#legend"); } @@ -17,12 +15,17 @@ const LegendView = Backbone.View.extend({ .css("color", legendModel.get("otherColor")) .appendTo("#legend"); this.$el.show(); - }, + return this; + } + remove() { this.$el.hide(); + return this; } -}); +} -const legendView = new LegendView; +const legendView = new LegendView({ + el: "#legend_wrap", +}); legendModel.on("change:artistColors", (model, artistColors) => legendView.render()); diff --git a/src/views/ToolTipView.js b/src/views/ToolTipView.js index c073a56..f874ca7 100644 --- a/src/views/ToolTipView.js +++ b/src/views/ToolTipView.js @@ -1,8 +1,4 @@ -const ToolTipView = Backbone.View.extend({ - tagname: "div", - className: "popover left", - id: "tooltip", - visible: false, +class ToolTipView extends Backbone.View { render(x, y, scrobble) { let template = `\
    @@ -33,10 +29,16 @@ const ToolTipView = Backbone.View.extend({ }; this.$el.html(tooltip_html).css(css).appendTo("body").fadeIn(200); + return this; } -}); +} -const toolTipView = new ToolTipView; +const toolTipView = new ToolTipView({ + tagname: "div", + className: "popover left", + id: "tooltip", + visible: false, +}); let previousPointIndex = null; diff --git a/src/views/UsernameView.ts b/src/views/UsernameView.ts index 3b5d732..b736d8f 100644 --- a/src/views/UsernameView.ts +++ b/src/views/UsernameView.ts @@ -1,10 +1,11 @@ -const UsernameView = Backbone.View.extend({ +class UsernameView extends Backbone.View { render() { let user = appModel.get("user"); $(".user_link").text(user); $(".user_link").attr("href", `http://www.last.fm/user/${user}`); + return this; } -}); +} -const usernameView = new UsernameView; +const usernameView = new UsernameView(); appModel.on("change:user", () => usernameView.render());