11.2025: new version released, sample folder added
This commit is contained in:
1
.env
Normal file
1
.env
Normal file
@@ -0,0 +1 @@
|
||||
VSCE_PAT=9wucEHBNFD5RTS403Oyoflyi984cAwAfa42FHN7tmqmzfbXsSKP9JQQJ99BKACAAAAAAAAAAAAAGAZDO25Ro
|
||||
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()
|
||||
@@ -5,7 +5,7 @@ Forked from this repository:
|
||||
|
||||
## 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:
|
||||
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",
|
||||
"displayName": "Quill",
|
||||
"description": "HTML/CSS Syntax highlighting, best used with the Quill framework",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.6",
|
||||
"publisher": "capturedsun",
|
||||
"icon": "docs/Quill.png",
|
||||
"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": {
|
||||
"prefix": "*html",
|
||||
"body": [
|
||||
"<!DOCTYPE html>",
|
||||
"<!DOCTYPE html>",
|
||||
"<html lang=\"en\">",
|
||||
" <head>",
|
||||
" <title>Quill</title>",
|
||||
" <link rel=\"icon\" href=\"\">",
|
||||
" <link rel=\"stylesheet\" href=\"\">",
|
||||
" <script type=\"module\">",
|
||||
" window.addStyle = function addStyle(cssString) {",
|
||||
" let container = document.querySelector(\"style#quillStyles\");",
|
||||
" 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>",
|
||||
" <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>",
|
||||
"",
|
||||
" <body style=\"margin: 0px\">",
|
||||
" </body>",
|
||||
"</html>"
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"html": {
|
||||
"prefix": "html",
|
||||
"body": [
|
||||
"html(`$1`);"
|
||||
"html(`$`);"
|
||||
],
|
||||
"description": "Create a DOM node"
|
||||
},
|
||||
@@ -15,35 +15,17 @@
|
||||
"description": "Use the DOM collection of functions"
|
||||
},
|
||||
|
||||
"*element": {
|
||||
"prefix": "*element",
|
||||
"*shadow": {
|
||||
"prefix": "*shadow",
|
||||
"body": [
|
||||
"class Index extends HTMLElement {",
|
||||
"",
|
||||
" css = /*css*/ `",
|
||||
" index-el {",
|
||||
" ",
|
||||
" }",
|
||||
" `",
|
||||
"",
|
||||
" html = /*html*/ `",
|
||||
" `",
|
||||
"",
|
||||
" constructor() {",
|
||||
" super();",
|
||||
" addStyle(this.css);",
|
||||
" this.innerHTML = this.html;",
|
||||
" }",
|
||||
"",
|
||||
" connectedCallback() {",
|
||||
"class Index extends Shadow {",
|
||||
" render() {",
|
||||
" ",
|
||||
" }",
|
||||
"",
|
||||
"}",
|
||||
"",
|
||||
"customElements.define('index-el', Index);",
|
||||
"export default Index;"
|
||||
" ",
|
||||
"register(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