added navigation and a lot of unit tests
This commit is contained in:
8
Test/Pages/home.js
Normal file
8
Test/Pages/home.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class Home extends Page {
|
||||||
|
results = window.test
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home
|
||||||
@@ -12,15 +12,109 @@ window.testSuites.push( class testShadow {
|
|||||||
window.register(File, "file-el")
|
window.register(File, "file-el")
|
||||||
let form = {data: "asdf"}
|
let form = {data: "asdf"}
|
||||||
const el = window.File(form)
|
const el = window.File(form)
|
||||||
console.log(el, el.$form, el._form)
|
|
||||||
if(!(el.form === form)) {
|
if(!(el.form === form)) {
|
||||||
return `State field does not match object passed in!`
|
return `State field does not match object passed in!`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testRegisterThrowsIfNoConstructorParams() {
|
testMultiParams() {
|
||||||
class File2 extends Shadow {
|
class File2 extends Shadow {
|
||||||
$form
|
$form
|
||||||
|
$tag
|
||||||
|
|
||||||
|
constructor(...params) {
|
||||||
|
super(...params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File2, "file2-el")
|
||||||
|
let form = {data: "asdf"}
|
||||||
|
const el = window.File2(form, "tag")
|
||||||
|
if(!(el.form === form)) {
|
||||||
|
return `Form field does not match object passed in!`
|
||||||
|
}
|
||||||
|
if(!(el.tag === "tag")) {
|
||||||
|
return `Tag field does not match object passed in!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyGetFieldsNotUsing$() {
|
||||||
|
class File5 extends Shadow {
|
||||||
|
$form
|
||||||
|
$tag
|
||||||
|
|
||||||
|
constructor(...params) {
|
||||||
|
super(...params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File5, "file5-el")
|
||||||
|
let form = {data: "asdf"}
|
||||||
|
const el = window.File5(form, "tag")
|
||||||
|
if(el.$tag !== undefined) {
|
||||||
|
return "Got field the wrong way!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testChangeAttrChangesField() {
|
||||||
|
class File3 extends Shadow {
|
||||||
|
$form
|
||||||
|
$tag
|
||||||
|
|
||||||
|
constructor(...params) {
|
||||||
|
super(...params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File3, "file3-el")
|
||||||
|
let form = {data: "asdf"}
|
||||||
|
const el = window.File3(form, "tag")
|
||||||
|
el.setAttribute("tag", "asdf")
|
||||||
|
if(el.tag !== "asdf") {
|
||||||
|
return "Field did not change!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testChangeFieldChangesAttr() {
|
||||||
|
class File4 extends Shadow {
|
||||||
|
$form
|
||||||
|
$tag
|
||||||
|
|
||||||
|
constructor(...params) {
|
||||||
|
super(...params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File4, "file4-el")
|
||||||
|
let form = {data: "asdf"}
|
||||||
|
const el = window.File4(form, "tag")
|
||||||
|
el.tag = "asdf"
|
||||||
|
if(el.getAttribute("tag") !== "asdf") {
|
||||||
|
return "Attribute did not change!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDefaultStateFieldWorks() {
|
||||||
|
class File6 extends Shadow {
|
||||||
|
$form = {data: "asdf"}
|
||||||
|
|
||||||
|
constructor(...params) {
|
||||||
|
super(...params)
|
||||||
|
console.log(this.form)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.register(File6, "file6-el")
|
||||||
|
const el = window.File6()
|
||||||
|
console.log(el, el.$form, el._form)
|
||||||
|
if(el.form === undefined) {
|
||||||
|
return `Default value did not work`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testRegisterThrowsIfNoConstructorParams() {
|
||||||
|
class File3 extends Shadow {
|
||||||
|
$form
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
@@ -28,7 +122,7 @@ window.testSuites.push( class testShadow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.register(File2, "file2-el")
|
window.register(File3, "file3-el")
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
return "Error not thrown!"
|
return "Error not thrown!"
|
||||||
|
|||||||
@@ -4,9 +4,15 @@
|
|||||||
<title>Quill</title>
|
<title>Quill</title>
|
||||||
<link rel="icon" href="">
|
<link rel="icon" href="">
|
||||||
<link rel="stylesheet" href="">
|
<link rel="stylesheet" href="">
|
||||||
<script src=""></script>
|
|
||||||
<script src="../index.js"></script>
|
<script src="../index.js"></script>
|
||||||
<script src="test.js"></script>
|
<script src="test.js"></script>
|
||||||
|
<script type="module">
|
||||||
|
import Home from "./Pages/home.js";
|
||||||
|
|
||||||
|
window.routes = {
|
||||||
|
"/Test": Home
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|||||||
14
Test/test.js
14
Test/test.js
@@ -15,7 +15,7 @@ window.test = async function() {
|
|||||||
var start = new Date();
|
var start = new Date();
|
||||||
for(let j=0; j<window.testSuites.length; j++) {
|
for(let j=0; j<window.testSuites.length; j++) {
|
||||||
let testSuite = window.testSuites[j];
|
let testSuite = window.testSuites[j];
|
||||||
console.log(`%c ➽ ${j+1} ${testSuite.name.replace("test", "")}`, 'color: #ffffff; font-size: 17px; padding-left: -20px; padding-top: 10px; padding-bottom: 10px; text-align: right;')
|
console.log(`%c ➽ ${j+1} ${testSuite.name.replace("test", "")}`, 'color: #e9c9a0; border: 3px solid #e9c9a0; border-radius: 10px; font-size: 17px; padding-left: -20px; margin-top: 20px; margin-bottom: 20px; padding-top: 10px; padding-bottom: 10px; padding-right: 10px; text-align: right;')
|
||||||
let suite = new testSuite();
|
let suite = new testSuite();
|
||||||
let testNum = 0;
|
let testNum = 0;
|
||||||
let suiteContents = Object.getOwnPropertyNames(testSuite.prototype)
|
let suiteContents = Object.getOwnPropertyNames(testSuite.prototype)
|
||||||
@@ -23,13 +23,21 @@ window.test = async function() {
|
|||||||
let test = suiteContents[i];
|
let test = suiteContents[i];
|
||||||
if(typeof suite[test] === 'function' && test !== "constructor") {
|
if(typeof suite[test] === 'function' && test !== "constructor") {
|
||||||
testNum++;
|
testNum++;
|
||||||
|
console.log(`%c${testNum}. ${test}`, "margin-top: 10px; border-top: 2px solid #e9c9a0; color: #e9c9a0; border-radius: 10px; padding: 10px;");
|
||||||
let fail = await suite[test]();
|
let fail = await suite[test]();
|
||||||
if(fail) {
|
if(fail) {
|
||||||
failed++;
|
failed++;
|
||||||
console.log(`%c ${testNum}. ${test}: ${fail}`, 'background: #222; color: rgb(254, 62, 43)');
|
let spaceNum = test.length - fail.length
|
||||||
|
let spaces = ""
|
||||||
|
if(spaceNum > 0) {
|
||||||
|
for(let i=0; i < spaceNum; i++) {
|
||||||
|
spaces += " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`%c ${fail}${spaces}`, 'border-bottom: 2px solid #e9c9a0; color: rgb(254, 62, 43); border-radius: 10px; padding: 10px');
|
||||||
} else {
|
} else {
|
||||||
success++;
|
success++;
|
||||||
console.log(`%c ${testNum}. ${test}`, 'background: #222; color: #00FF00');
|
console.log(`%c${testNum}. ${test}`, 'border-bottom: 2px solid #e9c9a0; background: #628c60; color: transparent; border-radius: 10px; padding: 0px 10px 0px 10px');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Need to flush ws buffer since saving is disabled, and that is what usually does the flushing
|
// Need to flush ws buffer since saving is disabled, and that is what usually does the flushing
|
||||||
|
|||||||
379
index.js
379
index.js
@@ -1,3 +1,61 @@
|
|||||||
|
/* NAVIGATION */
|
||||||
|
|
||||||
|
let oldPushState = history.pushState;
|
||||||
|
history.pushState = function pushState() {
|
||||||
|
let ret = oldPushState.apply(this, arguments);
|
||||||
|
window.dispatchEvent(new Event('pushstate'));
|
||||||
|
window.dispatchEvent(new Event('locationchange'));
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('popstate', () => {
|
||||||
|
window.dispatchEvent(new Event('locationchange'));
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('locationchange', locationChange);
|
||||||
|
let urlBeforeChange = window.location.href;
|
||||||
|
|
||||||
|
window.navigateTo = function(url) {
|
||||||
|
window.history.pushState({}, '', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'routes', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
set: function(newValue) {
|
||||||
|
Object.defineProperty(window, 'routes', {
|
||||||
|
value: newValue,
|
||||||
|
writable: false,
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
locationChange();
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
return window.routes;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function locationChange() {
|
||||||
|
let URL = window.location.pathname.split("/").filter(d => (d !== 'Web') && (!d.includes('.html'))).join("/")
|
||||||
|
if(URL === "") URL = "/"
|
||||||
|
|
||||||
|
console.log("Location change: ", URL)
|
||||||
|
|
||||||
|
if(!window.routes[URL]) {
|
||||||
|
console.error("Quill: no URL for this route: ", URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let page = new window.routes[URL]()
|
||||||
|
window.rendering.push(page)
|
||||||
|
page.render()
|
||||||
|
window.rendering.pop(page)
|
||||||
|
|
||||||
|
urlBeforeChange = window.location.href;
|
||||||
|
}
|
||||||
|
|
||||||
/* $() */
|
/* $() */
|
||||||
|
|
||||||
HTMLElement.prototype.$ = function(selector) {
|
HTMLElement.prototype.$ = function(selector) {
|
||||||
@@ -14,6 +72,77 @@ window.$ = function(selector, el = document) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CONSOLE */
|
||||||
|
|
||||||
|
console.red = function(message) {
|
||||||
|
this.log(`%c${message}`, "color: rgb(254, 79, 42);");
|
||||||
|
};
|
||||||
|
|
||||||
|
console.green = function(message) {
|
||||||
|
this.log(`%c${message}`, "color: rgb(79, 254, 42);");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STRING TRANSLATORS */
|
||||||
|
|
||||||
|
window.css = function css(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("{")).trim();
|
||||||
|
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(htmlString) {
|
||||||
|
let container = document.createElement('div');
|
||||||
|
container.innerHTML = htmlString;
|
||||||
|
|
||||||
|
// If there's only one child, return it directly
|
||||||
|
if (container.children.length === 1) {
|
||||||
|
return container.children[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are multiple children, use a DocumentFragment
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
while (container.firstChild) {
|
||||||
|
fragment.appendChild(container.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* COMPATIBILITY */
|
||||||
|
|
||||||
|
function detectMobile() {
|
||||||
|
const mobileDeviceRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
||||||
|
return mobileDeviceRegex.test(navigator.userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSafariVersion() {
|
||||||
|
const userAgent = navigator.userAgent;
|
||||||
|
const isSafari = userAgent.includes("Safari") && !userAgent.includes("Chrome");
|
||||||
|
if (isSafari) {
|
||||||
|
const safariVersionMatch = userAgent.match(/Version\/(\d+\.\d+)/);
|
||||||
|
const safariVersion = safariVersionMatch ? parseFloat(safariVersionMatch[1]) : null;
|
||||||
|
return safariVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* REGISTER */
|
/* REGISTER */
|
||||||
|
|
||||||
window.ObservedObject = class ObservedObject {
|
window.ObservedObject = class ObservedObject {
|
||||||
@@ -30,26 +159,21 @@ window.Page = class Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.Shadow = class Shadow extends HTMLElement {
|
window.Shadow = class Shadow extends HTMLElement {
|
||||||
constructor(stateVariables, ...params) {
|
constructor(stateNames, ...params) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
console.log(Object.getOwnPropertyDescriptors(this))
|
// State -> Attributes: set each state value as getter and setter
|
||||||
|
stateNames.forEach(name => {
|
||||||
if(!stateVariables) {
|
const backingFieldName = `_${name}`; // Construct the backing field name
|
||||||
console.log("No state variables passed in, returning")
|
if(this["$" + name] !== undefined) { // User provided a default value
|
||||||
return
|
console.log("user default: ", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State -> Attributes
|
|
||||||
// set each state value as getter and setter
|
|
||||||
stateVariables.forEach(attrName => {
|
|
||||||
const backingFieldName = `_${attrName}`; // Construct the backing field name
|
|
||||||
|
|
||||||
Object.defineProperty(this, attrName, {
|
Object.defineProperty(this, name, {
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
// console.log(`Setting attribute ${attrName} to `, newValue);
|
// console.log(`Setting attribute ${name} to `, newValue);
|
||||||
this[backingFieldName] = newValue; // Use the backing field to store the value
|
this[backingFieldName] = newValue; // Use the backing field to store the value
|
||||||
this.setAttribute(attrName, typeof newValue === "object" ? "{..}" : newValue); // Synchronize with the attribute
|
this.setAttribute(name, typeof newValue === "object" ? "{..}" : newValue); // Synchronize with the attribute
|
||||||
},
|
},
|
||||||
get: function() {
|
get: function() {
|
||||||
// console.log("get: ", this[backingFieldName])
|
// console.log("get: ", this[backingFieldName])
|
||||||
@@ -60,42 +184,31 @@ window.Shadow = class Shadow extends HTMLElement {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// if there are more than one, expect them to be named
|
// Match params to state names
|
||||||
if(stateVariables.length > 1) {
|
switch(stateNames.length) {
|
||||||
if(typeof params[0] !== "object") {
|
case 0:
|
||||||
console.error(`Quill: elements with multiple state initializers must structure them like {color: "blue", type: 2}`)
|
console.log("No state variables passed in, returning")
|
||||||
return
|
return
|
||||||
}
|
default:
|
||||||
let i = -1
|
let i = -1
|
||||||
for (let param in params[0]) {
|
for (let param of params) {
|
||||||
i++
|
i++
|
||||||
let paramName = "$" + param
|
|
||||||
if(stateVariables[i] !== paramName) {
|
if(i > stateNames.length) {
|
||||||
if(!stateVariables[i]) {
|
console.error(`${el.prototype.constructor.name}: too many parameters for state!`)
|
||||||
console.error(`${el.prototype.constructor.name}: state ${state} must be initialized`)
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
console.error(`${el.prototype.constructor.name}: state initializer ${i} ${paramName} should be ${stateVariables[0]}`)
|
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
this[paramName] = params[0][param]
|
|
||||||
|
if(this[stateNames[i]] === undefined) {
|
||||||
|
this[stateNames[i]] = param
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if(!params[0] && stateVariables[0]) {
|
|
||||||
console.log(params, stateVariables)
|
|
||||||
console.error(`${el.prototype.constructor.name}: state initializer $${params[0]} should be ${stateVariables[0]}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this[stateVariables[0]] = params[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all state variables are set
|
// Check if all state variables are set. If not set, check if it is a user-initted value
|
||||||
for(let state of stateVariables) {
|
for(let state of stateNames) {
|
||||||
// console.log(elem[state])
|
if(this[state] === undefined) {
|
||||||
if(!this[state]) {
|
console.error(`Quill: state "${state}" must be initialized`)
|
||||||
console.error(`Quill: state ${state} must be initialized`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,42 +252,31 @@ window.Registry = class Registry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static render = (el, parent) => {
|
static render = (el, parent) => {
|
||||||
if(!(el.constructor.name === "Home")) {
|
let renderParent = window.rendering[window.rendering.length-1]
|
||||||
window.rendering[window.rendering.length-1].appendChild(el)
|
if(renderParent) {
|
||||||
|
renderParent.appendChild(el)
|
||||||
}
|
}
|
||||||
window.rendering.push(el)
|
window.rendering.push(el)
|
||||||
el.render()
|
el.render()
|
||||||
window.rendering.pop(el)
|
window.rendering.pop(el)
|
||||||
}
|
}
|
||||||
|
|
||||||
static registerHome(el) {
|
|
||||||
// console.log(params, stateVariables)
|
|
||||||
let home = new el()
|
|
||||||
Registry.render(home)
|
|
||||||
return home
|
|
||||||
}
|
|
||||||
|
|
||||||
static register = (el, tagname) => {
|
static register = (el, tagname) => {
|
||||||
if(el.prototype.constructor.name === "Home") {
|
let stateVariables = this.parseClassFields(el).filter(field => field.startsWith('$')).map(str => str.substring(1));
|
||||||
Registry.registerHome(el)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let stateVariables = this.parseClassFields(el).filter(field => field.startsWith('$'));
|
|
||||||
let stateVariablesWithout$ = stateVariables.map(str => str.substring(1));
|
|
||||||
|
|
||||||
// Observe attributes
|
// Observe attributes
|
||||||
Object.defineProperty(el, 'observedAttributes', {
|
Object.defineProperty(el, 'observedAttributes', {
|
||||||
get: function() {
|
get: function() {
|
||||||
return stateVariablesWithout$;
|
return stateVariables;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attributes -> State
|
// Attributes -> State
|
||||||
Object.defineProperty(el.prototype, 'attributeChangedCallback', {
|
Object.defineProperty(el.prototype, 'attributeChangedCallback', {
|
||||||
value: function(name, oldValue, newValue) {
|
value: function(name, oldValue, newValue) {
|
||||||
const fieldName = `$${name}`;
|
const fieldName = `${name}`;
|
||||||
if (fieldName in this && this[fieldName] !== newValue && newValue !== "[object Object]") {
|
let blacklistedValues = ["[object Object]", "{..}", this[fieldName]]
|
||||||
|
if (stateVariables.includes(fieldName) && !blacklistedValues.includes(newValue)) {
|
||||||
this[fieldName] = newValue;
|
this[fieldName] = newValue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -186,18 +288,52 @@ window.Registry = class Registry {
|
|||||||
|
|
||||||
// Actual Constructor
|
// Actual Constructor
|
||||||
window[el.prototype.constructor.name] = function (...params) {
|
window[el.prototype.constructor.name] = function (...params) {
|
||||||
// console.log(params, stateVariables)
|
let elIncarnate = new el(stateVariables, ...params)
|
||||||
let elIncarnate = new el(stateVariablesWithout$, ...params)
|
// detect default variables, give em the treatment
|
||||||
|
// consider going back to this as the method, since i can then maybe fix the cosntructor issue as well
|
||||||
|
// user can define non-initted and non-outside variables in the constructor
|
||||||
Registry.render(elIncarnate)
|
Registry.render(elIncarnate)
|
||||||
return elIncarnate
|
return elIncarnate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.register = Registry.register
|
|
||||||
window.rendering = []
|
/* DEFAULT WRAPPERS */
|
||||||
|
|
||||||
|
window.a = function a({ href, name=href } = {}) {
|
||||||
|
let link = document.createElement("a")
|
||||||
|
link.setAttribute('href', href);
|
||||||
|
link.innerText = name
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
window.img = function img({width="", height="", src=""}) {
|
||||||
|
let image = new Image()
|
||||||
|
if(width) image.style.width = width
|
||||||
|
if(height) image.style.height = height
|
||||||
|
if(src) image.src = src
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
window.p = function p(innerText) {
|
||||||
|
let para = document.createElement("p")
|
||||||
|
para.innerText = innerText
|
||||||
|
return para
|
||||||
|
}
|
||||||
|
|
||||||
|
window.div = function (innerText) {
|
||||||
|
let div = document.createElement("div")
|
||||||
|
div.innerText = innerText
|
||||||
|
return div
|
||||||
|
}
|
||||||
|
|
||||||
|
window.span = function (innerText) {
|
||||||
|
let span = document.createElement("span")
|
||||||
|
span.innerText = innerText
|
||||||
|
return span
|
||||||
|
}
|
||||||
|
|
||||||
/* PROTOTYPE FUNCTIONS */
|
/* PROTOTYPE FUNCTIONS */
|
||||||
|
|
||||||
HTMLElement.prototype.addAttribute = function(name) {
|
HTMLElement.prototype.addAttribute = function(name) {
|
||||||
this.setAttribute(name, "")
|
this.setAttribute(name, "")
|
||||||
}
|
}
|
||||||
@@ -340,108 +476,5 @@ HTMLElement.prototype.onClick = function(func) {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DEFAULT WRAPPERS */
|
window.register = Registry.register
|
||||||
|
window.rendering = []
|
||||||
window.a = function a({ href, name=href } = {}) {
|
|
||||||
let link = document.createElement("a")
|
|
||||||
link.setAttribute('href', href);
|
|
||||||
link.innerText = name
|
|
||||||
return link
|
|
||||||
}
|
|
||||||
|
|
||||||
window.img = function img({width="", height="", src=""}) {
|
|
||||||
let image = new Image()
|
|
||||||
if(width) image.style.width = width
|
|
||||||
if(height) image.style.height = height
|
|
||||||
if(src) image.src = src
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
|
|
||||||
window.p = function p(innerText) {
|
|
||||||
let para = document.createElement("p")
|
|
||||||
para.innerText = innerText
|
|
||||||
return para
|
|
||||||
}
|
|
||||||
|
|
||||||
window.div = function (innerText) {
|
|
||||||
let div = document.createElement("div")
|
|
||||||
div.innerText = innerText
|
|
||||||
return div
|
|
||||||
}
|
|
||||||
|
|
||||||
window.span = function (innerText) {
|
|
||||||
let span = document.createElement("span")
|
|
||||||
span.innerText = innerText
|
|
||||||
return span
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STRING TRANSLATORS */
|
|
||||||
|
|
||||||
window.css = function css(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("{")).trim();
|
|
||||||
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(htmlString) {
|
|
||||||
let container = document.createElement('div');
|
|
||||||
container.innerHTML = htmlString;
|
|
||||||
|
|
||||||
// If there's only one child, return it directly
|
|
||||||
if (container.children.length === 1) {
|
|
||||||
return container.children[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are multiple children, use a DocumentFragment
|
|
||||||
let fragment = document.createDocumentFragment();
|
|
||||||
while (container.firstChild) {
|
|
||||||
fragment.appendChild(container.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fragment;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* COMPATIBILITY */
|
|
||||||
|
|
||||||
function detectMobile() {
|
|
||||||
const mobileDeviceRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
||||||
return mobileDeviceRegex.test(navigator.userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSafariVersion() {
|
|
||||||
const userAgent = navigator.userAgent;
|
|
||||||
const isSafari = userAgent.includes("Safari") && !userAgent.includes("Chrome");
|
|
||||||
if (isSafari) {
|
|
||||||
const safariVersionMatch = userAgent.match(/Version\/(\d+\.\d+)/);
|
|
||||||
const safariVersion = safariVersionMatch ? parseFloat(safariVersionMatch[1]) : null;
|
|
||||||
return safariVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CONSOLE */
|
|
||||||
|
|
||||||
console.red = function(message) {
|
|
||||||
this.log(`%c${message}`, "color: rgb(254, 79, 42);");
|
|
||||||
};
|
|
||||||
|
|
||||||
console.green = function(message) {
|
|
||||||
this.log(`%c${message}`, "color: rgb(79, 254, 42);");
|
|
||||||
|
|
||||||
}
|
|
||||||
11
todo.txt
Normal file
11
todo.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Constructor Parameters
|
||||||
|
|
||||||
|
In swift, can choose to pass in parameters from outside or not
|
||||||
|
This works because of labels and ability to override based on parameters
|
||||||
|
|
||||||
|
In JS, how would I init state from within but pass in another parameter?
|
||||||
|
|
||||||
|
Refresh in Subpages
|
||||||
|
|
||||||
|
The hosting service needs to redirect all pages to the main index.html
|
||||||
|
This is the problem with Forum - you navigate, it's fine, refresh, goes away
|
||||||
Reference in New Issue
Block a user