[💥] Major system rewrite
This commit is contained in:
parent
2aaf0475a0
commit
8de338f899
6 changed files with 463 additions and 37 deletions
|
|
@ -45,7 +45,7 @@ npm run serve
|
||||||
|
|
||||||
## Made with
|
## Made with
|
||||||
FlowOS is made with the following software:
|
FlowOS is made with the following software:
|
||||||
* [Filer](https://github.com/filerjs/filer)
|
* [Kat21's HTML Library](https://github.com/datkat21/html)
|
||||||
* [Prism Code Editor](https://github.com/FIameCaster/prism-code-editor)
|
* [Prism Code Editor](https://github.com/FIameCaster/prism-code-editor)
|
||||||
* [Vite](https://vitejs.dev)
|
* [Vite](https://vitejs.dev)
|
||||||
* [Ultraviolet](https://github.com/titaniumnetwork-dev/ultraviolet)
|
* [Ultraviolet](https://github.com/titaniumnetwork-dev/ultraviolet)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import icon from '../../assets/icons/userinfo.svg'
|
import icon from '../../assets/icons/userinfo.svg'
|
||||||
import badge from '../../assets/badge.png'
|
import badge from '../../assets/badge.png'
|
||||||
import { App, PackageJSON } from '../../types'
|
import { App } from '../../types'
|
||||||
|
|
||||||
import FlowWindow from '../../structures/FlowWindow'
|
import FlowWindow from '../../structures/FlowWindow'
|
||||||
|
import HTML from '../../lib'
|
||||||
|
|
||||||
export default class InfoApp implements App {
|
export default class InfoApp implements App {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -14,7 +15,6 @@ export default class InfoApp implements App {
|
||||||
}
|
}
|
||||||
|
|
||||||
async open (): Promise<FlowWindow> {
|
async open (): Promise<FlowWindow> {
|
||||||
const packageJSON: PackageJSON = await import('../../../package.json')
|
|
||||||
const win = window.wm.createWindow({
|
const win = window.wm.createWindow({
|
||||||
title: this.meta.name,
|
title: this.meta.name,
|
||||||
icon: this.meta.icon,
|
icon: this.meta.icon,
|
||||||
|
|
@ -30,18 +30,29 @@ export default class InfoApp implements App {
|
||||||
win.content.style.justifyContent = 'center'
|
win.content.style.justifyContent = 'center'
|
||||||
win.content.style.alignItems = 'center'
|
win.content.style.alignItems = 'center'
|
||||||
win.content.style.background = 'var(--base)'
|
win.content.style.background = 'var(--base)'
|
||||||
win.content.innerHTML = `
|
|
||||||
<div>
|
const div = new HTML('div').appendTo(win.content)
|
||||||
<h1 style="margin:0;">FlowOS</h1>
|
new HTML('h1').style({
|
||||||
<p style="margin:0;">v${packageJSON.version}</p>
|
margin: '0'
|
||||||
<br/>
|
}).text(`FlowOS ${window.flowDetails.codename}`).appendTo(div)
|
||||||
<p>Created by ThinLiquid, 1nspird_, proudparot2, systemless_</p>
|
new HTML('p').style({
|
||||||
<img src="${badge}" height="50"><br/>
|
margin: '0'
|
||||||
<a class="discord" href="https://discord.gg/flowos">Discord</a>
|
}).text(`v${window.flowDetails.version}`).appendTo(div)
|
||||||
-
|
new HTML('br').appendTo(div)
|
||||||
<a class="github" href="https://github.com/Flow-Works/FlowOS-2.0">Github</a>
|
new HTML('img').attr({
|
||||||
</div>
|
src: badge,
|
||||||
`
|
height: '50'
|
||||||
|
}).appendTo(div)
|
||||||
|
new HTML('br').appendTo(div)
|
||||||
|
new HTML('a').text('Discord').attr({
|
||||||
|
href: 'https://discord.gg/86F8dK9vfn',
|
||||||
|
class: 'discord'
|
||||||
|
}).appendTo(div)
|
||||||
|
new HTML('span').text(' - ').appendTo(div)
|
||||||
|
new HTML('a').text('Github').attr({
|
||||||
|
href: 'https://github.com/Flow-Works/FlowOS',
|
||||||
|
class: 'github'
|
||||||
|
}).appendTo(div)
|
||||||
|
|
||||||
return win
|
return win
|
||||||
}
|
}
|
||||||
|
|
|
||||||
89
src/fs.ts
89
src/fs.ts
|
|
@ -8,14 +8,15 @@ export enum Errors {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Permission {
|
export enum Permission {
|
||||||
ROOT,
|
USER,
|
||||||
ADMIN,
|
ELEVATED,
|
||||||
USER
|
SYSTEM
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Directory {
|
export interface Directory {
|
||||||
type: 'directory'
|
type: 'directory'
|
||||||
permission: Permission
|
permission: Permission
|
||||||
|
deleteable: boolean
|
||||||
children: {
|
children: {
|
||||||
[key: string]: Directory | File
|
[key: string]: Directory | File
|
||||||
}
|
}
|
||||||
|
|
@ -24,34 +25,41 @@ export interface Directory {
|
||||||
export interface File {
|
export interface File {
|
||||||
type: 'file'
|
type: 'file'
|
||||||
permission: Permission
|
permission: Permission
|
||||||
|
deleteable: boolean
|
||||||
content: Buffer
|
content: Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultFS: { root: Directory } = {
|
const defaultFS: { root: Directory } = {
|
||||||
root: {
|
root: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ROOT,
|
deleteable: false,
|
||||||
|
permission: Permission.SYSTEM,
|
||||||
children: {
|
children: {
|
||||||
home: {
|
home: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ROOT,
|
deleteable: false,
|
||||||
|
permission: Permission.SYSTEM,
|
||||||
children: {
|
children: {
|
||||||
Downloads: {
|
Downloads: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {}
|
children: {}
|
||||||
},
|
},
|
||||||
Applications: {
|
Applications: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {}
|
children: {}
|
||||||
},
|
},
|
||||||
Desktop: {
|
Desktop: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {
|
children: {
|
||||||
'README.md': {
|
'README.md': {
|
||||||
type: 'file',
|
type: 'file',
|
||||||
|
deleteable: true,
|
||||||
permission: Permission.USER,
|
permission: Permission.USER,
|
||||||
content: Buffer.from('# Welcome to FlowOS!')
|
content: Buffer.from('# Welcome to FlowOS!')
|
||||||
}
|
}
|
||||||
|
|
@ -59,39 +67,53 @@ const defaultFS: { root: Directory } = {
|
||||||
},
|
},
|
||||||
Pictures: {
|
Pictures: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {}
|
children: {}
|
||||||
},
|
},
|
||||||
Videos: {
|
Videos: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {}
|
children: {}
|
||||||
},
|
},
|
||||||
Documents: {
|
Documents: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {}
|
children: {}
|
||||||
},
|
},
|
||||||
Music: {
|
Music: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ADMIN,
|
deleteable: false,
|
||||||
|
permission: Permission.USER,
|
||||||
children: {}
|
children: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
var: {
|
var: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ROOT,
|
deleteable: false,
|
||||||
|
permission: Permission.SYSTEM,
|
||||||
children: {}
|
children: {}
|
||||||
},
|
},
|
||||||
etc: {
|
etc: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ROOT,
|
deleteable: false,
|
||||||
children: {}
|
permission: Permission.SYSTEM,
|
||||||
|
children: {
|
||||||
|
hostname: {
|
||||||
|
type: 'file',
|
||||||
|
deleteable: false,
|
||||||
|
permission: Permission.ELEVATED,
|
||||||
|
content: Buffer.from('flow')
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
boot: {
|
boot: {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.ROOT,
|
deleteable: false,
|
||||||
|
permission: Permission.SYSTEM,
|
||||||
children: {}
|
children: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,10 +225,28 @@ export class VirtualFS {
|
||||||
* sufficient permissions to access a certain path.
|
* sufficient permissions to access a certain path.
|
||||||
*/
|
*/
|
||||||
private async handlePermissions (path: string, permission: Permission): Promise<void> {
|
private async handlePermissions (path: string, permission: Permission): Promise<void> {
|
||||||
const { current } = await this.navigatePath(path)
|
let current
|
||||||
|
|
||||||
if (current.permission === Permission.ADMIN && current.permission <= permission) throw new Error(Errors.EACCES)
|
current = (await this.navigatePath(path)).current
|
||||||
if (current.permission === Permission.ROOT && current.permission <= permission) throw new Error(Errors.EPERM)
|
console.log(current)
|
||||||
|
if (current === undefined) current = (await this.navigatePathParent(path)).current
|
||||||
|
console.log(current)
|
||||||
|
|
||||||
|
console.log(current.permission, permission)
|
||||||
|
|
||||||
|
if (current.permission === Permission.USER && current.permission < permission) {
|
||||||
|
const uac = await window.wm.createModal('Elevated Permissions Required', 'You need elevated permissions to perform this operation.')
|
||||||
|
if (!uac) {
|
||||||
|
throw new Error(Errors.EACCES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current.permission === Permission.ELEVATED && current.permission < permission) {
|
||||||
|
const uac = await window.wm.createModal('Elevated Permissions Required', 'You need elevated permissions to perform this operation.')
|
||||||
|
if (!uac) {
|
||||||
|
throw new Error(Errors.EACCES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current.permission === Permission.SYSTEM && current.permission < permission) throw new Error(Errors.EPERM)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -254,6 +294,7 @@ export class VirtualFS {
|
||||||
async unlink (path: string, permission = Permission.USER): Promise<void> {
|
async unlink (path: string, permission = Permission.USER): Promise<void> {
|
||||||
const { current, filename } = await this.navigatePathParent(path)
|
const { current, filename } = await this.navigatePathParent(path)
|
||||||
|
|
||||||
|
if (!current.children[filename].deleteable) throw new Error(Errors.EPERM)
|
||||||
await this.handlePermissions(path, permission)
|
await this.handlePermissions(path, permission)
|
||||||
|
|
||||||
Reflect.deleteProperty(current.children, filename)
|
Reflect.deleteProperty(current.children, filename)
|
||||||
|
|
@ -296,7 +337,8 @@ export class VirtualFS {
|
||||||
|
|
||||||
current.children[filename] = {
|
current.children[filename] = {
|
||||||
type: 'file',
|
type: 'file',
|
||||||
permission: Permission.USER,
|
deleteable: true,
|
||||||
|
permission,
|
||||||
content: Buffer.from(content)
|
content: Buffer.from(content)
|
||||||
}
|
}
|
||||||
await this.save()
|
await this.save()
|
||||||
|
|
@ -318,7 +360,8 @@ export class VirtualFS {
|
||||||
|
|
||||||
current.children[filename] = {
|
current.children[filename] = {
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
permission: Permission.USER,
|
deleteable: true,
|
||||||
|
permission,
|
||||||
children: {}
|
children: {}
|
||||||
}
|
}
|
||||||
await this.save()
|
await this.save()
|
||||||
|
|
@ -336,6 +379,7 @@ export class VirtualFS {
|
||||||
async rmdir (path: string, permission = Permission.USER): Promise<void> {
|
async rmdir (path: string, permission = Permission.USER): Promise<void> {
|
||||||
const { current, filename } = await this.navigatePathParent(path)
|
const { current, filename } = await this.navigatePathParent(path)
|
||||||
|
|
||||||
|
if (!current.deleteable) throw new Error(Errors.EPERM)
|
||||||
await this.handlePermissions(path, permission)
|
await this.handlePermissions(path, permission)
|
||||||
|
|
||||||
if (current.children[filename].type !== 'directory') throw new Error(Errors.ENOTDIR)
|
if (current.children[filename].type !== 'directory') throw new Error(Errors.ENOTDIR)
|
||||||
|
|
@ -391,6 +435,9 @@ export class VirtualFS {
|
||||||
const { current: oldCurrent, filename: oldFilename } = await this.navigatePathParent(oldPath)
|
const { current: oldCurrent, filename: oldFilename } = await this.navigatePathParent(oldPath)
|
||||||
const { current: newCurrent, filename: newFilename } = await this.navigatePathParent(newPath)
|
const { current: newCurrent, filename: newFilename } = await this.navigatePathParent(newPath)
|
||||||
|
|
||||||
|
if (!oldCurrent.deleteable) throw new Error(Errors.EPERM)
|
||||||
|
if (!newCurrent.deleteable) throw new Error(Errors.EPERM)
|
||||||
|
|
||||||
await this.handlePermissions(oldPath, permission)
|
await this.handlePermissions(oldPath, permission)
|
||||||
await this.handlePermissions(newPath, permission)
|
await this.handlePermissions(newPath, permission)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ const flowDetails = {
|
||||||
version,
|
version,
|
||||||
codename: 'Mochi'
|
codename: 'Mochi'
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
flowDetails: typeof flowDetails
|
flowDetails: typeof flowDetails
|
||||||
|
|
@ -38,6 +37,7 @@ if (params.get('debug') !== null && params.get('debug') !== undefined) {
|
||||||
enableDebug().catch(e => console.error(e))
|
enableDebug().catch(e => console.error(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.flowDetails = flowDetails
|
||||||
window.preloader = new Preloader()
|
window.preloader = new Preloader()
|
||||||
window.flow = new Flow()
|
window.flow = new Flow()
|
||||||
window.statusBar = new StatusBar()
|
window.statusBar = new StatusBar()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
import HTML from '../lib'
|
||||||
import FlowWindow from '../structures/FlowWindow'
|
import FlowWindow from '../structures/FlowWindow'
|
||||||
import { FlowWindowConfig } from '../types'
|
import { FlowWindowConfig } from '../types'
|
||||||
|
|
||||||
|
|
@ -43,6 +44,40 @@ class WindowManager {
|
||||||
return win
|
return win
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a modal window.
|
||||||
|
*
|
||||||
|
* @param {string} title - A string representing the title of the modal window.
|
||||||
|
* @param {string} text - The `text` parameter is a string that represents the content or message to
|
||||||
|
* be displayed in the modal window.
|
||||||
|
* @returns The function `createModal` is returning a `FlowWindow` object.
|
||||||
|
*/
|
||||||
|
async createModal (title: string, text: string): Promise<boolean> {
|
||||||
|
const win = new FlowWindow(this, {
|
||||||
|
title,
|
||||||
|
icon: '',
|
||||||
|
width: 300,
|
||||||
|
height: 200,
|
||||||
|
canResize: false
|
||||||
|
})
|
||||||
|
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
new HTML('h3').text(text).appendTo(win.content)
|
||||||
|
new HTML('p').text(text).appendTo(win.content)
|
||||||
|
new HTML('button').text('Allow').appendTo(win.content).on('click', () => {
|
||||||
|
resolve(true)
|
||||||
|
win.close()
|
||||||
|
})
|
||||||
|
new HTML('button').text('Allow').appendTo(win.content).on('click', () => {
|
||||||
|
resolve(false)
|
||||||
|
win.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.windows.push(win)
|
||||||
|
this.windowArea.appendChild(win.element)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the app launcher.
|
* Toggles the app launcher.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
333
src/lib.ts
Normal file
333
src/lib.ts
Normal file
|
|
@ -0,0 +1,333 @@
|
||||||
|
export default class HTML {
|
||||||
|
/** The HTML element referenced in this instance. Change using `.swapRef()`, or remove using `.cleanup()`. */
|
||||||
|
elm: HTMLInputElement | HTMLElement
|
||||||
|
/**
|
||||||
|
* Create a new instance of the HTML class.
|
||||||
|
* @param elm The HTML element to be created or classified from.
|
||||||
|
*/
|
||||||
|
constructor (elm: string | HTMLElement) {
|
||||||
|
if (elm instanceof HTMLElement) {
|
||||||
|
this.elm = elm
|
||||||
|
} else {
|
||||||
|
this.elm = document.createElement(elm === '' ? 'div' : elm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text of the current element.
|
||||||
|
* @param val The text to set to.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
text (val: string): HTML {
|
||||||
|
this.elm.innerText = val
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text of the current element.
|
||||||
|
* @param val The text to set to.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
html (val: string): HTML {
|
||||||
|
this.elm.innerHTML = val
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely remove the element. Can be used in combination with a `.swapRef()` to achieve a "delete & swap" result.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
cleanup (): HTML {
|
||||||
|
this.elm.remove()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* querySelector something.
|
||||||
|
* @param selector The query selector.
|
||||||
|
* @returns The HTML element (not as HTML)
|
||||||
|
*/
|
||||||
|
query (selector: string): HTMLElement | null {
|
||||||
|
return this.elm.querySelector(selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An easier querySelector method.
|
||||||
|
* @param query The string to query
|
||||||
|
* @returns a new HTML
|
||||||
|
*/
|
||||||
|
qs (query: string): HTML | null {
|
||||||
|
if (this.elm.querySelector(query) != null) {
|
||||||
|
return HTML.from(this.elm.querySelector(query) as HTMLElement)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An easier querySelectorAll method.
|
||||||
|
* @param query The string to query
|
||||||
|
* @returns a new HTML
|
||||||
|
*/
|
||||||
|
qsa (query: string): Array<HTML | null> | null {
|
||||||
|
if (this.elm.querySelector(query) != null) {
|
||||||
|
return Array.from(this.elm.querySelectorAll(query)).map((e) =>
|
||||||
|
HTML.from(e as HTMLElement)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ID of the element.
|
||||||
|
* @param val The ID to set.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
id (val: string): HTML {
|
||||||
|
this.elm.id = val
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle on/off a class.
|
||||||
|
* @param val The class to toggle.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
class (...val: string[]): HTML {
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
this.elm.classList.toggle(val[i])
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles ON a class.
|
||||||
|
* @param val The class to enable.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
classOn (...val: string[]): HTML {
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
this.elm.classList.add(val[i])
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles OFF a class.
|
||||||
|
* @param val The class to disable.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
classOff (...val: string[]): HTML {
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
this.elm.classList.remove(val[i])
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply CSS styles (dashed method.) Keys use CSS syntax, e.g. `background-color`.
|
||||||
|
* @param obj The styles to apply (as an object of `key: value;`.)
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
style (obj: { [x: string]: string | null }): HTML {
|
||||||
|
for (const key of Object.keys(obj)) {
|
||||||
|
this.elm.style.setProperty(key, obj[key])
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply CSS styles (JS method.) Keys use JS syntax, e.g. `backgroundColor`.
|
||||||
|
* @param obj The styles to apply (as an object of `key: value;`)
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
styleJs (obj: { [key: string]: string | null }): HTML {
|
||||||
|
for (const key of Object.keys(obj)) {
|
||||||
|
// @ts-expect-error No other workaround I could find.
|
||||||
|
this.elm.style[key] = obj[key]
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply an event listener.
|
||||||
|
* @param ev The event listener type to add.
|
||||||
|
* @param cb The event listener callback to add.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
on (ev: string, cb: EventListenerOrEventListenerObject): HTML {
|
||||||
|
this.elm.addEventListener(ev, cb)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an event listener.
|
||||||
|
* @param ev The event listener type to remove.
|
||||||
|
* @param cb The event listener callback to remove.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
un (ev: string, cb: EventListenerOrEventListenerObject): HTML {
|
||||||
|
this.elm.removeEventListener(ev, cb)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append this element to another element. Uses `appendChild()` on the parent.
|
||||||
|
* @param parent Element to append to. HTMLElement, HTML, and string (as querySelector) are supported.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
appendTo (parent: HTMLElement | HTML | string): HTML {
|
||||||
|
if (parent instanceof HTMLElement) {
|
||||||
|
parent.appendChild(this.elm)
|
||||||
|
} else if (parent instanceof HTML) {
|
||||||
|
parent.elm.appendChild(this.elm)
|
||||||
|
} else if (typeof parent === 'string') {
|
||||||
|
document.querySelector(parent)?.appendChild(this.elm)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append an element. Typically used as a `.append(new HTML(...))` call.
|
||||||
|
* @param elem The element to append.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
append (elem: string | HTMLElement | HTML): HTML {
|
||||||
|
if (elem instanceof HTMLElement) {
|
||||||
|
this.elm.appendChild(elem)
|
||||||
|
} else if (elem instanceof HTML) {
|
||||||
|
this.elm.appendChild(elem.elm)
|
||||||
|
} else if (typeof elem === 'string') {
|
||||||
|
const newElem = document.createElement(elem)
|
||||||
|
this.elm.appendChild(newElem)
|
||||||
|
return new HTML(newElem.tagName)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append multiple elements. Typically used as a `.appendMany(new HTML(...), new HTML(...)` call.
|
||||||
|
* @param elements The elements to append.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
appendMany (...elements: any[]): HTML {
|
||||||
|
for (const elem of elements) {
|
||||||
|
this.append(elem)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the innerHTML of the element.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
clear (): HTML {
|
||||||
|
this.elm.innerHTML = ''
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set attributes (object method.)
|
||||||
|
* @param obj The attributes to set (as an object of `key: value;`)
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
attr (obj: { [x: string]: any }): HTML {
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj[key] !== null && obj[key] !== undefined) {
|
||||||
|
this.elm.setAttribute(key, obj[key])
|
||||||
|
} else {
|
||||||
|
this.elm.removeAttribute(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the text value of the element. Only works if element is `input` or `textarea`.
|
||||||
|
* @param str The value to set.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
val (str: any): HTML {
|
||||||
|
const x = this.elm as HTMLInputElement
|
||||||
|
x.value = str
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve text content from the element. (as innerText, not trimmed)
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
getText (): string {
|
||||||
|
return (this.elm as HTMLInputElement).innerText
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve HTML content from the element.
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
getHTML (): string {
|
||||||
|
return (this.elm as HTMLInputElement).innerHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the value of the element. Only applicable if it is an `input` or `textarea`.
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
getValue (): string {
|
||||||
|
return (this.elm as HTMLInputElement).value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swap the local `elm` with a new HTMLElement.
|
||||||
|
* @param elm The element to swap with.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
swapRef (elm: HTMLElement): HTML {
|
||||||
|
this.elm = elm
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alternative method to create an HTML instance.
|
||||||
|
* @param elm Element to create from.
|
||||||
|
* @returns HTML
|
||||||
|
*/
|
||||||
|
static from (elm: HTMLElement | string): HTML | null {
|
||||||
|
if (typeof elm === 'string') {
|
||||||
|
const element = HTML.qs(elm)
|
||||||
|
if (element === null) return null
|
||||||
|
else return element
|
||||||
|
} else {
|
||||||
|
return new HTML(elm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An easier querySelector method.
|
||||||
|
* @param query The string to query
|
||||||
|
* @returns a new HTML
|
||||||
|
*/
|
||||||
|
static qs (query: string): HTML | null {
|
||||||
|
if (document.querySelector(query) != null) {
|
||||||
|
return HTML.from(document.querySelector(query) as HTMLElement)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An easier querySelectorAll method.
|
||||||
|
* @param query The string to query
|
||||||
|
* @returns a new HTML
|
||||||
|
*/
|
||||||
|
static qsa (query: string): Array<HTML | null> | null {
|
||||||
|
if (document.querySelector(query) != null) {
|
||||||
|
return Array.from(document.querySelectorAll(query)).map((e) =>
|
||||||
|
HTML.from(e as HTMLElement)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue