From c6fc9c90c6ca7607927ae1505857562477b34ff4 Mon Sep 17 00:00:00 2001 From: metacryst Date: Sat, 20 Dec 2025 14:39:14 -0600 Subject: [PATCH] working node server --- UI/_/code/quill.js | 1047 -------------------------------------- UI/_/icons/lightning.png | Bin 15593 -> 0 bytes UI/_/icons/shield.png | Bin 8535 -> 0 bytes UI/index.html | 13 - UI/index.js | 0 http.js | 12 + index.js | 14 +- package.json | 7 +- 8 files changed, 22 insertions(+), 1071 deletions(-) delete mode 100644 UI/_/code/quill.js delete mode 100644 UI/_/icons/lightning.png delete mode 100644 UI/_/icons/shield.png delete mode 100644 UI/index.html delete mode 100644 UI/index.js create mode 100644 http.js 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 39ff0844e479184d05931ad5517f4af7d28bac16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15593 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 Qw7nx#QPfnZk+TZ>KPjx27XSbN diff --git a/UI/_/icons/shield.png b/UI/_/icons/shield.png deleted file mode 100644 index 1602e5355e231a68a8b053673fffc8695dcded28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8535 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 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" } }