Refactor scripts

This commit is contained in:
Eduard Urbach 2019-11-18 11:04:13 +09:00
parent 7e25ee6faf
commit 1ddcd4d570
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
33 changed files with 670 additions and 749 deletions

View File

@ -1,5 +1,5 @@
import requestIdleCallback from "scripts/Utils/requestIdleCallback"
import AnimeNotifier from "../AnimeNotifier" import AnimeNotifier from "../AnimeNotifier"
import { requestIdleCallback } from "../Utils"
// Load // Load
export function load(arn: AnimeNotifier, element: HTMLElement) { export function load(arn: AnimeNotifier, element: HTMLElement) {

View File

@ -1,5 +1,6 @@
import emptyPixel from "scripts/Utils/emptyPixel"
import findAll from "scripts/Utils/findAll"
import AnimeNotifier from "../AnimeNotifier" import AnimeNotifier from "../AnimeNotifier"
import { findAll } from "scripts/Utils"
// Filter anime on explore page // Filter anime on explore page
export function filterAnime(arn: AnimeNotifier, _: HTMLInputElement) { export function filterAnime(arn: AnimeNotifier, _: HTMLInputElement) {
@ -12,7 +13,7 @@ export function filterAnime(arn: AnimeNotifier, _: HTMLInputElement) {
for(const element of findAll("anime-grid-image")) { for(const element of findAll("anime-grid-image")) {
const img = element as HTMLImageElement const img = element as HTMLImageElement
img.src = arn.emptyPixel() img.src = emptyPixel
img.classList.remove("element-found") img.classList.remove("element-found")
img.classList.remove("element-color-preview") img.classList.remove("element-color-preview")
} }

View File

@ -2,24 +2,24 @@ import AnimeNotifier from "../AnimeNotifier"
// Enable notifications // Enable notifications
export async function enableNotifications(arn: AnimeNotifier, _: HTMLElement) { export async function enableNotifications(arn: AnimeNotifier, _: HTMLElement) {
if(!arn.user || !arn.user.dataset.id) { if(!arn.user) {
return return
} }
arn.statusMessage.showInfo("Enabling instant notifications...") arn.statusMessage.showInfo("Enabling instant notifications...")
await arn.pushManager.subscribe(arn.user.dataset.id) await arn.pushManager.subscribe(arn.user.id)
arn.updatePushUI() arn.updatePushUI()
arn.statusMessage.showInfo("Enabled instant notifications for this device.") arn.statusMessage.showInfo("Enabled instant notifications for this device.")
} }
// Disable notifications // Disable notifications
export async function disableNotifications(arn: AnimeNotifier, _: HTMLElement) { export async function disableNotifications(arn: AnimeNotifier, _: HTMLElement) {
if(!arn.user || !arn.user.dataset.id) { if(!arn.user) {
return return
} }
arn.statusMessage.showInfo("Disabling instant notifications...") arn.statusMessage.showInfo("Disabling instant notifications...")
await arn.pushManager.unsubscribe(arn.user.dataset.id) await arn.pushManager.unsubscribe(arn.user.id)
arn.updatePushUI() arn.updatePushUI()
arn.statusMessage.showInfo("Disabled instant notifications for this device.") arn.statusMessage.showInfo("Disabled instant notifications for this device.")
} }

View File

@ -1,6 +1,7 @@
import Diff from "scripts/Diff"
import delay from "scripts/Utils/delay"
import requestIdleCallback from "scripts/Utils/requestIdleCallback"
import AnimeNotifier from "../AnimeNotifier" import AnimeNotifier from "../AnimeNotifier"
import { delay, requestIdleCallback } from "../Utils"
import Diff from "scripts/Diff";
// Search page reference // Search page reference
let emptySearchHTML = "" let emptySearchHTML = ""
@ -111,7 +112,7 @@ export async function search(arn: AnimeNotifier, search: HTMLInputElement, evt?:
searchPageTitle.textContent = document.title searchPageTitle.textContent = document.title
if(!term || term.length < 1) { if(!term || term.length < 1) {
await arn.innerHTML(searchPage, emptySearchHTML) await Diff.innerHTML(searchPage, emptySearchHTML)
arn.app.emit("DOMContentLoaded") arn.app.emit("DOMContentLoaded")
return return
} }
@ -168,7 +169,7 @@ function showResponseInElement(arn: AnimeNotifier, url: string, typeName: string
correctResponseRendered[typeName] = true correctResponseRendered[typeName] = true
} }
await arn.innerHTML(element, html) await Diff.innerHTML(element, html)
arn.onNewContent(element) arn.onNewContent(element)
} }
} }

View File

@ -1,5 +1,5 @@
import hexToHSL from "scripts/Utils/hexToHSL"
import AnimeNotifier from "../AnimeNotifier" import AnimeNotifier from "../AnimeNotifier"
import { hexToHSL } from "scripts/Utils"
let currentThemeName = "light" let currentThemeName = "light"
let previewTimeoutID: number = 0 let previewTimeoutID: number = 0
@ -7,8 +7,8 @@ let previewTimeoutID: number = 0
// let themeWheelTimeoutID: number = 0 // let themeWheelTimeoutID: number = 0
const themes = { const themes = {
"light": {}, light: {},
"dark": { dark: {
"link-color-h": "45", "link-color-h": "45",
"link-color-s": "100%", "link-color-s": "100%",
"link-color-l": "66%", "link-color-l": "66%",
@ -117,7 +117,7 @@ export function applyThemeAndPreview(arn: AnimeNotifier, themeName: string) {
clearTimeout(previewTimeoutID) clearTimeout(previewTimeoutID)
// If it's the free light theme or a PRO user, nothing to do here // If it's the free light theme or a PRO user, nothing to do here
if(currentThemeName === "light" || (arn.user && arn.user.dataset.pro == "true")) { if(currentThemeName === "light" || (arn.user && arn.user.IsPro())) {
return return
} }

View File

@ -1,12 +1,13 @@
import bytesHumanReadable from "scripts/Utils/bytesHumanReadable"
import uploadWithProgress from "scripts/Utils/uploadWithProgress"
import AnimeNotifier from "../AnimeNotifier" import AnimeNotifier from "../AnimeNotifier"
import { bytesHumanReadable, uploadWithProgress } from "../Utils"
// Select file // Select file
export function selectFile(arn: AnimeNotifier, button: HTMLButtonElement) { export function selectFile(arn: AnimeNotifier, button: HTMLButtonElement) {
const fileType = button.dataset.type const fileType = button.dataset.type
const endpoint = button.dataset.endpoint const endpoint = button.dataset.endpoint
if(endpoint === "/api/upload/user/cover" && arn.user && arn.user.dataset.pro !== "true") { if(endpoint === "/api/upload/user/cover" && arn.user && !arn.user.IsPro()) {
alert("Please buy a PRO account to use this feature.") alert("Please buy a PRO account to use this feature.")
return return
} }

View File

@ -1,41 +1,39 @@
export default class Analytics { export function uploadAnalytics() {
push() { const analytics = {
const analytics = { general: {
general: { timezoneOffset: new Date().getTimezoneOffset()
timezoneOffset: new Date().getTimezoneOffset() },
}, screen: {
screen: { availableHeight: screen.availHeight,
width: screen.width, availableWidth: screen.availWidth,
height: screen.height, height: screen.height,
availableWidth: screen.availWidth, pixelRatio: window.devicePixelRatio,
availableHeight: screen.availHeight, width: screen.width
pixelRatio: window.devicePixelRatio },
}, system: {
system: { cpuCount: navigator.hardwareConcurrency,
cpuCount: navigator.hardwareConcurrency, platform: navigator.platform
platform: navigator.platform },
}, connection: {
connection: { downLink: 0,
downLink: 0, effectiveType: "",
roundTripTime: 0, roundTripTime: 0
effectiveType: ""
}
} }
if("connection" in navigator) {
const connection = navigator["connection"] as any
analytics.connection = {
downLink: connection.downlink,
roundTripTime: connection.rtt,
effectiveType: connection.effectiveType
}
}
fetch("/dark-flame-master", {
method: "POST",
credentials: "same-origin",
body: JSON.stringify(analytics)
})
} }
if("connection" in navigator) {
const connection = navigator["connection"] as any
analytics.connection = {
downLink: connection.downlink,
roundTripTime: connection.rtt,
effectiveType: connection.effectiveType
}
}
fetch("/dark-flame-master", {
method: "POST",
credentials: "same-origin",
body: JSON.stringify(analytics)
})
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
import Diff from "./Diff" import Diff from "./Diff"
import LoadOptions from "./LoadOptions" import LoadOptions from "./LoadOptions"
import { delay } from "./Utils" import delay from "./Utils/delay"
export default class Application { export default class Application {
public originalPath: string public originalPath: string

View File

@ -1,4 +1,4 @@
import { plural } from "./Utils" import plural from "./Utils/plural"
const oneSecond = 1000 const oneSecond = 1000
const oneMinute = 60 * oneSecond const oneMinute = 60 * oneSecond

View File

@ -1,39 +0,0 @@
import Diff from "./Diff"
export default class InfiniteScroller {
container: HTMLElement
threshold: number
constructor(container, threshold) {
this.container = container
this.threshold = threshold
const check = () => {
if(this.container.scrollTop + this.container.clientHeight >= this.container.scrollHeight - threshold) {
this.loadMore()
}
}
this.container.addEventListener("scroll", _ => {
// Wait for mutations to finish before checking if we need infinite scroll to trigger.
if(Diff.mutations.mutations.length > 0) {
Diff.mutations.wait(() => check())
return
}
// Otherwise, queue up the check immediately.
// Don't call check() directly to make scrolling as smooth as possible.
Diff.mutations.queue(check)
})
}
loadMore() {
const button = document.getElementById("load-more-button")
if(!button) {
return
}
button.click()
}
}

View File

@ -9,15 +9,15 @@ const timeCapacity = 6.5
// It checks the time used to process these mutations and if the time is over the // It checks the time used to process these mutations and if the time is over the
// defined time capacity, it will pause and continue the mutations in the next frame. // defined time capacity, it will pause and continue the mutations in the next frame.
export default class MutationQueue { export default class MutationQueue {
mutations: Array<() => void> private mutations: Array<() => void>
onClearCallBacks: Array<() => void> private onClearCallBacks: Array<() => void>
constructor() { constructor() {
this.mutations = [] this.mutations = []
this.onClearCallBacks = [] this.onClearCallBacks = []
} }
queue(mutation: () => void) { public queue(mutation: () => void) {
this.mutations.push(mutation) this.mutations.push(mutation)
if(this.mutations.length === 1) { if(this.mutations.length === 1) {
@ -25,7 +25,20 @@ export default class MutationQueue {
} }
} }
mutateAll() { public wait(callBack: () => void) {
if(this.mutations.length === 0) {
callBack()
return
}
this.onClearCallBacks.push(callBack)
}
public length() {
return this.mutations.length
}
private mutateAll() {
const start = performance.now() const start = performance.now()
for(let i = 0; i < this.mutations.length; i++) { for(let i = 0; i < this.mutations.length; i++) {
@ -45,7 +58,7 @@ export default class MutationQueue {
this.clear() this.clear()
} }
clear() { private clear() {
this.mutations.length = 0 this.mutations.length = 0
if(this.onClearCallBacks.length > 0) { if(this.onClearCallBacks.length > 0) {
@ -56,13 +69,4 @@ export default class MutationQueue {
this.onClearCallBacks.length = 0 this.onClearCallBacks.length = 0
} }
} }
wait(callBack: () => void) {
if(this.mutations.length === 0) {
callBack()
return
}
this.onClearCallBacks.push(callBack)
}
} }

View File

@ -0,0 +1,3 @@
export default class ServerEvent {
public data: string
}

View File

@ -0,0 +1,95 @@
import AnimeNotifier from "../AnimeNotifier"
import plural from "../Utils/plural"
import ServerEvent from "./ServerEvent"
const reconnectDelay = 3000
let supported: boolean
let eventSource: EventSource
let arn: AnimeNotifier
let etags: Map<string, string>
export default function receiveServerEvents(animeNotifier: AnimeNotifier) {
supported = ("EventSource" in window)
if(!supported) {
return
}
arn = animeNotifier
etags = new Map<string, string>()
connect()
}
function connect() {
if(eventSource) {
eventSource.close()
}
eventSource = new EventSource("/api/sse/events", {
withCredentials: true
})
eventSource.addEventListener("ping", (e: any) => ping(e))
eventSource.addEventListener("etag", (e: any) => etag(e))
eventSource.addEventListener("activity", (e: any) => activity(e))
eventSource.addEventListener("notificationCount", (e: any) => notificationCount(e))
eventSource.onerror = () => {
setTimeout(() => connect(), reconnectDelay)
}
}
function ping(_: ServerEvent) {
console.log("ping")
}
function etag(e: ServerEvent) {
const data = JSON.parse(e.data)
const oldETag = etags.get(data.url)
const newETag = data.etag
if(oldETag && newETag && oldETag !== newETag) {
arn.statusMessage.showInfo("A new version of the website is available. Please refresh the page.", -1)
}
etags.set(data.url, newETag)
}
function activity(e: ServerEvent) {
if(!location.pathname.startsWith("/activity")) {
return
}
const isFollowingUser = JSON.parse(e.data)
// If we're on the followed only feed and we receive an activity
// about a user we don't follow, ignore the message.
if(location.pathname.startsWith("/activity/followed") && !isFollowingUser) {
return
}
const button = document.getElementById("load-new-activities")
if(!button || !button.dataset.count) {
return
}
const buttonText = document.getElementById("load-new-activities-text")
if(!buttonText) {
return
}
const newCount = parseInt(button.dataset.count, 10) + 1
button.dataset.count = newCount.toString()
buttonText.textContent = plural(newCount, "new activity")
}
function notificationCount(e: ServerEvent) {
if(!arn.notificationManager) {
return
}
arn.notificationManager.setCounter(parseInt(e.data, 10))
}

View File

@ -1,100 +0,0 @@
import AnimeNotifier from "./AnimeNotifier"
import { plural } from "./Utils"
const reconnectDelay = 3000
class ServerEvent {
data: string
}
export default class ServerEvents {
supported: boolean
eventSource: EventSource
arn: AnimeNotifier
etags: Map<string, string>
constructor(arn: AnimeNotifier) {
this.supported = ("EventSource" in window)
if(!this.supported) {
return
}
this.arn = arn
this.etags = new Map<string, string>()
this.connect()
}
connect() {
if(this.eventSource) {
this.eventSource.close()
}
this.eventSource = new EventSource("/api/sse/events", {
withCredentials: true
})
this.eventSource.addEventListener("ping", (e: any) => this.ping(e))
this.eventSource.addEventListener("etag", (e: any) => this.etag(e))
this.eventSource.addEventListener("activity", (e: any) => this.activity(e))
this.eventSource.addEventListener("notificationCount", (e: any) => this.notificationCount(e))
this.eventSource.onerror = () => {
setTimeout(() => this.connect(), reconnectDelay)
}
}
ping(_: ServerEvent) {
console.log("ping")
}
etag(e: ServerEvent) {
const data = JSON.parse(e.data)
const oldETag = this.etags.get(data.url)
const newETag = data.etag
if(oldETag && newETag && oldETag != newETag) {
this.arn.statusMessage.showInfo("A new version of the website is available. Please refresh the page.", -1)
}
this.etags.set(data.url, newETag)
}
activity(e: ServerEvent) {
if(!location.pathname.startsWith("/activity")) {
return
}
const isFollowingUser = JSON.parse(e.data)
// If we're on the followed only feed and we receive an activity
// about a user we don't follow, ignore the message.
if(location.pathname.startsWith("/activity/followed") && !isFollowingUser) {
return
}
const button = document.getElementById("load-new-activities")
if(!button || !button.dataset.count) {
return
}
const buttonText = document.getElementById("load-new-activities-text")
if(!buttonText) {
return
}
const newCount = parseInt(button.dataset.count) + 1
button.dataset.count = newCount.toString()
buttonText.textContent = plural(newCount, "new activity")
}
notificationCount(e: ServerEvent) {
if(!this.arn.notificationManager) {
return
}
this.arn.notificationManager.setCounter(parseInt(e.data))
}
}

View File

@ -1,66 +1,25 @@
import AnimeNotifier from "./AnimeNotifier" import AnimeNotifier from "./AnimeNotifier"
export default class ServiceWorkerManager { export default class ServiceWorkerManager {
arn: AnimeNotifier private arn: AnimeNotifier
uri: string private uri: string
constructor(arn: AnimeNotifier, uri: string) { constructor(arn: AnimeNotifier, uri: string) {
this.arn = arn this.arn = arn
this.uri = uri this.uri = uri
} }
register() { public register() {
if(!("serviceWorker" in navigator)) { if(!("serviceWorker" in navigator)) {
console.warn("service worker not supported, skipping registration") console.warn("service worker not supported, skipping registration")
return return
} }
navigator.serviceWorker.register(this.uri) navigator.serviceWorker.register(this.uri)
navigator.serviceWorker.addEventListener("message", evt => this.onMessage(evt))
navigator.serviceWorker.addEventListener("message", evt => {
this.onMessage(evt)
})
// This will send a message to the service worker that the DOM has been loaded
const sendContentLoadedEvent = () => {
if(!navigator.serviceWorker.controller) {
return
}
// A reloadContent call should never trigger another reload
if(this.arn.app.currentPath === this.arn.lastReloadContentPath) {
this.arn.lastReloadContentPath = ""
return
}
let url = ""
// If mainPageLoaded is set, it means every single request is now an AJAX request for the /_/ prefixed page
if(this.arn.mainPageLoaded) {
url = window.location.origin + "/_" + window.location.pathname
} else {
this.arn.mainPageLoaded = true
url = window.location.href
}
// console.log("checking for updates:", message.url)
this.postMessage({
type: "loaded",
url
})
}
// For future loaded events
document.addEventListener("DOMContentLoaded", sendContentLoadedEvent)
// If the page is loaded already, send the loaded event right now.
if(document.readyState !== "loading") {
sendContentLoadedEvent()
}
} }
postMessage(message: any) { public postMessage(message: any) {
const controller = navigator.serviceWorker.controller const controller = navigator.serviceWorker.controller
if(!controller) { if(!controller) {
@ -70,7 +29,7 @@ export default class ServiceWorkerManager {
controller.postMessage(JSON.stringify(message)) controller.postMessage(JSON.stringify(message))
} }
onMessage(evt: MessageEvent) { private onMessage(evt: MessageEvent) {
const message = JSON.parse(evt.data) const message = JSON.parse(evt.data)
switch(message.type) { switch(message.type) {
@ -80,25 +39,6 @@ export default class ServiceWorkerManager {
} }
break break
// case "new content":
// if(message.url.includes("/_/")) {
// // Content reload
// this.arn.contentLoadedActions.then(() => {
// this.arn.reloadContent(true)
// })
// } else {
// // Full page reload
// this.arn.contentLoadedActions.then(() => {
// this.arn.reloadPage()
// })
// }
// break
// case "offline":
// this.arn.statusMessage.showError("You are viewing an offline version of the site now.")
// break
} }
} }
} }

View File

@ -1,4 +1,4 @@
import { delay } from "./Utils" import delay from "./Utils/delay"
export default class StatusMessage { export default class StatusMessage {
private container: HTMLElement private container: HTMLElement

19
scripts/User.ts Normal file
View File

@ -0,0 +1,19 @@
export default class User {
public id: string
private proExpires: string
public constructor(id: string) {
this.id = id
this.sync()
}
public IsPro(): boolean {
return new Date() > new Date(this.proExpires)
}
private async sync() {
const response = await fetch(`/api/user/${this.id}`)
const json = await response.json()
Object.assign(this, json)
}
}

View File

@ -1,4 +1,4 @@
export function bytesHumanReadable(fileSize: number): string { export default function bytesHumanReadable(fileSize: number): string {
let unit = "bytes" let unit = "bytes"
if(fileSize >= 1024) { if(fileSize >= 1024) {

View File

@ -1,3 +1,3 @@
export function delay<T>(millis: number, value?: T): Promise<T> { export default function delay<T>(millis: number, value?: T): Promise<T> {
return new Promise(resolve => setTimeout(() => resolve(value), millis)) return new Promise(resolve => setTimeout(() => resolve(value), millis))
} }

View File

@ -0,0 +1 @@
export default "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="

View File

@ -1,15 +1,7 @@
export function* findAll(className: string): IterableIterator<HTMLElement> { export default function* findAll(className: string): IterableIterator<HTMLElement> {
const elements = document.getElementsByClassName(className) const elements = document.getElementsByClassName(className)
for(let i = 0; i < elements.length; ++i) { for(const element of elements) {
yield elements[i] as HTMLElement yield element as HTMLElement
}
}
export function* findAllInside(className: string, root: HTMLElement): IterableIterator<HTMLElement> {
const elements = root.getElementsByClassName(className)
for(let i = 0; i < elements.length; ++i) {
yield elements[i] as HTMLElement
} }
} }

View File

@ -0,0 +1,7 @@
export default function* findAllInside(className: string, root: HTMLElement): IterableIterator<HTMLElement> {
const elements = root.getElementsByClassName(className)
for(const element of elements) {
yield element as HTMLElement
}
}

View File

@ -1,4 +1,4 @@
export function hexToHSL(hex: string) { export default function hexToHSL(hex: string) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
if(!result) { if(!result) {
@ -20,7 +20,7 @@ export function hexToHSL(hex: string) {
let s = 0 let s = 0
const l = (max + min) / 2 const l = (max + min) / 2
if(max == min) { if(max === min) {
h = s = 0 h = s = 0
} else { } else {
const d = max - min const d = max - min

View File

@ -1,9 +0,0 @@
export * from "./supportsWebP"
export * from "./delay"
export * from "./findAll"
export * from "./hexToHSL"
export * from "./plural"
export * from "./requestIdleCallback"
export * from "./swapElements"
export * from "./uploadWithProgress"
export * from "./bytesHumanReadable"

View File

@ -2,7 +2,7 @@ const specialized = {
"new activity": "new activities" "new activity": "new activities"
} }
export function plural(count: number, singular: string): string { export default function plural(count: number, singular: string): string {
if(count === 1 || count === -1) { if(count === 1 || count === -1) {
return count + " " + singular return count + " " + singular
} }

View File

@ -1,4 +1,4 @@
export function requestIdleCallback(func: Function) { export default function requestIdleCallback(func: Function) {
if("requestIdleCallback" in window) { if("requestIdleCallback" in window) {
(window["requestIdleCallback"] as Function)(func) (window["requestIdleCallback"] as Function)(func)
} else { } else {

View File

@ -1,4 +1,4 @@
export async function supportsWebP(): Promise<boolean> { export default async function supportsWebP(): Promise<boolean> {
if(!window.createImageBitmap) { if(!window.createImageBitmap) {
return false return false
} }

View File

@ -1,5 +1,5 @@
// swapElements assumes that both elements have valid parent nodes. // swapElements assumes that both elements have valid parent nodes.
export function swapElements(a: Node, b: Node) { export default function swapElements(a: Node, b: Node) {
const bParent = b.parentNode as Node const bParent = b.parentNode as Node
const bNext = b.nextSibling const bNext = b.nextSibling

View File

@ -1,4 +1,4 @@
export function uploadWithProgress(url, options: RequestInit, onProgress: ((ev: ProgressEvent) => any) | null): Promise<string> { export default function uploadWithProgress(url, options: RequestInit, onProgress: ((ev: ProgressEvent) => any) | null): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest() const xhr = new XMLHttpRequest()

37
scripts/infiniteScroll.ts Normal file
View File

@ -0,0 +1,37 @@
import Diff from "./Diff"
let container: HTMLElement
let threshold: number
export default function infiniteScroll(scrollContainer: HTMLElement, scrollThreshold: number) {
container = scrollContainer
threshold = scrollThreshold
container.addEventListener("scroll", _ => {
// Wait for mutations to finish before checking if we need infinite scroll to trigger.
if(Diff.mutations.length() > 0) {
Diff.mutations.wait(check)
return
}
// Otherwise, queue up the check immediately.
// Don't call check() directly to make scrolling as smooth as possible.
Diff.mutations.queue(() => check())
})
}
function check() {
if(container.scrollTop + container.clientHeight >= container.scrollHeight - threshold) {
loadMore()
}
}
function loadMore() {
const button = document.getElementById("load-more-button")
if(!button) {
return
}
button.click()
}

View File

@ -1,7 +1,3 @@
import AnimeNotifier from "./AnimeNotifier" import AnimeNotifier from "./AnimeNotifier"
import Application from "./Application"
const app = new Application() new AnimeNotifier().init()
const arn = new AnimeNotifier(app)
arn.init()

View File

@ -12,7 +12,12 @@
"trailing-comma": false, "trailing-comma": false,
"prefer-const": true, "prefer-const": true,
"no-var-keyword": true, "no-var-keyword": true,
"eofline": true "eofline": true,
"no-console": false,
"object-literal-sort-keys": false,
"no-string-literal": false,
"max-line-length": false,
"no-string-throw": false
}, },
"rulesDirectory": [] "rulesDirectory": []
} }