PageSourceSearch

https://oscarl.com/javascript/tour.js?v=20261006d

js oscarl.com collected 2026-10-09 01:40:42 UTC 8,058 bytes, 151 lines download raw bytes

1// Custom tour dates list. Pulls events from Bandsintown's public REST API and
2// renders them in the site's own style. If the request fails for any reason the
3// original Bandsintown widget is left untouched (it is only hidden on success).
4(function () {
5    "use strict";
6
7    var ARTIST = "id_390867";
8    var APP_ID = "js_www.oscarl.com";
9    var API = "https://rest.bandsintown.com/artists/" + ARTIST + "/events";
10    var MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
11    var DAYS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
12
13    function esc(str) {
14        var div = document.createElement("div");
15        div.textContent = str == null ? "" : String(str);
16        return div.innerHTML;
17    }
18
19    function ticketUrl(ev) {
20        var offers = ev.offers || [];
21        for (var i = 0; i < offers.length; i++) {
22            if (offers[i].type === "Tickets" && offers[i].url) return offers[i].url;
23        }
24        return "";
25    }
26
27    function row(ev) {
28        var m = /^(\d{4})-(\d{2})-(\d{2})/.exec(ev.datetime || "");
29        if (!m) return "";
30        var d = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3]));
31        var v = ev.venue || {};
32        var place = [v.city, v.region && v.country === "United States" ? v.region : "", v.country].filter(Boolean).join(", ");
33        var tickets = ticketUrl(ev);
34        var past = new Date(ev.datetime) < new Date(Date.now() - 864e5);
35        var more = ev.url || "https://www.bandsintown.com/a/390867-oscar-l";
36        var btn = past
37            ? ""
38            : (tickets
39                ? '<a class="tour-btn tour-btn-solid" href="' + esc(tickets) + '" target="_blank" rel="noopener">TICKETS</a>'
40                : '<a class="tour-btn" href="' + esc(more) + '" target="_blank" rel="noopener">INFO</a>');
41        return (
42            '<div class="tour-row">' +
43            '<div class="tour-date"><span class="tour-day">' + m[3] + '</span>' +
44            '<span class="tour-month">' + MONTHS[d.getUTCMonth()] + (past ? " " + m[1] : "") + '</span></div>' +
45            '<div class="tour-info"><span class="tour-venue">' + esc(v.name || ev.title || "") + '</span>' +
46            '<span class="tour-place">' + esc(DAYS[d.getUTCDay()]) + ' · ' + esc(place) + '</span></div>' +
47            '<div class="tour-actions">' + btn + '</div>' +
48            '</div>'
49        );
50    }
51
52    function load(kind) {
53        return fetch(API + "?app_id=" + APP_ID + "&date=" + kind)
54            .then(function (r) {
55                if (!r.ok) throw new Error("HTTP " + r.status);
56                return r.json();
57            })
58            .then(function (list) {
59                if (!Array.isArray(list)) throw new Error("bad payload");
60                return list;
61            });
62    }
63
64    function init() {
65        var box = document.getElementById("tour-list");
66        if (!box) return;
67        var wrap = box.parentNode;
68        var pastCache = null;
69
70        load("upcoming").then(function (upcoming) {
71            box.innerHTML =
72                '<div class="tour-tabs"><button type="button" class="tour-tab is-active" data-k="up">UPCOMING</button>' +
73                '<button type="button" class="tour-tab" data-k="past">PAST</button></div>' +
74                '<div class="tour-rows" id="tour-rows"></div>';
75            var rows = document.getElementById("tour-rows");
76
77            function show(list, empty) {
78                rows.innerHTML = list.length ? list.map(row).join("") : '<p class="tour-empty">' + empty + '</p>';
79            }
80            show(upcoming, "No upcoming dates right now. Follow on Bandsintown to get notified.");
81            wrap.classList.add("tour-custom");
82
83            var link = '<p class="tour-empty">More past dates on <a class="tour-link" href="https://www.bandsintown.com/a/390867-oscar-l?came_from=267&utm_medium=web&utm_source=oscarl.com&past=true" target="_blank" rel="noopener">Bandsintown</a>.</p>';
84            var pageSize = 10;
85
86            function showPast(page) {
87                var total = pastCache.length;
88                var pages = Math.max(1, Math.ceil(total / pageSize));
89                page = Math.min(Math.max(1, page), pages);
90                var slice = pastCache.slice((page - 1) * pageSize, page * pageSize);
91                var opts = [5, 10, 25, 50].map(function (n) {
92                    return '<option value="' + n + '"' + (n === pageSize ? " selected" : "") + '>
92' + n + '</option>';
93                }).join("");
94                rows.innerHTML = slice.map(row).join("") +
95                    '<div class="tour-pager">' +
96                    '<label class="tour-per">Per page <select class="tour-select">' + opts + '</select></label>' +
97                    '<span class="tour-nav">' +
98                    '<button type="button" class="tour-page-btn" data-p="' + (page - 1) + '"' + (page <= 1 ? " disabled" : "") + '>&larr; PREV</button>' +
99                    '<span class="tour-count">' + page + ' / ' + pages + ' <em>(' + total + ' shows)</em></span>' +
100                    '<button type="button" class="tour-page-btn" data-p="' + (page + 1) + '"' + (page >= pages ? " disabled" : "") + '>NEXT &rarr;</button>' +
101                    '</span></div>' + link;
102            }
103
104            rows.addEventListener("click", function (e) {
105                var b = e.target.closest ? e.target.closest(".tour-page-btn") : null;
106                if (!b || b.disabled) return;
107                showPast(+b.getAttribute("data-p"));
108                box.scrollIntoView({ behavior: "smooth", block: "start" });
109            });
110            rows.addEventListener("change", function (e) {
111                if (!e.target.classList.contains("tour-select")) return;
112                pageSize = +e.target.value;
113                showPast(1);
114            });
115
116            box.addEventListener("click", function (e) {
117                var t = e.target;
118                if (!t.classList || !t.classList.contains("tour-tab")) return;
119                var tabs = box.querySelectorAll(".tour-tab");
120                for (var i = 0; i < tabs.length; i++) tabs[i].classList.remove("is-active");
121                t.classList.add("is-active");
122                if (t.getAttribute("data-k") === "up") return show(upcoming, "No upcoming dates right now.");
123                if (pastCache) return showPast(1);
124                rows.innerHTML = '<p class="tour-empty">Loading…</p>'; '<p class="tour-empty">More past dates on <a class="tour-link" href="https://www.bandsintown.com/a/390867-oscar-l?came_from=267&utm_medium=web&utm_source=oscarl.com&past=true" target="_blank" rel="noopener">Bandsintown</a>.</p>';
125                // Bandsintown's API returns no past dates for this app id, so past shows come from
126                // tour-history.json (kept up to date daily by scripts/update_tour_history.py).
127                fetch("tour-history.json", { cache: "no-store" })
128                    .then(function (r) { if (!r.ok) throw new Error("HTTP " + r.status); return r.json(); })
129                    .then(function (d) {
130                        var today = new Date().toISOString().slice(0, 10);
131                        var list = ((d && d.events) || [])
132                            .filter(function (e) { return (e.datetime || "").slice(0, 10) < today; })
133                            .map(function (e) {
134                                return { datetime: e.datetime, url: e.url, offers: [],
135                                    venue: { name: e.venue, city: e.city, region: e.region, country: e.country } };
136                            });
137                        pastCache = list;
138                        if (!list.length) { rows.innerHTML = link; return; }
139                        showPast(1);
140                    })
141                    .catch(function () { rows.innerHTML = link; });
142            });
143        }).catch(function () { /* keep the Bandsintown widget as is */ });
144    }
145
146    if (document.readyState === "loading") {
147        document.addEventListener("DOMContentLoaded", init);
148    } else {
149        init();
150    }
151})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.