Compare commits
10 Commits
48529ae3e2
...
ed6d885557
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed6d885557 | ||
|
|
6e2b4dcdbd | ||
|
|
71b08bd184 | ||
|
|
1060797170 | ||
|
|
cfdf67998d | ||
|
|
9432c65fa4 | ||
|
|
65f79d1631 | ||
|
|
41ec6b7dd3 | ||
|
|
8b2f9e2b77 | ||
|
|
07725994b9 |
1
.env
Normal file
1
.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VSCE_PAT=9wucEHBNFD5RTS403Oyoflyi984cAwAfa42FHN7tmqmzfbXsSKP9JQQJ99BKACAAAAAAAAAAAAAGAZDO25Ro
|
||||||
39
README.md
39
README.md
@@ -5,25 +5,42 @@
|
|||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
Usage: Install the VSCode extension "Quill".
|
Quill is a SwiftUI-style JavaScript framework. It makes use of components called Shadows, which are HTML Custom Elements.
|
||||||
|
|
||||||
## Rendering Elements:
|
### Getting Started:
|
||||||
|
Go to https://github.com/capturedsun/template-quill and clone this repo.
|
||||||
|
|
||||||
|
### App:
|
||||||
|
src/app.js is the application entry point. Any Shadows rendered inside will be rendered in the body.
|
||||||
|
|
||||||
|
### Rendering Elements:
|
||||||
|
|
||||||
|
The basis of rendering is the Shadow, which extends HTMLElement. The Shadow is the equivalent of a React Component.
|
||||||
|
|
||||||
|
To create a Shadow, simply:
|
||||||
|
- Create a class which extends Shadow
|
||||||
|
- Include a render() function
|
||||||
|
- Register the shadow as an element
|
||||||
|
|
||||||
|
...
|
||||||
```
|
```
|
||||||
document.body.append(
|
class File extends Shadow {
|
||||||
p("Hi")
|
render = () => {
|
||||||
.padding("top", 12),
|
p(this.name)
|
||||||
|
p("asd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Link({href: "google.com", name: "hey"})
|
register(File, "file-el")
|
||||||
.background("green")
|
|
||||||
|
|
||||||
// NavigationBar()
|
|
||||||
// .onClick()
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Needs Support:
|
## Needs Support:
|
||||||
Ternaries within render()
|
Ternaries within render()
|
||||||
Other statements within render()
|
Other statements within render()
|
||||||
|
Multiple-argument attributes in render()
|
||||||
|
|
||||||
|
## Limitations:
|
||||||
|
While rendering is underway, an element's state can only be accessed from within that element
|
||||||
|
|
||||||
## Boilerplate:
|
## Boilerplate:
|
||||||
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
||||||
|
|||||||
2
Sample/.gitignore
vendored
Normal file
2
Sample/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
0
Sample/db/db.json
Normal file
0
Sample/db/db.json
Normal file
18
Sample/package.json
Normal file
18
Sample/package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "Quill Starter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server/index.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"moment": "^2.30.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Sample/server/db/db.js
Normal file
12
Sample/server/db/db.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const fs = require('fs/promises');
|
||||||
|
|
||||||
|
export default class Database {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
100
Sample/server/index.js
Normal file
100
Sample/server/index.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const cors = require('cors');
|
||||||
|
const http = require('http');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const moment = require('moment');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
db;
|
||||||
|
UIPath = path.join(__dirname, '../ui')
|
||||||
|
|
||||||
|
registerRoutes(router) {
|
||||||
|
router.post('/join', this.join)
|
||||||
|
router.post('/contact', this.contact)
|
||||||
|
router.get('/*', this.get)
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
join = (req, res) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
contact = (req, res) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get = async (req, res) => {
|
||||||
|
console.log(this.UIPath)
|
||||||
|
let url = req.url
|
||||||
|
let filePath;
|
||||||
|
if(url.startsWith("/_")) {
|
||||||
|
filePath = path.join(this.UIPath, url);
|
||||||
|
} else if(url.includes("75820185")) {
|
||||||
|
filePath = path.join(this.UIPath, url.split("75820185")[1]);
|
||||||
|
} else {
|
||||||
|
filePath = path.join(this.UIPath, "index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
logRequest(req, res, next) {
|
||||||
|
const formattedDate = moment().format('M.D');
|
||||||
|
const formattedTime = moment().format('h:mma');
|
||||||
|
if(req.url.includes("/api/")) {
|
||||||
|
console.log(chalk.blue(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
||||||
|
} else {
|
||||||
|
if(req.url === "/")
|
||||||
|
console.log(chalk.gray(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
logResponse(req, res, next) {
|
||||||
|
const originalSend = res.send;
|
||||||
|
res.send = function (body) {
|
||||||
|
if(res.statusCode >= 400) {
|
||||||
|
console.log(chalk.blue( `<-${chalk.red(res.statusCode)}- ${req.method} ${req.url} | ${chalk.red(body)}`));
|
||||||
|
} else {
|
||||||
|
console.log(chalk.blue(`<-${res.statusCode}- ${req.method} ${req.url}`));
|
||||||
|
}
|
||||||
|
originalSend.call(this, body);
|
||||||
|
};
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const app = express();
|
||||||
|
app.use(cors({ origin: '*' }));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
app.use(this.logRequest);
|
||||||
|
app.use(this.logResponse);
|
||||||
|
|
||||||
|
let router = express.Router();
|
||||||
|
this.registerRoutes(router)
|
||||||
|
app.use('/', router);
|
||||||
|
|
||||||
|
const server = http.createServer(app);
|
||||||
|
const PORT = 3004;
|
||||||
|
server.listen(PORT, () => {
|
||||||
|
console.log("\n")
|
||||||
|
console.log(chalk.yellow("*************** Comal YR ***************"))
|
||||||
|
console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`));
|
||||||
|
console.log(chalk.yellow("***************************************"))
|
||||||
|
console.log("\n")
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
console.log(chalk.red('Closing server...'));
|
||||||
|
console.log(chalk.green('Database connection closed.'));
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.preventExtensions(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = new Server()
|
||||||
1047
Sample/ui/_/code/quill.js
Normal file
1047
Sample/ui/_/code/quill.js
Normal file
File diff suppressed because one or more lines are too long
0
Sample/ui/_/code/shared.css
Normal file
0
Sample/ui/_/code/shared.css
Normal file
7
Sample/ui/components/Home.js
Normal file
7
Sample/ui/components/Home.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class Home extends Shadow {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register(Home)
|
||||||
13
Sample/ui/index.html
Normal file
13
Sample/ui/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Quill Starter</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" href="/_/icons/logo.svg">
|
||||||
|
<link rel="stylesheet" href="/_/code/shared.css">
|
||||||
|
<script src="/_/code/quill.js"></script>
|
||||||
|
<script type="module" src="75820185/index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0px">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2
Sample/ui/index.js
Normal file
2
Sample/ui/index.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import "./components/Home.js"
|
||||||
|
Home()
|
||||||
38
Test/Element/Group.test.js
Normal file
38
Test/Element/Group.test.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
window.testSuites.push( class testGroup {
|
||||||
|
|
||||||
|
BasicDivs() {
|
||||||
|
let divs = Group(() => {
|
||||||
|
div()
|
||||||
|
div()
|
||||||
|
div()
|
||||||
|
})
|
||||||
|
|
||||||
|
if(!(divs instanceof HTMLElement)) {
|
||||||
|
return `Did not receive an element!`
|
||||||
|
}
|
||||||
|
if(!(divs.children.length === 3)) {
|
||||||
|
return `Incorrect amount of children!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack() {
|
||||||
|
let divs = Group(() => {
|
||||||
|
VStack(() => {
|
||||||
|
div()
|
||||||
|
div()
|
||||||
|
div()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if(!(divs instanceof HTMLElement)) {
|
||||||
|
return `Did not receive an element!`
|
||||||
|
}
|
||||||
|
if(!(divs.style.display === "flex")) {
|
||||||
|
return `Did not receive a flex container!`
|
||||||
|
}
|
||||||
|
if(!(divs.children.length === 3)) {
|
||||||
|
return `Incorrect amount of children!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
79
Test/Element/stacks.test.js
Normal file
79
Test/Element/stacks.test.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
window.testSuites.push( class testStacks {
|
||||||
|
|
||||||
|
NestedChildren() {
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$name
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.name)
|
||||||
|
|
||||||
|
VStack(() => {
|
||||||
|
p("Learn to code inside of a startup")
|
||||||
|
|
||||||
|
p("➵")
|
||||||
|
.position("absolute")
|
||||||
|
.fontSize(30)
|
||||||
|
.left("50%")
|
||||||
|
.top("50%")
|
||||||
|
.transformOrigin("center")
|
||||||
|
.transform("translate(-50%, -50%) rotate(90deg)")
|
||||||
|
.transition("all 1s")
|
||||||
|
.userSelect("none")
|
||||||
|
.onAppear((self) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
self.style.top = "88%"
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
const file = File("asdf")
|
||||||
|
if(file.querySelector(".VStack")?.children.length !== 2) {
|
||||||
|
return "Incorrect amount of children inside vstack!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NestedStacks() {
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$name
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
VStack(() => {
|
||||||
|
HStack(() => {
|
||||||
|
p("hi")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
const file = File("asdf")
|
||||||
|
console.log(file)
|
||||||
|
let fileStyle = Registry.styles.children[Registry.styles.children.length-1].sheet.cssRules
|
||||||
|
if(fileStyle[0].cssText.includes("row")) {
|
||||||
|
return "Should not be horizontal"
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(file.innerHTML)
|
||||||
|
if(!file.children[0].matches("div.HStack")) {
|
||||||
|
return "The child is not an HStack"
|
||||||
|
}
|
||||||
|
|
||||||
|
file.rerender()
|
||||||
|
|
||||||
|
if(!file.children[0].matches("div.HStack")) {
|
||||||
|
return "The child is not an HStack"
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileStyle2 = Registry.styles.children[Registry.styles.children.length-1].sheet.cssRules
|
||||||
|
if(fileStyle2[0].cssText.includes("row")) {
|
||||||
|
return "Should not be horizontal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
@@ -1,75 +1,45 @@
|
|||||||
window.testSuites.push( class testRender {
|
window.testSuites.push( class testObservedObject {
|
||||||
|
|
||||||
SimpleParagraphWithState() {
|
FromJSONFailsWithoutAllFields() {
|
||||||
class File extends Shadow {
|
class Form extends ObservedObject {
|
||||||
$form
|
id
|
||||||
|
path
|
||||||
render = () => {
|
$canvasPosition
|
||||||
p(this.form.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.register(File, randomName("file"))
|
try {
|
||||||
let form = {data: "asdf"}
|
let obj = Form.create({id: "123"})
|
||||||
const el = window.File(form)
|
return "Not implemented"
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
if(!(el.firstChild?.matches("p"))) {
|
FromJSONInitsAllFields() {
|
||||||
return `Child paragraph not rendered`
|
class Form extends ObservedObject {
|
||||||
|
id
|
||||||
|
path
|
||||||
|
$canvasPosition
|
||||||
}
|
}
|
||||||
if(!(el.firstChild.innerText === "asdf")) {
|
|
||||||
return "Child para does not have inner text"
|
let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||||
|
if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
||||||
|
return "Not all fields initialized!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphConstructorChangeState() {
|
DefaultValueWorks() {
|
||||||
register(class File extends Shadow {
|
class WindowState extends ObservedObject {
|
||||||
$name
|
$sidebarOut = false
|
||||||
|
}
|
||||||
|
|
||||||
render = () => {
|
let obj = WindowState.create()
|
||||||
p(this.name)
|
console.log(obj)
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
if(obj.sidebarOut !== false) {
|
||||||
super()
|
return "Default field not set"
|
||||||
}
|
|
||||||
}, randomName("file"))
|
|
||||||
|
|
||||||
|
|
||||||
let name = "asdf"
|
|
||||||
const file = File(name)
|
|
||||||
file.name = "hey123"
|
|
||||||
|
|
||||||
if(file.firstChild.innerText !== "hey123") {
|
|
||||||
return "Paragraph did not react to change!"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LiteralDoesNotCreateFalseReactivity() {
|
// throw some sort of warning if a global OO is accessed without "this"
|
||||||
register(class File extends Shadow {
|
|
||||||
$name = "asd"
|
|
||||||
|
|
||||||
render = () => {
|
|
||||||
p(this.name)
|
|
||||||
p("asd")
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
}, randomName("file"))
|
|
||||||
|
|
||||||
|
|
||||||
const file = File()
|
|
||||||
file.name = "hey123"
|
|
||||||
|
|
||||||
if(file.children[1].innerText === "hey123") {
|
|
||||||
return "Paragraph innertext falsely changed"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultObservedObject() {
|
DefaultObservedObject() {
|
||||||
window.Form = class Form extends ObservedObject {
|
window.Form = class Form extends ObservedObject {
|
||||||
@@ -156,4 +126,37 @@ window.testSuites.push( class testRender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotExtensible() {
|
||||||
|
return "not done"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MustInitAllFields() {
|
||||||
|
// class Form extends ObservedObject {
|
||||||
|
// id
|
||||||
|
// path
|
||||||
|
// $canvasPosition
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||||
|
// if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
||||||
|
// return "Not all fields initialized!"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ChangingObjChangesInstance() {
|
||||||
|
// class Form extends ObservedObject {
|
||||||
|
// id
|
||||||
|
// path
|
||||||
|
// $canvasPosition
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let json = {id: "123", path: "/", canvasPosition: "25|25"}
|
||||||
|
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||||
|
// json.id = "456"
|
||||||
|
// if(!(obj["id"] === "456")) {
|
||||||
|
// return "Change to original object was not reflected!"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
"(" is preceding character: el, el.attr, if, switch
|
||||||
|
el: the el is window.rendering
|
||||||
|
el.attr:
|
||||||
|
find the function and attr in the string
|
||||||
|
if there are multiple instances of being used with this el, add to a list (and if no list then make it and we are first)
|
||||||
|
if: the el is window.rendering. rerender el
|
||||||
|
switch: the el is window.rendering. rerender el
|
||||||
|
*/
|
||||||
|
|
||||||
window.testSuites.push(
|
window.testSuites.push(
|
||||||
|
|
||||||
|
|
||||||
class ParseRender {
|
class ParseRender {
|
||||||
// CopyTo() {
|
|
||||||
// let str = "render=()=>{VStack(()=>{"
|
|
||||||
// let ret = str.copyTo("{")
|
|
||||||
|
|
||||||
// if(ret !== "render=()=>") return "Copy 1 failed!"
|
|
||||||
// }
|
|
||||||
|
|
||||||
ParseRender() {
|
ParseRender() {
|
||||||
class Sidebar extends Shadow {
|
class Sidebar extends Shadow {
|
||||||
126
Test/Skeleton/state.test.js
Normal file
126
Test/Skeleton/state.test.js
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
window.testSuites.push( class testState {
|
||||||
|
|
||||||
|
SimpleParagraphWithState() {
|
||||||
|
class File extends Shadow {
|
||||||
|
$form
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.form.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File, randomName("file"))
|
||||||
|
let form = {data: "asdf"}
|
||||||
|
const el = window.File(form)
|
||||||
|
|
||||||
|
if(!(el.firstChild?.matches("p"))) {
|
||||||
|
return `Child paragraph not rendered`
|
||||||
|
}
|
||||||
|
if(!(el.firstChild.innerText === "asdf")) {
|
||||||
|
return "Child para does not have inner text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParagraphConstructorChangeState() {
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$name
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
|
||||||
|
let name = "asdf"
|
||||||
|
const file = File(name)
|
||||||
|
file.name = "hey123"
|
||||||
|
|
||||||
|
if(file.firstChild.innerText !== "hey123") {
|
||||||
|
return "Paragraph did not react to change!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteralDoesNotCreateFalseReactivity() {
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$name = "asd"
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.name)
|
||||||
|
p("asd")
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
|
||||||
|
const file = File()
|
||||||
|
file.name = "hey123"
|
||||||
|
|
||||||
|
if(file.children[1].innerText === "hey123") {
|
||||||
|
return "Paragraph innertext falsely changed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
State itself should check if the reactivity is based on an element or a standalone expression
|
||||||
|
If standalone, handle it
|
||||||
|
If element, push the info for initReactivity to handle it
|
||||||
|
*/
|
||||||
|
|
||||||
|
TernaryInState() {
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$name
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p(this.name)
|
||||||
|
.fontSize(this.name === "asdf" ? 16 : 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
let name = "asdf"
|
||||||
|
const file = File(name)
|
||||||
|
|
||||||
|
if(file.style.fontSize !== "16px") {
|
||||||
|
return "fail"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateWorksWithCustomStyleFunctions() {
|
||||||
|
// reactive setting needs to use the actual style functions
|
||||||
|
|
||||||
|
register(class File extends Shadow {
|
||||||
|
$paraWidth = 16
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
p("guppy")
|
||||||
|
.width(this.paraWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}, randomName("file"))
|
||||||
|
|
||||||
|
const file = File()
|
||||||
|
file.paraWidth = 18
|
||||||
|
|
||||||
|
if(file.firstChild.style.width !== "18px") {
|
||||||
|
return "Width did not reactively change!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
window.testSuites.push( class testObservedObject {
|
|
||||||
|
|
||||||
FromJSONFailsWithoutAllFields() {
|
|
||||||
class Form extends ObservedObject {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
$canvasPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let obj = Form.create({id: "123"})
|
|
||||||
return "Not implemented"
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
FromJSONInitsAllFields() {
|
|
||||||
class Form extends ObservedObject {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
$canvasPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
|
||||||
if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
|
||||||
return "Not all fields initialized!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultValueWorks() {
|
|
||||||
class WindowState extends ObservedObject {
|
|
||||||
$sidebarOut = false
|
|
||||||
}
|
|
||||||
|
|
||||||
let obj = WindowState.create()
|
|
||||||
console.log(obj)
|
|
||||||
|
|
||||||
if(obj.sidebarOut !== false) {
|
|
||||||
return "Default field not set"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// throw some sort of warning if a global OO is accessed without "this"
|
|
||||||
|
|
||||||
NotExtensible() {
|
|
||||||
return "not done"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MustInitAllFields() {
|
|
||||||
// class Form extends ObservedObject {
|
|
||||||
// id
|
|
||||||
// path
|
|
||||||
// $canvasPosition
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
|
||||||
// if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
|
||||||
// return "Not all fields initialized!"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ChangingObjChangesInstance() {
|
|
||||||
// class Form extends ObservedObject {
|
|
||||||
// id
|
|
||||||
// path
|
|
||||||
// $canvasPosition
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let json = {id: "123", path: "/", canvasPosition: "25|25"}
|
|
||||||
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
|
||||||
// json.id = "456"
|
|
||||||
// if(!(obj["id"] === "456")) {
|
|
||||||
// return "Change to original object was not reflected!"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
})
|
|
||||||
12
Test/test.js
12
Test/test.js
@@ -1,11 +1,13 @@
|
|||||||
console.log("Tests initializing.")
|
console.log("Tests initializing.")
|
||||||
window.testSuites = [];
|
window.testSuites = [];
|
||||||
|
|
||||||
await import ("./parse.test.js")
|
await import ("./Skeleton/parse.test.js")
|
||||||
await import ("./init.test.js")
|
await import ("./Skeleton/init.test.js")
|
||||||
await import ("./render.test.js")
|
await import ("./Skeleton/observedobject.test.js")
|
||||||
await import ("./observedobject.test.js")
|
await import ("./Skeleton/parserender.test.js")
|
||||||
await import ("./parserender.test.js")
|
await import ("./Skeleton/state.test.js")
|
||||||
|
await import ("./Element/stacks.test.js")
|
||||||
|
await import ("./Element/Group.test.js")
|
||||||
|
|
||||||
window.randomName = function randomName(prefix) {
|
window.randomName = function randomName(prefix) {
|
||||||
const sanitizedPrefix = prefix.toLowerCase().replace(/[^a-z0-9]/g, '');
|
const sanitizedPrefix = prefix.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Forked from this repository:
|
|||||||
|
|
||||||
## Boilerplate:
|
## Boilerplate:
|
||||||
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
||||||
- ```*element```: Type in a JS file and select the suggestion to create JS Custom Element boilerplate.
|
- ```*shadow```: Type in a JS file and select the suggestion to create JS Custom Element boilerplate.
|
||||||
|
|
||||||
## Functions:
|
## Functions:
|
||||||
Clone this repository into the top level of the project you are working on, so the HTML can find the "quill.js" functions.
|
Clone this repository into the top level of the project you are working on, so the HTML can find the "quill.js" functions.
|
||||||
|
|||||||
15
VSCode/deploy-howto.md
Normal file
15
VSCode/deploy-howto.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# To Deploy Extension
|
||||||
|
|
||||||
|
```vsce package```
|
||||||
|
|
||||||
|
```vsce publish```
|
||||||
|
|
||||||
|
# To Get PAT
|
||||||
|
|
||||||
|
Login to Azure
|
||||||
|
Search "Devops"
|
||||||
|
Login to devops (samuel@sun.museum)
|
||||||
|
Click "User Settings" in the top right > "Personal Access Tokens"
|
||||||
|
Create one which has "Allow all Organizations" and "Marketplace" > "Manage" selected
|
||||||
|
Put it in the .env file here
|
||||||
|
set it in the terminal like `export VSCE_PAT="<token>"`
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "quill",
|
"name": "quill",
|
||||||
"displayName": "Quill",
|
"displayName": "Quill",
|
||||||
"description": "HTML/CSS Syntax highlighting, best used with the Quill framework",
|
"description": "HTML/CSS Syntax highlighting, best used with the Quill framework",
|
||||||
"version": "1.0.4",
|
"version": "1.0.6",
|
||||||
"publisher": "capturedsun",
|
"publisher": "capturedsun",
|
||||||
"icon": "docs/Quill.png",
|
"icon": "docs/Quill.png",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
BIN
VSCode/quill-1.0.5.vsix
Normal file
BIN
VSCode/quill-1.0.5.vsix
Normal file
Binary file not shown.
@@ -2,46 +2,17 @@
|
|||||||
"*page": {
|
"*page": {
|
||||||
"prefix": "*html",
|
"prefix": "*html",
|
||||||
"body": [
|
"body": [
|
||||||
"<!DOCTYPE html>",
|
"<!DOCTYPE html>",
|
||||||
"<html lang=\"en\">",
|
"<html lang=\"en\">",
|
||||||
" <head>",
|
" <head>",
|
||||||
" <title>Quill</title>",
|
" <title>Quill</title>",
|
||||||
" <link rel=\"icon\" href=\"\">",
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
|
||||||
" <link rel=\"stylesheet\" href=\"\">",
|
" <link rel=\"icon\" href=\"/_/icons/logo.svg\">",
|
||||||
" <script type=\"module\">",
|
" <link rel=\"stylesheet\" href=\"/_/code/shared.css\">",
|
||||||
" window.addStyle = function addStyle(cssString) {",
|
" <script src=\"/_/code/quill.js\"></script>",
|
||||||
" let container = document.querySelector(\"style#quillStyles\");",
|
" <script type=\"module\" src=\"75820185/index.js\"></script>",
|
||||||
" if(!container) {",
|
|
||||||
" container = document.createElement('style');",
|
|
||||||
" container.id = \"quillStyles\";",
|
|
||||||
" document.head.appendChild(container);",
|
|
||||||
" }",
|
|
||||||
"",
|
|
||||||
" let primarySelector = cssString.substring(0, cssString.indexOf(\"{\"));",
|
|
||||||
" primarySelector = primarySelector.replace(/\\*/g, \"all\");",
|
|
||||||
" primarySelector = primarySelector.replace(/#/g, \"id-\");",
|
|
||||||
" primarySelector = primarySelector.replace(/,/g, \"\");",
|
|
||||||
" let stylesheet = container.querySelector(`:scope > style[id='${primarySelector}']`)",
|
|
||||||
" if(!stylesheet) {",
|
|
||||||
" stylesheet = document.createElement('style');",
|
|
||||||
" stylesheet.id = primarySelector;",
|
|
||||||
" stylesheet.appendChild(document.createTextNode(cssString));",
|
|
||||||
" container.appendChild(stylesheet);",
|
|
||||||
" } else {",
|
|
||||||
" stylesheet.innerText = cssString",
|
|
||||||
" }",
|
|
||||||
" }",
|
|
||||||
"",
|
|
||||||
" window.html = function html(elementString) {",
|
|
||||||
" let parser = new DOMParser();",
|
|
||||||
" let doc = parser.parseFromString(elementString, 'text/html');",
|
|
||||||
" return doc.body.firstChild;",
|
|
||||||
" }",
|
|
||||||
" </script>",
|
|
||||||
" <script type=\"module\" src=\"\"></script>",
|
|
||||||
" </head>",
|
" </head>",
|
||||||
" <body>",
|
" <body style=\"margin: 0px\">",
|
||||||
"",
|
|
||||||
" </body>",
|
" </body>",
|
||||||
"</html>"
|
"</html>"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"html": {
|
"html": {
|
||||||
"prefix": "html",
|
"prefix": "html",
|
||||||
"body": [
|
"body": [
|
||||||
"html(`$1`);"
|
"html(`$`);"
|
||||||
],
|
],
|
||||||
"description": "Create a DOM node"
|
"description": "Create a DOM node"
|
||||||
},
|
},
|
||||||
@@ -15,35 +15,17 @@
|
|||||||
"description": "Use the DOM collection of functions"
|
"description": "Use the DOM collection of functions"
|
||||||
},
|
},
|
||||||
|
|
||||||
"*element": {
|
"*shadow": {
|
||||||
"prefix": "*element",
|
"prefix": "*shadow",
|
||||||
"body": [
|
"body": [
|
||||||
"class Index extends HTMLElement {",
|
"class Index extends Shadow {",
|
||||||
"",
|
" render() {",
|
||||||
" css = /*css*/ `",
|
|
||||||
" index-el {",
|
|
||||||
" ",
|
|
||||||
" }",
|
|
||||||
" `",
|
|
||||||
"",
|
|
||||||
" html = /*html*/ `",
|
|
||||||
" `",
|
|
||||||
"",
|
|
||||||
" constructor() {",
|
|
||||||
" super();",
|
|
||||||
" addStyle(this.css);",
|
|
||||||
" this.innerHTML = this.html;",
|
|
||||||
" }",
|
|
||||||
"",
|
|
||||||
" connectedCallback() {",
|
|
||||||
" ",
|
" ",
|
||||||
" }",
|
" }",
|
||||||
"",
|
|
||||||
"}",
|
"}",
|
||||||
"",
|
" ",
|
||||||
"customElements.define('index-el', Index);",
|
"register(Index)"
|
||||||
"export default Index;"
|
|
||||||
],
|
],
|
||||||
"description": "Custom Element Template"
|
"description": "Custom Shadow Template"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
26
about.md
26
about.md
@@ -1,26 +0,0 @@
|
|||||||
Attribute/State Cases:
|
|
||||||
|
|
||||||
Dual Instantiation
|
|
||||||
- HTML-first instantiation from attributes (when first loaded and parsed)
|
|
||||||
observedAttributes will pick this up
|
|
||||||
- JS-first instantiation where attributes are set from constructor (or) from init function (or)
|
|
||||||
press puts attributes on from state before saving
|
|
||||||
init function can set attributes and variables - perhaps state is always required to be passed in
|
|
||||||
|
|
||||||
Usage Flexibility
|
|
||||||
- attributes can have default values
|
|
||||||
$url = "hey"
|
|
||||||
- attributes can be named or unnamed when passed in to constructor functions
|
|
||||||
|
|
||||||
Attribute / State Reflexivity
|
|
||||||
- when attribute is changed, state value is changed
|
|
||||||
modify prototype at runtime? Add overrides for setattr and remove? ||
|
|
||||||
use observedAttributes + attributeChanged (not good) - Forms parent element?
|
|
||||||
- when state is changed, attribute value is changed
|
|
||||||
modify prototype at runtime to add a setter for the state such that when it is set it sets the attribute
|
|
||||||
|
|
||||||
Bindings
|
|
||||||
- should be able to have a child variable be bound to that of a parent
|
|
||||||
|
|
||||||
Binding is denoted by prior to variable
|
|
||||||
State is denoted by "$" prior to variable
|
|
||||||
13
app.js
13
app.js
@@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
Captured Sun
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Home extends Page {
|
|
||||||
render = () => {
|
|
||||||
("hello world")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Home
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# To Deploy Extension
|
|
||||||
|
|
||||||
```vsce package```
|
|
||||||
|
|
||||||
```vsce publish```
|
|
||||||
20
index.html
20
index.html
@@ -1,20 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Quill</title>
|
|
||||||
<link rel="icon" href="">
|
|
||||||
<link rel="stylesheet" href="">
|
|
||||||
<script src=""></script>
|
|
||||||
<script src="Quill/index.js"></script>
|
|
||||||
<script type="module">
|
|
||||||
import Home from "./app.js"
|
|
||||||
|
|
||||||
window.routes = {
|
|
||||||
"/": Home
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
151
notes.txt
151
notes.txt
@@ -1,151 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Shadow.File = (param) => new class {
|
|
||||||
trippy = "asd"
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
console.log(param)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(Shadow.File("as"))
|
|
||||||
|
|
||||||
|
|
||||||
ObservedObject.WindowState = () => ({
|
|
||||||
sidebarOut: false,
|
|
||||||
children: null
|
|
||||||
})
|
|
||||||
let windowState = ObservedObject.WindowState()
|
|
||||||
console.log(windowState)
|
|
||||||
|
|
||||||
|
|
||||||
Shadow(class File {
|
|
||||||
|
|
||||||
}, "file-")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3/16
|
|
||||||
const TestClass = () => new class {
|
|
||||||
constructor() {
|
|
||||||
console.log("hey")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
This works. Could extend shadow. This way the function and the actual code are not separate.
|
|
||||||
|
|
||||||
|
|
||||||
3/10
|
|
||||||
|
|
||||||
Ran into a problem where I can't call the same element within itself.
|
|
||||||
|
|
||||||
There are two problems:
|
|
||||||
|
|
||||||
1. Javascript scoping means that it tries to call the class it is inside of.
|
|
||||||
2. Quill instantiates the object when registered, to track its state
|
|
||||||
|
|
||||||
|
|
||||||
This is what I ended up going with - simply not using the Space() recursively and instead making a child space.
|
|
||||||
|
|
||||||
class Space extends HTMLElement {
|
|
||||||
form = Forms.observe(window.location.pathname, this)
|
|
||||||
|
|
||||||
contents = [
|
|
||||||
...this.form.children.map(form => {
|
|
||||||
switch(form.type) {
|
|
||||||
case "file":
|
|
||||||
return File()
|
|
||||||
case "space":
|
|
||||||
return ChildSpace()
|
|
||||||
case "link":
|
|
||||||
return Link()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
console.log(this.form.path)
|
|
||||||
console.log(this.contents)
|
|
||||||
this.render(...this.contents)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This was my attempt to see if an anonymous class can be used. The class functions and extends HTMLElement - so problem #2 is solved.
|
|
||||||
Problem #1 however, scoping, is not solved unless putting "window.Space()" instead of Space() here, so that it does not
|
|
||||||
attempt to access the named function value. It seems both functions and classes have this problem. Perhaps there is a
|
|
||||||
different way it could be done in Quill, like so -
|
|
||||||
|
|
||||||
|
|
||||||
const el = () => class extends HTMLElement {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
quill.register(el, "Space", "parchment-space")
|
|
||||||
|
|
||||||
|
|
||||||
However, not naming it at the top is less than desirable.
|
|
||||||
|
|
||||||
quill("Space", class extends HTMLElement {
|
|
||||||
form = Forms.observe(window.location.pathname, this)
|
|
||||||
|
|
||||||
contents = [
|
|
||||||
...this.form.children.map(form => {
|
|
||||||
switch(form.type) {
|
|
||||||
case "file":
|
|
||||||
return File()
|
|
||||||
case "space":
|
|
||||||
return ChildSpace()
|
|
||||||
case "link":
|
|
||||||
return Link()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
console.log(this.form.path)
|
|
||||||
console.log(this.contents)
|
|
||||||
this.render(...this.contents)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this would probably work. Still less than ideal but maybe if we used syntax highlighting it could be alright.
|
|
||||||
How to name something without the class having access? This is the problem. I didn't try it without being an arrow function so
|
|
||||||
perhaps this would have a chance.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// const Space = () => class extends HTMLElement {
|
|
||||||
// form = Forms.observe(window.location.pathname, this)
|
|
||||||
|
|
||||||
// contents = [
|
|
||||||
// ...this.form.children.map(form => {
|
|
||||||
// switch(form.type) {
|
|
||||||
// case "file":
|
|
||||||
// return File()
|
|
||||||
// case "space":
|
|
||||||
// return Space()
|
|
||||||
// case "link":
|
|
||||||
// return Link()
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// constructor() {
|
|
||||||
// super()
|
|
||||||
// console.log(this.form.path)
|
|
||||||
// console.log(this.contents)
|
|
||||||
// this.render(...this.contents)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let instan = Space()
|
|
||||||
// customElements.define("space-", instan)
|
|
||||||
// window.Space = () => "boi"
|
|
||||||
// console.log(new instan())
|
|
||||||
|
|
||||||
window.register(Space, "parchment-space")
|
|
||||||
151
path.js
151
path.js
@@ -1,151 +0,0 @@
|
|||||||
class PathProcessor {
|
|
||||||
path;
|
|
||||||
hasTrailingSlash = false;
|
|
||||||
node = (typeof module !== 'undefined' && module.exports) ? true : false;
|
|
||||||
|
|
||||||
constructor(path) {
|
|
||||||
this.path = path;
|
|
||||||
if(path === undefined) {
|
|
||||||
this.path = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#removeTrailingSlash(path) {
|
|
||||||
if(path === "/") {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return path.endsWith("/") ? path.slice(0, -1) : path
|
|
||||||
}
|
|
||||||
|
|
||||||
full() {
|
|
||||||
if(!this.node) return;
|
|
||||||
let path = this.path;
|
|
||||||
this.path = ""
|
|
||||||
this.join(Path.homedir(), path)
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
web() {
|
|
||||||
if(!this.node) return;
|
|
||||||
const os = require('os')
|
|
||||||
this.path = this.path.replace(this.#removeTrailingSlash(os.homedir()), "")
|
|
||||||
if(this.path === "") {
|
|
||||||
this.path = "/"
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
encode() { // Uses the built in encodeURI and also encodes certain characters that are't covered by it
|
|
||||||
this.path = encodeURI(this.path).replace(/[!'()*]/g, function(c) {
|
|
||||||
return '%' + c.charCodeAt(0).toString(16);
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoded() {
|
|
||||||
this.path = decodeURIComponent(this.path);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent() {
|
|
||||||
const parts = this.path.split('/').filter(Boolean);
|
|
||||||
parts.pop();
|
|
||||||
this.path = '/' + parts.join('/');
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
const parts = this.path.split('/').filter(Boolean);
|
|
||||||
this.path = parts.pop() || '';
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
trailingSlash() {
|
|
||||||
this.path = this.path.endsWith("/") ? this.path : this.path+"/";
|
|
||||||
this.hasTrailingSlash = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
noTrailingSlash() {
|
|
||||||
this.path = this.#removeTrailingSlash(this.path)
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
join(...segments) {
|
|
||||||
if (this.path) {
|
|
||||||
segments.unshift(this.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.path = segments
|
|
||||||
.map((part, index) => {
|
|
||||||
if (index === 0) {
|
|
||||||
return part.trim().replace(/[/]*$/g, '');
|
|
||||||
} else {
|
|
||||||
return part.trim().replace(/(^[/]*|[/]*$)/g, '');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(Boolean) // Remove empty segments
|
|
||||||
.join('/');
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
components() {
|
|
||||||
return this.path.split('/').filter(Boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
return this.hasTrailingSlash ? this.path : this.#removeTrailingSlash(this.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Path {
|
|
||||||
static full(path) {
|
|
||||||
return new PathProcessor(path).full();
|
|
||||||
}
|
|
||||||
|
|
||||||
static web(path) {
|
|
||||||
return new PathProcessor(path).web();
|
|
||||||
}
|
|
||||||
|
|
||||||
static decoded(path) {
|
|
||||||
return new PathProcessor(path).decoded()
|
|
||||||
}
|
|
||||||
|
|
||||||
static encode(path) {
|
|
||||||
return new PathProcessor(path).encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
static parent(path) {
|
|
||||||
return new PathProcessor(path).parent();
|
|
||||||
}
|
|
||||||
|
|
||||||
static end(path) {
|
|
||||||
return new PathProcessor(path).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
static trailingSlash(path) {
|
|
||||||
return new PathProcessor(path).trailingSlash();
|
|
||||||
}
|
|
||||||
|
|
||||||
static noTrailingSlash(path) {
|
|
||||||
return new PathProcessor(path).noTrailingSlash();
|
|
||||||
}
|
|
||||||
|
|
||||||
static components(path) {
|
|
||||||
return new PathProcessor(path).components();
|
|
||||||
}
|
|
||||||
|
|
||||||
static join(...segments) {
|
|
||||||
return new PathProcessor(null).join(...segments);
|
|
||||||
}
|
|
||||||
|
|
||||||
static homedir() {
|
|
||||||
if(typeof module === 'undefined' || !module.exports) return;
|
|
||||||
const os = require('os')
|
|
||||||
let ret = os.homedir().replace(/\\/g, '/').replace(/^[a-zA-Z]:/, '');
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.Path = Path;
|
|
||||||
Reference in New Issue
Block a user