diff --git a/UI/_/code/quill.js b/UI/_/code/quill.js deleted file mode 100644 index fcebb29..0000000 --- a/UI/_/code/quill.js +++ /dev/null @@ -1,1047 +0,0 @@ -/* - Sam Russell - Captured Sun - 11.25.25.1 - Added minHeight and minWidth to be counted as numerical styles - 11.25.25 - Added onChange for check boxes, added setQuery / onQueryChanged for easy filtering - 11.24.25 - Fixing onClick because it was reversed, adding event to onHover params - 11.23.25 - Added onSubmit() event for form submission, added marginHorizontal() and marginVertical() - 11.20.25 - Added "pct" style unit, added alignVertical and alignHorizontal for flex boxes - 11.19.25 - Allowing for "auto" values in otherwise numeric styles, adding vmin and vmax units - 11.17.25.3 - Adding styles() and fixing dynamic function from earlier - 11.17.25.2 - Fixing onNavigate() and onAppear() - 11.17.25 - Added dynamic function to have units in style func parameters. - 11.14.25 - Added onTouch, onTap. Changed style setters to work with Safari. Added center() funcs. - 11.13.25 - changed onFocus() to be a boolean event, added onInput() - 11.9.25 - changed p(innerText) to p(innerHTML), adjusted onNavigate to work for multiple elements and with correct "this" scope - 11.7.25 - changed registerShadow() to register(), changed onClick() to be like onHover() - 11.6.25 - adding default value for "button()" "children" parameter - 10.29.25 - adding "gap()" and "label()" functions -*/ - -/* $ NAVIGATION */ -let oldPushState = history.pushState; -history.pushState = function pushState() { - let ret = oldPushState.apply(this, arguments); - window.dispatchEvent(new Event('pushstate')); - window.dispatchEvent(new Event('navigate')); - return ret; -}; - -window.addEventListener('popstate', () => { - window.dispatchEvent(new Event('navigate')); -}); - -window.setQuery = function(key, value) { - const url = new URL(window.location.href); - const params = url.searchParams; - - if (value === null || value === undefined) { - params.delete(key); - } else { - params.set(key, value); - } - - const newUrl = url.toString(); - history.replaceState(null, "", newUrl); - window.dispatchEvent(new Event('query-changed')); - - return newUrl; -}; - -window.navigateTo = function(url) { - window.dispatchEvent(new Event('navigate')); - window.history.pushState({}, '', url); -} - -/* $ SELECTOR */ - -HTMLElement.prototype.$ = function(selector) { - return window.$(selector, this) -} -DocumentFragment.prototype.$ = function(selector) { - return window.$(selector, this) -} -window.$ = function(selector, el = document) { - return el.querySelector(selector) -} - -window.$$ = function(selector, el = document) { - return Array.from(el.querySelectorAll(selector)) -} - -/* 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);"); - -} - -/* GET CSS VARIABLES FOR DARK OR LIGHT MODE */ -window.darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; -document.documentElement.classList.add(darkMode ? 'dark' : 'light'); - -window.getColor = function(name) { - const rootStyles = getComputedStyle(document.documentElement); - const color = rootStyles.getPropertyValue(`--${name}`).trim(); - if(!color) { - throw new Error("Color not found") - } - return color -} - -/* MOBILE */ - -window.isMobile = function isMobile() { - return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent); -} - -window.css = function css(cssString) { - let container = document.querySelector("style#pageStyle"); - if(!container) { - container = document.createElement('style'); - container.id = "pageStyle"; - 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(elementString) { - let parser = new DOMParser(); - let doc = parser.parseFromString(elementString, 'text/html'); - return doc.body.firstChild; -} - -window.util = {} -window.util.observeClassChange = (el, callback) => { - if (!el || !(el instanceof Element)) { - throw new Error("observeClassChange requires a valid DOM element."); - } - - const observer = new MutationObserver((mutations) => { - for (const mutation of mutations) { - if (mutation.type === "attributes" && mutation.attributeName === "class") { - callback(el.classList); - } - } - }); - - observer.observe(el, { - attributes: true, - attributeFilter: ["class"] - }); - - return observer; // Optional: return it so you can disconnect later -} - -/* PAGE SETUP */ - -Object.defineProperty(Array.prototype, 'last', { - get() { - return this[this.length - 1]; - }, - enumerable: false, -}); - -/* QUILL */ - -window.quill = { - rendering: [], - - render: (el) => { - if(el instanceof Shadow) { - let parent = quill.rendering[quill.rendering.length-1] - if(!parent) { - parent = document.body - } - parent.appendChild(el) - } else { - if(!el.render) {el.render = () => {}} - let parent = quill.rendering[quill.rendering.length-1] - if(!parent) throw new Error("Quill: no parent for element") - parent.appendChild(el) - } - - quill.rendering.push(el) - el.render() - quill.rendering.pop(el) - }, - - rerender: (el) => { - Array.from(el.attributes).forEach(attr => el.removeAttribute(attr.name)); - el.innerHTML = "" - el.removeAllListeners() - - quill.rendering.push(el) - el.render() - quill.rendering.pop() - }, - - loadPage: () => { - let URL = window.location.pathname - if(!window.routes[URL]) { - throw new Error("No URL for this route: ", URL) - } - - let pageClass = window[routes[URL]] - document.title = pageClass.title ?? document.title - document.body.innerHTML = "" - let page = new pageClass() - quill.render(page) - }, - - isStack: (el) => { - return el.classList.contains("HStack") || el.classList.contains("ZStack") || el.classList.contains("VStack") - }, -} - -window.Shadow = class extends HTMLElement { - constructor() { - super() - } -} -window.register = (el, tagname) => { - if (typeof el.prototype.render !== 'function') { - throw new Error("Element must have a render: " + el.prototype.constructor.name) - } - if(!tagname) { - tagname = el.prototype.constructor.name.toLowerCase() + "-" - } - customElements.define(tagname, el) - if(el.css) { - css(el.css) - } - - window[el.prototype.constructor.name] = function (...params) { - let instance = new el(...params) - quill.render(instance) - return instance - } -} - -HTMLElement.prototype.rerender = function() { - quill.rerender(this) -} - -/* Styling */ - -window.pct = "%" -window.vmin = "vmin" -window.vmax = "vmax" -window.vh = "vh" -window.vw = "vw" -window.px = "px" -window.em = "em" -window.rem = "rem" -window.inches = "in" - -HTMLElement.prototype.addStyle = function(func) { - return func(this) -} - -window.css = function css(cssString) { - let container = document.querySelector("style#pageStyle"); - if(!container) { - container = document.createElement('style'); - container.id = "pageStyle"; - 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 - } -} - -function extendHTMLElementWithStyleSetters() { - - function cssValueType(prop) { - const div = document.createElement("div"); - const style = div.style; - if (!(prop in style)) return "invalid"; - - switch(prop) { - - case "gap": - case "borderRadius": - case "width": - case "height": - case "maxWidth": - case "maxHeight": - case "minWidth": - case "minHeight": - - case "left": - case "top": - case "bottom": - case "right": - - case "padding": - case "paddingLeft": - case "paddingTop": - case "paddingBottom": - case "paddingRight": - - case "margin": - case "marginLeft": - case "marginTop": - case "marginBottom": - case "marginRight": - - case "textUnderlineOffset": - - return "unit-number" - - default: - - return "string" - } - - } - - let allStyleProps = ["accentColor", "additiveSymbols", "alignContent", "alignItems", "alignSelf", "alignmentBaseline", "all", "anchorName", "anchorScope", "animation", "animationComposition", "animationDelay", "animationDirection", "animationDuration", "animationFillMode", "animationIterationCount", "animationName", "animationPlayState", "animationRange", "animationRangeEnd", "animationRangeStart", "animationTimeline", "animationTimingFunction", "appRegion", "appearance", "ascentOverride", "aspectRatio", "backdropFilter", "backfaceVisibility", "background", "backgroundAttachment", "backgroundBlendMode", "backgroundClip", "backgroundColor", "backgroundImage", "backgroundOrigin", "backgroundPosition", "backgroundPositionX", "backgroundPositionY", "backgroundRepeat", "backgroundSize", "basePalette", "baselineShift", "baselineSource", "blockSize", "border", "borderBlock", "borderBlockColor", "borderBlockEnd", "borderBlockEndColor", "borderBlockEndStyle", "borderBlockEndWidth", "borderBlockStart", "borderBlockStartColor", "borderBlockStartStyle", "borderBlockStartWidth", "borderBlockStyle", "borderBlockWidth", "borderBottom", "borderBottomColor", "borderBottomLeftRadius", "borderBottomRightRadius", "borderBottomStyle", "borderBottomWidth", "borderCollapse", "borderColor", "borderEndEndRadius", "borderEndStartRadius", "borderImage", "borderImageOutset", "borderImageRepeat", "borderImageSlice", "borderImageSource", "borderImageWidth", "borderInline", "borderInlineColor", "borderInlineEnd", "borderInlineEndColor", "borderInlineEndStyle", "borderInlineEndWidth", "borderInlineStart", "borderInlineStartColor", "borderInlineStartStyle", "borderInlineStartWidth", "borderInlineStyle", "borderInlineWidth", "borderLeft", "borderLeftColor", "borderLeftStyle", "borderLeftWidth", "borderRadius", "borderRight", "borderRightColor", "borderRightStyle", "borderRightWidth", "borderSpacing", "borderStartEndRadius", "borderStartStartRadius", "borderStyle", "borderTop", "borderTopColor", "borderTopLeftRadius", "borderTopRightRadius", "borderTopStyle", "borderTopWidth", "borderWidth", "bottom", "boxDecorationBreak", "boxShadow", "boxSizing", "breakAfter", "breakBefore", "breakInside", "bufferedRendering", "captionSide", "caretAnimation", "caretColor", "clear", "clip", "clipPath", "clipRule", "color", "colorInterpolation", "colorInterpolationFilters", "colorRendering", "colorScheme", "columnCount", "columnFill", "columnGap", "columnRule", "columnRuleColor", "columnRuleStyle", "columnRuleWidth", "columnSpan", "columnWidth", "columns", "contain", "containIntrinsicBlockSize", "containIntrinsicHeight", "containIntrinsicInlineSize", "containIntrinsicSize", "containIntrinsicWidth", "container", "containerName", "containerType", "content", "contentVisibility", "cornerBlockEndShape", "cornerBlockStartShape", "cornerBottomLeftShape", "cornerBottomRightShape", "cornerBottomShape", "cornerEndEndShape", "cornerEndStartShape", "cornerInlineEndShape", "cornerInlineStartShape", "cornerLeftShape", "cornerRightShape", "cornerShape", "cornerStartEndShape", "cornerStartStartShape", "cornerTopLeftShape", "cornerTopRightShape", "cornerTopShape", "counterIncrement", "counterReset", "counterSet", "cursor", "cx", "cy", "d", "descentOverride", "direction", "display", "dominantBaseline", "dynamicRangeLimit", "emptyCells", "fallback", "fieldSizing", "fill", "fillOpacity", "fillRule", "filter", "flex", "flexBasis", "flexDirection", "flexFlow", "flexGrow", "flexShrink", "flexWrap", "float", "floodColor", "floodOpacity", "font", "fontDisplay", "fontFamily", "fontFeatureSettings", "fontKerning", "fontOpticalSizing", "fontPalette", "fontSize", "fontSizeAdjust", "fontStretch", "fontStyle", "fontSynthesis", "fontSynthesisSmallCaps", "fontSynthesisStyle", "fontSynthesisWeight", "fontVariant", "fontVariantAlternates", "fontVariantCaps", "fontVariantEastAsian", "fontVariantEmoji", "fontVariantLigatures", "fontVariantNumeric", "fontVariantPosition", "fontVariationSettings", "fontWeight", "forcedColorAdjust", "gap", "grid", "gridArea", "gridAutoColumns", "gridAutoFlow", "gridAutoRows", "gridColumn", "gridColumnEnd", "gridColumnGap", "gridColumnStart", "gridGap", "gridRow", "gridRowEnd", "gridRowGap", "gridRowStart", "gridTemplate", "gridTemplateAreas", "gridTemplateColumns", "gridTemplateRows", "height", "hyphenateCharacter", "hyphenateLimitChars", "hyphens", "imageOrientation", "imageRendering", "inherits", "initialLetter", "initialValue", "inlineSize", "inset", "insetBlock", "insetBlockEnd", "insetBlockStart", "insetInline", "insetInlineEnd", "insetInlineStart", "interactivity", "interpolateSize", "isolation", "justifyContent", "justifyItems", "justifySelf", "left", "letterSpacing", "lightingColor", "lineBreak", "lineGapOverride", "lineHeight", "listStyle", "listStyleImage", "listStylePosition", "listStyleType", "margin", "marginBlock", "marginBlockEnd", "marginBlockStart", "marginBottom", "marginInline", "marginInlineEnd", "marginInlineStart", "marginLeft", "marginRight", "marginTop", "marker", "markerEnd", "markerMid", "markerStart", "mask", "maskClip", "maskComposite", "maskImage", "maskMode", "maskOrigin", "maskPosition", "maskRepeat", "maskSize", "maskType", "mathDepth", "mathShift", "mathStyle", "maxBlockSize", "maxHeight", "maxInlineSize", "maxWidth", "minBlockSize", "minHeight", "minInlineSize", "minWidth", "mixBlendMode", "navigation", "negative", "objectFit", "objectPosition", "objectViewBox", "offset", "offsetAnchor", "offsetDistance", "offsetPath", "offsetPosition", "offsetRotate", "opacity", "order", "orphans", "outline", "outlineColor", "outlineOffset", "outlineStyle", "outlineWidth", "overflow", "overflowAnchor", "overflowBlock", "overflowClipMargin", "overflowInline", "overflowWrap", "overflowX", "overflowY", "overlay", "overrideColors", "overscrollBehavior", "overscrollBehaviorBlock", "overscrollBehaviorInline", "overscrollBehaviorX", "overscrollBehaviorY", "pad", "padding", "paddingBlock", "paddingBlockEnd", "paddingBlockStart", "paddingBottom", "paddingInline", "paddingInlineEnd", "paddingInlineStart", "paddingLeft", "paddingRight", "paddingTop", "page", "pageBreakAfter", "pageBreakBefore", "pageBreakInside", "pageOrientation", "paintOrder", "perspective", "perspectiveOrigin", "placeContent", "placeItems", "placeSelf", "pointerEvents", "position", "positionAnchor", "positionArea", "positionTry", "positionTryFallbacks", "positionTryOrder", "positionVisibility", "prefix", "printColorAdjust", "quotes", "r", "range", "readingFlow", "readingOrder", "resize", "result", "right", "rotate", "rowGap", "rubyAlign", "rubyPosition", "rx", "ry", "scale", "scrollBehavior", "scrollInitialTarget", "scrollMargin", "scrollMarginBlock", "scrollMarginBlockEnd", "scrollMarginBlockStart", "scrollMarginBottom", "scrollMarginInline", "scrollMarginInlineEnd", "scrollMarginInlineStart", "scrollMarginLeft", "scrollMarginRight", "scrollMarginTop", "scrollMarkerGroup", "scrollPadding", "scrollPaddingBlock", "scrollPaddingBlockEnd", "scrollPaddingBlockStart", "scrollPaddingBottom", "scrollPaddingInline", "scrollPaddingInlineEnd", "scrollPaddingInlineStart", "scrollPaddingLeft", "scrollPaddingRight", "scrollPaddingTop", "scrollSnapAlign", "scrollSnapStop", "scrollSnapType", "scrollTargetGroup", "scrollTimeline", "scrollTimelineAxis", "scrollTimelineName", "scrollbarColor", "scrollbarGutter", "scrollbarWidth", "shapeImageThreshold", "shapeMargin", "shapeOutside", "shapeRendering", "size", "sizeAdjust", "speak", "speakAs", "src", "stopColor", "stopOpacity", "stroke", "strokeDasharray", "strokeDashoffset", "strokeLinecap", "strokeLinejoin", "strokeMiterlimit", "strokeOpacity", "strokeWidth", "suffix", "symbols", "syntax", "system", "tabSize", "tableLayout", "textAlign", "textAlignLast", "textAnchor", "textAutospace", "textBox", "textBoxEdge", "textBoxTrim", "textCombineUpright", "textDecoration", "textDecorationColor", "textDecorationLine", "textDecorationSkipInk", "textDecorationStyle", "textDecorationThickness", "textEmphasis", "textEmphasisColor", "textEmphasisPosition", "textEmphasisStyle", "textIndent", "textOrientation", "textOverflow", "textRendering", "textShadow", "textSizeAdjust", "textSpacingTrim", "textTransform", "textUnderlineOffset", "textUnderlinePosition", "textWrap", "textWrapMode", "textWrapStyle", "timelineScope", "top", "touchAction", "transform", "transformBox", "transformOrigin", "transformStyle", "transition", "transitionBehavior", "transitionDelay", "transitionDuration", "transitionProperty", "transitionTimingFunction", "translate", "types", "unicodeBidi", "unicodeRange", "userSelect", "vectorEffect", "verticalAlign", "viewTimeline", "viewTimelineAxis", "viewTimelineInset", "viewTimelineName", "viewTransitionClass", "viewTransitionGroup", "viewTransitionName", "visibility", "webkitAlignContent", "webkitAlignItems", "webkitAlignSelf", "webkitAnimation", "webkitAnimationDelay", "webkitAnimationDirection", "webkitAnimationDuration", "webkitAnimationFillMode", "webkitAnimationIterationCount", "webkitAnimationName", "webkitAnimationPlayState", "webkitAnimationTimingFunction", "webkitAppRegion", "webkitAppearance", "webkitBackfaceVisibility", "webkitBackgroundClip", "webkitBackgroundOrigin", "webkitBackgroundSize", "webkitBorderAfter", "webkitBorderAfterColor", "webkitBorderAfterStyle", "webkitBorderAfterWidth", "webkitBorderBefore", "webkitBorderBeforeColor", "webkitBorderBeforeStyle", "webkitBorderBeforeWidth", "webkitBorderBottomLeftRadius", "webkitBorderBottomRightRadius", "webkitBorderEnd", "webkitBorderEndColor", "webkitBorderEndStyle", "webkitBorderEndWidth", "webkitBorderHorizontalSpacing", "webkitBorderImage", "webkitBorderRadius", "webkitBorderStart", "webkitBorderStartColor", "webkitBorderStartStyle", "webkitBorderStartWidth", "webkitBorderTopLeftRadius", "webkitBorderTopRightRadius", "webkitBorderVerticalSpacing", "webkitBoxAlign", "webkitBoxDecorationBreak", "webkitBoxDirection", "webkitBoxFlex", "webkitBoxOrdinalGroup", "webkitBoxOrient", "webkitBoxPack", "webkitBoxReflect", "webkitBoxShadow", "webkitBoxSizing", "webkitClipPath", "webkitColumnBreakAfter", "webkitColumnBreakBefore", "webkitColumnBreakInside", "webkitColumnCount", "webkitColumnGap", "webkitColumnRule", "webkitColumnRuleColor", "webkitColumnRuleStyle", "webkitColumnRuleWidth", "webkitColumnSpan", "webkitColumnWidth", "webkitColumns", "webkitFilter", "webkitFlex", "webkitFlexBasis", "webkitFlexDirection", "webkitFlexFlow", "webkitFlexGrow", "webkitFlexShrink", "webkitFlexWrap", "webkitFontFeatureSettings", "webkitFontSmoothing", "webkitHyphenateCharacter", "webkitJustifyContent", "webkitLineBreak", "webkitLineClamp", "webkitLocale", "webkitLogicalHeight", "webkitLogicalWidth", "webkitMarginAfter", "webkitMarginBefore", "webkitMarginEnd", "webkitMarginStart", "webkitMask", "webkitMaskBoxImage", "webkitMaskBoxImageOutset", "webkitMaskBoxImageRepeat", "webkitMaskBoxImageSlice", "webkitMaskBoxImageSource", "webkitMaskBoxImageWidth", "webkitMaskClip", "webkitMaskComposite", "webkitMaskImage", "webkitMaskOrigin", "webkitMaskPosition", "webkitMaskPositionX", "webkitMaskPositionY", "webkitMaskRepeat", "webkitMaskSize", "webkitMaxLogicalHeight", "webkitMaxLogicalWidth", "webkitMinLogicalHeight", "webkitMinLogicalWidth", "webkitOpacity", "webkitOrder", "webkitPaddingAfter", "webkitPaddingBefore", "webkitPaddingEnd", "webkitPaddingStart", "webkitPerspective", "webkitPerspectiveOrigin", "webkitPerspectiveOriginX", "webkitPerspectiveOriginY", "webkitPrintColorAdjust", "webkitRtlOrdering", "webkitRubyPosition", "webkitShapeImageThreshold", "webkitShapeMargin", "webkitShapeOutside", "webkitTapHighlightColor", "webkitTextCombine", "webkitTextDecorationsInEffect", "webkitTextEmphasis", "webkitTextEmphasisColor", "webkitTextEmphasisPosition", "webkitTextEmphasisStyle", "webkitTextFillColor", "webkitTextOrientation", "webkitTextSecurity", "webkitTextSizeAdjust", "webkitTextStroke", "webkitTextStrokeColor", "webkitTextStrokeWidth", "webkitTransform", "webkitTransformOrigin", "webkitTransformOriginX", "webkitTransformOriginY", "webkitTransformOriginZ", "webkitTransformStyle", "webkitTransition", "webkitTransitionDelay", "webkitTransitionDuration", "webkitTransitionProperty", "webkitTransitionTimingFunction", "webkitUserDrag", "webkitUserModify", "webkitUserSelect", "webkitWritingMode", "whiteSpace", "whiteSpaceCollapse", "widows", "width", "willChange", "wordBreak", "wordSpacing", "wordWrap", "writingMode", "x", "y", "zIndex", "zoom"] - - allStyleProps.forEach(prop => { - if (prop === "translate") return; - - const type = cssValueType(prop); - - switch (type) { - case "unit-number": - HTMLElement.prototype[prop] = function(value, unit = "px") { - if ((typeof value !== "number" || isNaN(value)) && value !== "auto") { - throw new Error(`Invalid value for ${prop}: ${value}. Expected a number.`); - } - if(value === "auto") { - this.style[prop] = value - return this - } - this.style[prop] = value + unit; - return this; - }; - break; - - case "string": - HTMLElement.prototype[prop] = function(value) { - this.style[prop] = value; - return this; - }; - break; - } - }); -} - -extendHTMLElementWithStyleSetters(); - -HTMLElement.prototype.styles = function(cb) { - cb.call(this, this) - return this -} - -HTMLElement.prototype.paddingVertical = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.paddingTop = value + unit - this.style.paddingBottom = value + unit - return this -} - -HTMLElement.prototype.paddingHorizontal = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.paddingRight = value + unit - this.style.paddingLeft = value + unit - return this -} - -HTMLElement.prototype.marginVertical = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.marginTop = value + unit - this.style.marginBottom = value + unit - return this -} - -HTMLElement.prototype.marginHorizontal = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.marginRight = value + unit - this.style.marginLeft = value + unit - return this -} - -HTMLElement.prototype.fontSize = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - - switch(value) { - case "6xl": - value = "3.75"; unit = "rem" - break; - - case "5xl": - value = "3"; unit = "rem" - break; - - case "4xl": - value = "2.25"; unit = "rem" - break; - - case "3xl": - value = "1.875"; unit = "rem" - break; - - case "2xl": - value = "1.5"; unit = "rem" - break; - - case "xl": - value = "1.25"; unit = "rem" - break; - - case "l": - value = "1.125"; unit = "rem" - break; - - case "s": - value = "0.875"; unit = "rem" - break; - - case "xs": - value = "0.75"; unit = "rem" - break; - - default: - break; - } - this.style.fontSize = value + unit - return this -} - -function checkPositionType(el) { - let computed = window.getComputedStyle(el).position - if(!(computed === "absolute" || computed === "fixed")) { - el.style.position = "absolute" - } -} - -HTMLElement.prototype.x = function(value, unit = "px") { - if (typeof value !== 'number' || isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - checkPositionType(this) - this.style.left = value + unit - return this -} - -HTMLElement.prototype.y = function(value, unit = "px") { - if (typeof value !== 'number' || isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - checkPositionType(this) - this.style.top = value + unit - return this -} - -HTMLElement.prototype.xRight = function(value, unit = "px") { - if (typeof value !== 'number' || isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - checkPositionType(this) - this.style.right = value + unit - return this -} - -HTMLElement.prototype.yBottom = function(value, unit = "px") { - if (typeof value !== 'number' || isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - checkPositionType(this) - this.style.bottom = value + unit - return this -} - -HTMLElement.prototype.backgroundImage = function (...values) { - const formatted = values - .map(v => { - if(v.includes("/") && !v.includes("gradient")) { - v = "url(" + v + ")" - } - return String(v).trim(); - }) - .join(", "); - - this.style.backgroundImage = formatted; - return this; -}; - -HTMLElement.prototype.center = function () { - this.style.transform = "translate(-50%, -50%)" - return this; -}; - -HTMLElement.prototype.centerX = function () { - this.style.transform = "translateX(-50%)" - return this; -}; - -HTMLElement.prototype.centerY = function () { - this.style.transform = "translateY(-50%)" - return this; -}; - -HTMLElement.prototype.alignVertical = function (value) { - const direction = getComputedStyle(this).flexDirection; - if(!direction) { - throw new Error("alignVertical can be only be used on HStacks or VStacks!") - } - - if (direction === "column" || direction === "column-reverse") { - this.style.justifyContent = value; - } else { - this.style.alignItems = value; - } - return this -} - -HTMLElement.prototype.alignHorizontal = function (value) { - const direction = getComputedStyle(this).flexDirection; - if(!direction) { - throw new Error("alignHorizontal can be only be used on HStacks or VStacks!") - } - - if (direction === "column" || direction === "column-reverse") { - this.style.alignItems = value; - } else { - this.style.justifyContent = value; - } - return this -} - - -/* Elements */ - -quill.setChildren = function(el, innerContent) { - if(typeof innerContent === "string") { - el.innerText = innerContent - } else if(typeof innerContent === "function") { - el.render = innerContent - } else { - throw new Error("Children of unknown type") - } -} - -window.a = function a( href, inner=href ) { - if(!href) throw new Error("quill a: missing href argument. Function: a( href, inner=href )") - let link = document.createElement("a") - link.setAttribute('href', href); - quill.setChildren(link, inner) - quill.render(link) - return link -} - -window.img = function img(src, width="", height="") { - let image = document.createElement("img") - - if(!src || !(typeof src==="string")) { - throw new Error("img: missing first argument: src | String") - } else { - image.src = src - } - if(width && typeof width === "string") { - image.style.width = width - } else if(width) { - image.style.width = width + "px" - } - if(height && typeof height === "string") { - image.style.height = height - } else if(height) { - image.style.height = height + "px" - } - quill.render(image) - return image -} - -HTMLImageElement.prototype.backgroundColor = function(value) { - if (this.src.endsWith('.svg') || this.src.startsWith('data:image/svg+xml')) { - fetch(this.src).then(response => response.text()) - .then(svgText => { - const modifiedSvg = svgText.replace(/\bfill="[^"]*"/g, `fill="${value}"`); - const blob = new Blob([modifiedSvg], { type: 'image/svg+xml' }); - this.src = URL.createObjectURL(blob); - }).catch(error => { - console.error('Error updating SVG fill:', error); - }); - } else { - this.style.backgroundColor = value; - } - - return this; // Always returns the element itself - }; - -window.p = function p(innerHTML) { - let el = document.createElement("p") - if(typeof innerText === "function") { - el.render = innerHTML - } else { - el.innerHTML = innerHTML - } - el.style.margin = "0"; - quill.render(el) - return el -} - -window.h1 = function h1(innerText) { - let el = document.createElement("h1") - el.innerText = innerText - quill.render(el) - return el -} - -window.h2 = function h2(innerText) { - let el = document.createElement("h2") - el.innerText = innerText - quill.render(el) - return el -} - -window.h3 = function h3(innerText) { - let el = document.createElement("h3") - el.innerText = innerText - quill.render(el) - return el -} - -window.div = function (innerText) { - let el = document.createElement("div") - el.innerText = innerText ?? "" - quill.render(el) - return el -} - -window.span = function (innerText) { - let el = document.createElement("span") - el.innerText = innerText - quill.render(el) - return el -} - -window.button = function (children = "") { - let el = document.createElement("button") - quill.setChildren(el, children) - quill.render(el) - return el -} - -window.form = function(cb) { - let el = document.createElement("form") - el.render = cb - quill.render(el) - return el -} - -window.input = function(placeholder, width, height) { - let el = document.createElement("input") - el.placeholder = placeholder - el.style.width = width - el.style.height = height - quill.render(el) - return el -} - -window.label = function(text) { - let el = document.createElement("label") - el.innerText = text - quill.render(el) - return el -} - -window.textarea = function(placeholder) { - let el = document.createElement("textarea") - el.placeholder = placeholder - quill.render(el) - return el -} - - -/* STACKS */ - -window.VStack = function (cb = () => {}) { - let styles = ` - display: flex; - flex-direction: column; - ` - let nowRendering = quill.rendering[quill.rendering.length-1] - if (nowRendering.innerHTML.trim() === "" && !quill.isStack(nowRendering)) { - nowRendering.style.cssText += styles - nowRendering.classList.add("VStack") - cb() - return nowRendering - } - - let div = document.createElement("div") - div.classList.add("VStack") - div.style.cssText += styles - div.render = cb - quill.render(div) - return div -} - -window.HStack = function (cb = () => {}) { - let styles = ` - display: flex; - flex-direction: row; - `; - let nowRendering = quill.rendering[quill.rendering.length - 1]; - if (nowRendering.innerHTML.trim() === "" && !quill.isStack(nowRendering)) { - nowRendering.style.cssText += styles; - nowRendering.classList.add("HStack") - cb(); - return nowRendering; - } - - let div = document.createElement("div"); - div.classList.add("HStack"); - div.style.cssText += styles; - div.render = cb; - quill.render(div); - return div; -}; - -window.ZStack = function (cb = () => {}) { - let nowRendering = quill.rendering[quill.rendering.length - 1]; - if (nowRendering.innerHTML.trim() === "" && !quill.isStack(nowRendering)) { - nowRendering.classList.add("ZStack") - cb(); - return nowRendering; - } - - let div = document.createElement("div"); - div.classList.add("ZStack"); - div.render = cb; - quill.render(div); - return div; -}; - -/* SHAPES */ - -window.svgMethods = function(svg) { - svg.pulse = function (duration = 600) { - this.style.transition = `transform ${duration}ms ease-in-out` - this.style.transform = "scale(1.2)" - setTimeout(() => { - this.style.transform = "scale(1)" - }, duration / 2) - return this - } - - // Rotate (e.g. loading spinner) - svg.rotate = function (degrees = 360, duration = 1000) { - this.style.transition = `transform ${duration}ms linear` - this.style.transform = `rotate(${degrees}deg)` - return this - } - - // Change color - svg.fill = function (color) { - this.setAttribute("fill", color) - return this - } - - svg.height = function (height) { - this.setAttribute("height", height) - return this - } - - svg.width = function (width) { - this.setAttribute("width", width) - return this - } - - svg.stroke = function (width, color) { - this.setAttribute("stroke", color) - this.setAttribute("stroke-width", width) - return this - } - - // Toggle visibility - svg.toggle = function () { - this.style.display = this.style.display === "none" ? "" : "none" - return this - } -} - -window.Rectangle = function (width = "40px", height = "40px") { - const svgNS = "http://www.w3.org/2000/svg"; - const svgEl = document.createElementNS(svgNS, "svg"); - const rectEl = document.createElementNS(svgNS, "rect"); - - // SVG size - svgEl.setAttribute("width", width); - svgEl.setAttribute("height", height); - svgEl.setAttribute("viewBox", "0 0 100 100"); - svgEl.setAttribute("preserveAspectRatio", "xMidYMid meet"); - - // Rectangle: full size, slightly rounded corners - rectEl.setAttribute("x", "15"); // 15% margin from edges - rectEl.setAttribute("y", "15"); - rectEl.setAttribute("width", "70"); // 70% of viewBox - rectEl.setAttribute("height", "70"); - // rectEl.setAttribute("rx", "8"); // rounded corners (optional) - // rectEl.setAttribute("ry", "8"); - - svgEl.appendChild(rectEl); - svgMethods(svgEl); // assuming you have this - quill.render(svgEl); - return svgEl; -} - -window.Triangle = function (width = "40px", height = "40px") { - const svgNS = "http://www.w3.org/2000/svg" - const svgEl = document.createElementNS(svgNS, "svg") - const pathEl = document.createElementNS(svgNS, "path") - - // SVG size - svgEl.setAttribute("width", width) - svgEl.setAttribute("height", height) - svgEl.setAttribute("viewBox", "0 0 100 100") - svgEl.setAttribute("preserveAspectRatio", "xMidYMid meet") - // Right-pointing triangle (Play icon) - pathEl.setAttribute("d", "M 25 15 L 90 50 L 25 85 Z") // ◄ adjust points if needed - - svgEl.appendChild(pathEl) - svgMethods(svgEl) - quill.render(svgEl) - return svgEl -} - - -/* EVENTS */ - -HTMLElement.prototype.onAppear = function(func) { - func.call(this); - return this; -}; - -HTMLElement.prototype.onClick = function(func) { - const onMouseDown = () => func.call(this, false); - const onMouseUp = () => func.call(this, true); - this._storeListener("mousedown", onMouseDown); - this._storeListener("mouseup", onMouseUp); - return this; -}; - -HTMLElement.prototype.onMouseDown = function(func) { - this._storeListener("mousedown", func); - return this; -}; - -HTMLElement.prototype.onMouseUp = function(func) { - this._storeListener("mouseup", func); - return this; -}; - -HTMLElement.prototype.onRightClick = function(func) { - this._storeListener("contextmenu", func); - return this; -}; - -HTMLElement.prototype.onHover = function(cb) { - const onEnter = (e) => cb.call(this, true, e); - const onLeave = (e) => cb.call(this, false, e); - this._storeListener("mouseover", onEnter); - this._storeListener("mouseleave", onLeave); - return this; -}; - -HTMLElement.prototype.onFocus = function(cb) { - if (!this.matches('input, textarea, select, button')) { - throw new Error("Can't put focus event on non-form element!"); - } - const onFocus = () => cb.call(this, true); - const onBlur = () => cb.call(this, false); - this._storeListener("focus", onFocus); - this._storeListener("blur", onBlur); - return this; -}; - -HTMLElement.prototype.onKeyDown = function(cb) { - this._storeListener("keydown", cb); - return this; -}; - -HTMLElement.prototype.onInput = function(cb) { - if(!this.matches('input, textarea, [contenteditable=""], [contenteditable="true"]')) - throw new Error("Can't put input event on non-input element!") - this._storeListener("input", cb); - return this; -}; - -HTMLElement.prototype.onChange = function(cb) { - if(!this.matches('input, textarea, [contenteditable=""], [contenteditable="true"]')) - throw new Error("Can't put input event on non-input element!") - this._storeListener("change", cb); - return this; -}; - - -HTMLElement.prototype.onSubmit = function(cb) { - if(!this.matches('form')) - throw new Error("Can't put form event on non-form element!") - this._storeListener("submit", cb); - return this; -}; - -HTMLElement.prototype.onTouch = function(cb) { - const onStart = () => cb.call(this, true); - const onEnd = () => cb.call(this, false); - const onCancel = () => cb.call(this, null); - this._storeListener("touchstart", onStart); - this._storeListener("touchend", onEnd); - this._storeListener("touchcancel", onCancel); - return this; -}; - -HTMLElement.prototype.onTap = function(cb) { - this._storeListener("touchend", cb); - return this; -}; - -/* WHY THIS LISTENER IS THE WAY IT IS: -- If we dispatch the "navigate" event on the window (as one would expect for a "navigate" event), a listener placed on the element will not pick it up. -- However, if we add the listener on the window, it won't have the "this" scope that a callback normally would. Which makes it much less useful. -- Then, if we try to add that scope using bind(), it makes the function.toString() unreadable, which means we cannot detect duplicate listeners. -- Therefore, we just have to attach the navigate event to the element, and manually trigger that when the window listener fires. -*/ -navigateListeners = [] -HTMLElement.prototype.onNavigate = function(cb) { - this._storeListener("navigate", cb); - - let found = false - for(entry of navigateListeners) { - if(entry.cb.toString() === cb.toString() && - this.nodeName === entry.el.nodeName) { - found = true - break; - } - } - if(found === false) { - navigateListeners.push({el: this, cb: cb}) - } - - return this; -}; -window.addEventListener("navigate", () => { - for(entry of navigateListeners) { - entry.el.dispatchEvent(new CustomEvent("navigate")) - } -}) - -/* -Same principle applies -*/ -queryListeners = [] -HTMLElement.prototype.onQueryChanged = function(cb) { - this._storeListener("query-changed", cb); - - let found = false - for(entry of queryListeners) { - if(entry.cb.toString() === cb.toString() && - this.nodeName === entry.el.nodeName) { - found = true - break; - } - } - if(found === false) { - queryListeners.push({el: this, cb: cb}) - } - - return this; -}; -window.addEventListener("query-changed", () => { - for(entry of queryListeners) { - entry.el.dispatchEvent(new CustomEvent("query-changed")) - } -}) - -HTMLElement.prototype.onEvent = function(name, cb) { - window._storeListener(window, name, cb); - return this; -}; - -HTMLElement.prototype._storeListener = function(type, handler, options) { - window._storeListener(this, type, handler, options) -} - -window.__listeners = [] - -function _storeListener(target, type, handler, options) { - if (!target.__listeners) target.__listeners = []; - - const optionsString = JSON.stringify(options); - - const index = target.__listeners.findIndex(listener => - listener.type === type && - listener.handler.toString() === handler.toString() && - JSON.stringify(listener.options) === optionsString - ); - - if (index === -1) { // Listener is new - target.addEventListener(type, handler, options); - target.__listeners.push({ type, handler, options }); - } else { // Listener is a duplicate, can be replaced - const old = target.__listeners[index]; - target.removeEventListener(old.type, old.handler, old.options); - - // Replace with the new one - target.addEventListener(type, handler, options); - target.__listeners[index] = { type, handler, options }; - } -} - -HTMLElement.prototype.removeAllListeners = function() { - if (!this.__listeners) return; - for (const { type, handler, options } of this.__listeners) { - this.removeEventListener(type, handler, options); - } - this.__listeners = []; - return this; -}; - -/* ATTRIBUTES */ - -HTMLElement.prototype.attr = function(attributes) { - if ( - typeof attributes !== "object" || - attributes === null || - Array.isArray(attributes) - ) { - throw new TypeError("attr() expects an object with key-value pairs"); - } - - for (const [key, value] of Object.entries(attributes)) { - this.setAttribute(key, value); - } - return this; -}; diff --git a/UI/_/icons/lightning.png b/UI/_/icons/lightning.png deleted file mode 100644 index 39ff084..0000000 Binary files a/UI/_/icons/lightning.png and /dev/null differ diff --git a/UI/_/icons/shield.png b/UI/_/icons/shield.png deleted file mode 100644 index 1602e53..0000000 Binary files a/UI/_/icons/shield.png and /dev/null differ diff --git a/UI/index.html b/UI/index.html deleted file mode 100644 index 51452c4..0000000 --- a/UI/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - Admin - - - - - - - - - \ No newline at end of file diff --git a/UI/index.js b/UI/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/http.js b/http.js new file mode 100644 index 0000000..09c9637 --- /dev/null +++ b/http.js @@ -0,0 +1,12 @@ +const http = require("http"); + +http.createServer((req, res) => { + const host = req.headers.host || ""; + console.log("working") + res.writeHead(301, { + "Location": `https://${host}${req.url}` + }); + res.end(); +}).listen(2000, () => { + console.log("🔁 Redirect server running on port 2000"); +}); \ No newline at end of file diff --git a/index.js b/index.js index 8e52dca..3f93b39 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ const http = require("http"); const https = require("https"); const tls = require("tls"); const httpProxy = require("http-proxy"); +const { WebSocketServer } = require('ws') const path = require("path"); // --------------------------- @@ -80,13 +81,10 @@ function serveProxy(req, res, port) { proxy.on("error", (err, req, res) => { console.error("Proxy error:", err.message); - if (res.headersSent) { - res.end(); - return; - } - - res.writeHead(502, { "Content-Type": "text/plain" }); - res.end("⚠️ target is unavailable. Please try again later."); + try { + res.statusCode = 502; + res.end("Bad Gateway"); + } catch {} }); proxy.on("proxyReq", (proxyReq, req, res) => { @@ -135,7 +133,7 @@ function getPortForHost(host) { case "admin.sun.museum": return 8080 - default: + default: return null } } diff --git a/package.json b/package.json index 66d1b6e..aae2e94 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "scripts": { - "start": "node index.js" - }, + "start": "node index.js" + }, "dependencies": { - "http-proxy": "^1.18.1" + "http-proxy": "^1.18.1", + "ws": "^8.18.3" } }