Added a basic DOM diff algorithm
This commit is contained in:
parent
9bd5e71205
commit
b4a4e78c96
@ -1,4 +1,5 @@
|
|||||||
import { Application } from "./Application"
|
import { Application } from "./Application"
|
||||||
|
import { Diff } from "./Diff"
|
||||||
import { findAll } from "./utils"
|
import { findAll } from "./utils"
|
||||||
import * as actions from "./actions"
|
import * as actions from "./actions"
|
||||||
|
|
||||||
@ -23,6 +24,14 @@ export class AnimeNotifier {
|
|||||||
this.app.run()
|
this.app.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadContent() {
|
||||||
|
return fetch("/_" + this.app.currentPath, {
|
||||||
|
credentials: "same-origin"
|
||||||
|
})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(html => Diff.innerHTML(this.app.content, html))
|
||||||
|
}
|
||||||
|
|
||||||
loading(isLoading: boolean) {
|
loading(isLoading: boolean) {
|
||||||
if(isLoading) {
|
if(isLoading) {
|
||||||
this.app.loading.classList.remove(this.app.fadeOutClass)
|
this.app.loading.classList.remove(this.app.fadeOutClass)
|
||||||
|
@ -109,6 +109,7 @@ export class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContent(html: string) {
|
setContent(html: string) {
|
||||||
|
// Diff.innerHTML(this.content, html)
|
||||||
this.content.innerHTML = html
|
this.content.innerHTML = html
|
||||||
this.ajaxify(this.content)
|
this.ajaxify(this.content)
|
||||||
this.markActiveLinks(this.content)
|
this.markActiveLinks(this.content)
|
||||||
|
@ -1,5 +1,63 @@
|
|||||||
export class Diff {
|
export class Diff {
|
||||||
static update(element: HTMLElement, html: string) {
|
static childNodes(aRoot: HTMLElement, bRoot: HTMLElement) {
|
||||||
element.innerHTML = html
|
let aChild = [...aRoot.childNodes]
|
||||||
|
let bChild = [...bRoot.childNodes]
|
||||||
|
let numNodes = Math.max(aChild.length, bChild.length)
|
||||||
|
|
||||||
|
for(let i = 0; i < numNodes; i++) {
|
||||||
|
let a = aChild[i] as HTMLElement
|
||||||
|
|
||||||
|
if(i >= bChild.length) {
|
||||||
|
aRoot.removeChild(a)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = bChild[i] as HTMLElement
|
||||||
|
|
||||||
|
if(i >= aChild.length) {
|
||||||
|
aRoot.appendChild(b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if(a.nodeName !== b.nodeName || a.nodeType !== b.nodeType) {
|
||||||
|
aRoot.replaceChild(b, a)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if(a.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
let removeAttributes: Attr[] = []
|
||||||
|
|
||||||
|
for(let x = 0; x < a.attributes.length; x++) {
|
||||||
|
let attrib = a.attributes[x]
|
||||||
|
|
||||||
|
if(attrib.specified) {
|
||||||
|
if(!b.hasAttribute(attrib.name)) {
|
||||||
|
removeAttributes.push(attrib)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let attr of removeAttributes) {
|
||||||
|
a.removeAttributeNode(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let x = 0; x < b.attributes.length; x++) {
|
||||||
|
let attrib = b.attributes[x]
|
||||||
|
|
||||||
|
if(attrib.specified) {
|
||||||
|
a.setAttribute(attrib.name, b.getAttribute(attrib.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Diff.childNodes(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static innerHTML(aRoot: HTMLElement, html: string) {
|
||||||
|
let bRoot = document.createElement("main")
|
||||||
|
bRoot.innerHTML = html
|
||||||
|
|
||||||
|
Diff.childNodes(aRoot, bRoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import { Application } from "./Application"
|
import { Application } from "./Application"
|
||||||
import { AnimeNotifier } from "./AnimeNotifier"
|
import { AnimeNotifier } from "./AnimeNotifier"
|
||||||
import { Diff } from "./Diff"
|
|
||||||
|
|
||||||
// Save new data from an input field
|
// Save new data from an input field
|
||||||
export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaElement) {
|
export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaElement) {
|
||||||
@ -108,11 +107,7 @@ export function addAnimeToCollection(arn: AnimeNotifier, button: HTMLElement) {
|
|||||||
throw body
|
throw body
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch("/_" + arn.app.currentPath, {
|
return arn.reloadContent()
|
||||||
credentials: "same-origin"
|
|
||||||
})
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(html => Diff.update(arn.app.content, html))
|
|
||||||
})
|
})
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
.then(() => arn.loading(false))
|
.then(() => arn.loading(false))
|
||||||
|
@ -4,9 +4,7 @@ import { AnimeNotifier } from "./AnimeNotifier"
|
|||||||
let app = new Application()
|
let app = new Application()
|
||||||
let arn = new AnimeNotifier(app)
|
let arn = new AnimeNotifier(app)
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", arn.onContentLoaded.bind(arn))
|
|
||||||
document.addEventListener("readystatechange", arn.onReadyStateChange.bind(arn))
|
document.addEventListener("readystatechange", arn.onReadyStateChange.bind(arn))
|
||||||
|
document.addEventListener("DOMContentLoaded", arn.onContentLoaded.bind(arn))
|
||||||
document.addEventListener("keydown", arn.onKeyDown.bind(arn), false)
|
document.addEventListener("keydown", arn.onKeyDown.bind(arn), false)
|
||||||
|
|
||||||
window.addEventListener("popstate", arn.onPopState.bind(arn))
|
window.addEventListener("popstate", arn.onPopState.bind(arn))
|
||||||
// window.addEventListener("resize", arn.onResize.bind(arn))
|
|
@ -1,5 +1,7 @@
|
|||||||
export function* findAll(className: string) {
|
export function* findAll(className: string) {
|
||||||
let elements = document.getElementsByClassName(className)
|
// getElementsByClassName failed for some reason.
|
||||||
|
// TODO: Test getElementsByClassName again.
|
||||||
|
let elements = document.querySelectorAll("." + className)
|
||||||
|
|
||||||
for(let i = 0; i < elements.length; ++i) {
|
for(let i = 0; i < elements.length; ++i) {
|
||||||
yield elements[i] as HTMLElement
|
yield elements[i] as HTMLElement
|
||||||
|
Loading…
Reference in New Issue
Block a user