diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..29ae255 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: h44z # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index 74ca367..563c17d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2020-2023 Christoph Haas +Copyright (c) 2020-2025 Christoph Haas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 035c69f..9bc044f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The configuration portal supports using a database (SQLite, MySQL, MsSQL, or Pos ## Features * Self-hosted - the whole application is a single binary -* Responsive multi-language web UI written in Vue.js +* Responsive multi-language web UI with dark-mode written in Vue.js * Automatically selects IP from the network pool assigned to the client * QR-Code for convenient mobile client configuration * Sends email to the client with QR-code and client config @@ -32,7 +32,7 @@ The configuration portal supports using a database (SQLite, MySQL, MsSQL, or Pos * Docker ready * Can be used with existing WireGuard setups * Support for multiple WireGuard interfaces -* Supports multiple WireGuard backends (wgctrl or MikroTik [BETA]) +* Supports multiple WireGuard backends (wgctrl or MikroTik) * Peer Expiry Feature * Handles route and DNS settings like wg-quick does * Exposes Prometheus metrics for monitoring and alerting @@ -62,6 +62,17 @@ For the complete documentation visit [wgportal.org](https://wgportal.org). * MIT License. [MIT](LICENSE.txt) or +## Contributors and Sponsors + +Thanks so much for all your contributions! They’re truly appreciated and help keep WireGuard Portal moving ahead. + + + + + +Want to support the project? You can buy me a coffee or join as a contributor - every bit of support helps! +[Become a sponsor!](https://github.com/sponsors/h44z) + > [!IMPORTANT] > Since the project was accepted by the Docker-Sponsored Open Source Program, the Docker image location has moved to [wgportal/wg-portal](https://hub.docker.com/r/wgportal/wg-portal). diff --git a/SECURITY.md b/SECURITY.md index 559eccb..0ffebd7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ If you believe you've found a security issue in one of the supported versions of | Version | Supported | |---------|--------------------| | v2.x | :white_check_mark: | -| v1.x | :white_check_mark: | +| v1.x | :x: | ## Reporting a Vulnerability diff --git a/docs/assets/images/wgportal_dark.png b/docs/assets/images/wgportal_dark.png new file mode 100644 index 0000000..7260037 Binary files /dev/null and b/docs/assets/images/wgportal_dark.png differ diff --git a/docs/assets/images/wgportal_light.png b/docs/assets/images/wgportal_light.png new file mode 100644 index 0000000..9b76aa6 Binary files /dev/null and b/docs/assets/images/wgportal_light.png differ diff --git a/docs/javascript/img-comparison-slider.js b/docs/javascript/img-comparison-slider.js new file mode 100644 index 0000000..2655b62 --- /dev/null +++ b/docs/javascript/img-comparison-slider.js @@ -0,0 +1,2 @@ +(()=>{"use strict";var t={792:(t,e,i)=>{i.d(e,{Z:()=>n});var s=i(609),o=i.n(s)()((function(t){return t[1]}));o.push([t.id,':host{--divider-width: 1px;--divider-color: #fff;--divider-shadow: none;--default-handle-width: 50px;--default-handle-color: #fff;--default-handle-opacity: 1;--default-handle-shadow: none;--handle-position-start: 50%;position:relative;display:inline-block;overflow:hidden;line-height:0;direction:ltr}@media screen and (-webkit-min-device-pixel-ratio: 0)and (min-resolution: 0.001dpcm){:host{outline-offset:1px}}::slotted(*){-webkit-user-drag:none;-khtml-user-drag:none;-moz-user-drag:none;-o-user-drag:none;user-drag:none;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.first{position:absolute;left:0;top:0;right:0;line-height:normal;font-size:100%;max-height:100%;height:100%;width:100%;--exposure: 50%;--keyboard-transition-time: 0ms;--default-transition-time: 0ms;--transition-time: var(--default-transition-time)}.first .first-overlay-container{position:relative;clip-path:inset(0 var(--exposure) 0 0);transition:clip-path var(--transition-time);height:100%}.first .first-overlay{overflow:hidden;height:100%}.first.focused{will-change:clip-path}.first.focused .first-overlay-container{will-change:clip-path}.second{position:relative}.handle-container{transform:translateX(50%);position:absolute;top:0;right:var(--exposure);height:100%;transition:right var(--transition-time),bottom var(--transition-time)}.focused .handle-container{will-change:right}.divider{position:absolute;height:100%;width:100%;left:0;top:0;display:flex;align-items:center;justify-content:center;flex-direction:column}.divider:after{content:" ";display:block;height:100%;border-left-width:var(--divider-width);border-left-style:solid;border-left-color:var(--divider-color);box-shadow:var(--divider-shadow)}.handle{position:absolute;top:var(--handle-position-start);pointer-events:none;box-sizing:border-box;margin-left:1px;transform:translate(calc(-50% - 0.5px), -50%);line-height:0}.default-handle{width:var(--default-handle-width);opacity:var(--default-handle-opacity);transition:all 1s;filter:drop-shadow(var(--default-handle-shadow))}.default-handle path{stroke:var(--default-handle-color)}.vertical .first-overlay-container{clip-path:inset(0 0 var(--exposure) 0)}.vertical .handle-container{transform:translateY(50%);height:auto;top:unset;bottom:var(--exposure);width:100%;left:0;flex-direction:row}.vertical .divider:after{height:1px;width:100%;border-top-width:var(--divider-width);border-top-style:solid;border-top-color:var(--divider-color);border-left:0}.vertical .handle{top:auto;left:var(--handle-position-start);transform:translate(calc(-50% - 0.5px), -50%) rotate(90deg)}',""]);const n=o},609:t=>{t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var i=t(e);return e[2]?"@media ".concat(e[2]," {").concat(i,"}"):i})).join("")},e.i=function(t,i,s){"string"==typeof t&&(t=[[null,t,""]]);var o={};if(s)for(var n=0;n{var e=t&&t.__esModule?()=>t.default:()=>t;return i.d(e,{a:e}),e},i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{var t=i(792);const e="rendered",s=(t,e)=>{const i=t.getBoundingClientRect();let s,o;return"mousedown"===e.type?(s=e.clientX,o=e.clientY):(s=e.touches[0].clientX,o=e.touches[0].clientY),s>=i.x&&s<=i.x+i.width&&o>=i.y&&o<=i.y+i.height};let o;const n={ArrowLeft:-1,ArrowRight:1},r=["horizontal","vertical"],a=t=>({x:t.touches[0].pageX,y:t.touches[0].pageY}),d=t=>({x:t.pageX,y:t.pageY}),h="undefined"!=typeof window&&(null===window||void 0===window?void 0:window.HTMLElement);"undefined"!=typeof window&&(window.document&&(o=document.createElement("template"),o.innerHTML='
'),window.customElements.define("img-comparison-slider",class extends h{constructor(){super(),this.exposure=this.hasAttribute("value")?parseFloat(this.getAttribute("value")):50,this.slideOnHover=!1,this.slideDirection="horizontal",this.keyboard="enabled",this.isMouseDown=!1,this.animationDirection=0,this.isFocused=!1,this.dragByHandle=!1,this.onMouseMove=t=>{if(this.isMouseDown||this.slideOnHover){const e=d(t);this.slideToPage(e)}},this.bodyUserSelectStyle="",this.bodyWebkitUserSelectStyle="",this.onMouseDown=t=>{if(this.slideOnHover)return;if(this.handle&&!s(this.handleElement,t))return;t.preventDefault(),window.addEventListener("mousemove",this.onMouseMove),window.addEventListener("mouseup",this.onWindowMouseUp),this.isMouseDown=!0,this.enableTransition();const e=d(t);this.slideToPage(e),this.focus(),this.bodyUserSelectStyle=window.document.body.style.userSelect,this.bodyWebkitUserSelectStyle=window.document.body.style.webkitUserSelect,window.document.body.style.userSelect="none",window.document.body.style.webkitUserSelect="none"},this.onWindowMouseUp=()=>{this.isMouseDown=!1,window.document.body.style.userSelect=this.bodyUserSelectStyle,window.document.body.style.webkitUserSelect=this.bodyWebkitUserSelectStyle,window.removeEventListener("mousemove",this.onMouseMove),window.removeEventListener("mouseup",this.onWindowMouseUp)},this.touchStartPoint=null,this.isTouchComparing=!1,this.hasTouchMoved=!1,this.onTouchStart=t=>{this.dragByHandle&&!s(this.handleElement,t)||(this.touchStartPoint=a(t),this.isFocused&&(this.enableTransition(),this.slideToPage(this.touchStartPoint)))},this.onTouchMove=t=>{if(null===this.touchStartPoint)return;const e=a(t);if(this.isTouchComparing)return this.slideToPage(e),t.preventDefault(),!1;if(!this.hasTouchMoved){const i=Math.abs(e.y-this.touchStartPoint.y),s=Math.abs(e.x-this.touchStartPoint.x);if("horizontal"===this.slideDirection&&is)return this.isTouchComparing=!0,this.focus(),this.slideToPage(e),t.preventDefault(),!1;this.hasTouchMoved=!0}},this.onTouchEnd=()=>{this.isTouchComparing=!1,this.hasTouchMoved=!1,this.touchStartPoint=null},this.onBlur=()=>{this.stopSlideAnimation(),this.isFocused=!1,this.firstElement.classList.remove("focused")},this.onFocus=()=>{this.isFocused=!0,this.firstElement.classList.add("focused")},this.onKeyDown=t=>{if("disabled"===this.keyboard)return;const e=n[t.key];this.animationDirection!==e&&void 0!==e&&(this.animationDirection=e,this.startSlideAnimation())},this.onKeyUp=t=>{if("disabled"===this.keyboard)return;const e=n[t.key];void 0!==e&&this.animationDirection===e&&this.stopSlideAnimation()},this.resetDimensions=()=>{this.imageWidth=this.offsetWidth,this.imageHeight=this.offsetHeight};const e=this.attachShadow({mode:"open"}),i=document.createElement("style");i.innerHTML=t.Z,this.getAttribute("nonce")&&i.setAttribute("nonce",this.getAttribute("nonce")),e.appendChild(i),e.appendChild(o.content.cloneNode(!0)),this.firstElement=e.getElementById("first"),this.handleElement=e.getElementById("handle")}set handle(t){this.dragByHandle="false"!==t.toString().toLowerCase()}get handle(){return this.dragByHandle}get value(){return this.exposure}set value(t){const e=parseFloat(t);e!==this.exposure&&(this.exposure=e,this.enableTransition(),this.setExposure())}get hover(){return this.slideOnHover}set hover(t){this.slideOnHover="false"!==t.toString().toLowerCase(),this.removeEventListener("mousemove",this.onMouseMove),this.slideOnHover&&this.addEventListener("mousemove",this.onMouseMove)}get direction(){return this.slideDirection}set direction(t){this.slideDirection=t.toString().toLowerCase(),this.slide(0),this.firstElement.classList.remove(...r),r.includes(this.slideDirection)&&this.firstElement.classList.add(this.slideDirection)}static get observedAttributes(){return["hover","direction"]}connectedCallback(){this.hasAttribute("tabindex")||(this.tabIndex=0),this.addEventListener("dragstart",(t=>(t.preventDefault(),!1))),new ResizeObserver(this.resetDimensions).observe(this),this.setExposure(0),this.keyboard=this.hasAttribute("keyboard")&&"disabled"===this.getAttribute("keyboard")?"disabled":"enabled",this.addEventListener("keydown",this.onKeyDown),this.addEventListener("keyup",this.onKeyUp),this.addEventListener("focus",this.onFocus),this.addEventListener("blur",this.onBlur),this.addEventListener("touchstart",this.onTouchStart,{passive:!0}),this.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.addEventListener("touchend",this.onTouchEnd),this.addEventListener("mousedown",this.onMouseDown),this.handle=this.hasAttribute("handle")?this.getAttribute("handle"):this.dragByHandle,this.hover=this.hasAttribute("hover")?this.getAttribute("hover"):this.slideOnHover,this.direction=this.hasAttribute("direction")?this.getAttribute("direction"):this.slideDirection,this.resetDimensions(),this.classList.contains(e)||this.classList.add(e)}disconnectedCallback(){this.transitionTimer&&window.clearTimeout(this.transitionTimer)}attributeChangedCallback(t,e,i){"hover"===t&&(this.hover=i),"direction"===t&&(this.direction=i),"keyboard"===t&&(this.keyboard="disabled"===i?"disabled":"enabled")}setExposure(t=0){var e;this.exposure=(100,(e=this.exposure+t)<0?0:e>100?100:e),this.firstElement.style.setProperty("--exposure",100-this.exposure+"%")}slide(t=0){this.setExposure(t);const e=new Event("slide");this.dispatchEvent(e)}slideToPage(t){"horizontal"===this.slideDirection&&this.slideToPageX(t.x),"vertical"===this.slideDirection&&this.slideToPageY(t.y)}slideToPageX(t){const e=t-this.getBoundingClientRect().left-window.scrollX;this.exposure=e/this.imageWidth*100,this.slide(0)}slideToPageY(t){const e=t-this.getBoundingClientRect().top-window.scrollY;this.exposure=e/this.imageHeight*100,this.slide(0)}enableTransition(){this.firstElement.style.setProperty("--transition-time","100ms"),this.transitionTimer=window.setTimeout((()=>{this.firstElement.style.setProperty("--transition-time","var(--default-transition-time)"),this.transitionTimer=null}),100)}startSlideAnimation(){let t=null,e=this.animationDirection;this.firstElement.style.setProperty("--transition-time","var(--keyboard-transition-time)");const i=s=>{if(0===this.animationDirection||e!==this.animationDirection)return;null===t&&(t=s);const o=(s-t)/16.666666666666668*this.animationDirection;this.slide(o),setTimeout((()=>window.requestAnimationFrame(i)),0),t=s};window.requestAnimationFrame(i)}stopSlideAnimation(){this.animationDirection=0,this.firstElement.style.setProperty("--transition-time","var(--default-transition-time)")}}))})()})(); +//# sourceMappingURL=img-comparison-slider.js.map \ No newline at end of file diff --git a/docs/javascript/img-comparison-slider.js.map b/docs/javascript/img-comparison-slider.js.map new file mode 100644 index 0000000..d6cfc6f --- /dev/null +++ b/docs/javascript/img-comparison-slider.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","mappings":"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","sources":["webpack://img-comparison-slider/./src/styles.scss","webpack://img-comparison-slider/../../node_modules/css-loader/dist/runtime/api.js","webpack://img-comparison-slider/webpack/bootstrap","webpack://img-comparison-slider/webpack/runtime/compat get default export","webpack://img-comparison-slider/webpack/runtime/define property getters","webpack://img-comparison-slider/webpack/runtime/hasOwnProperty shorthand","webpack://img-comparison-slider/./src/template.html","webpack://img-comparison-slider/./src/defaults.ts","webpack://img-comparison-slider/./src/isElementAffected.ts","webpack://img-comparison-slider/./src/index.ts","webpack://img-comparison-slider/./src/inBetween.ts"],"sourcesContent":["// Imports\nimport ___CSS_LOADER_API_IMPORT___ from \"../../../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \":host{--divider-width: 1px;--divider-color: #fff;--divider-shadow: none;--default-handle-width: 50px;--default-handle-color: #fff;--default-handle-opacity: 1;--default-handle-shadow: none;--handle-position-start: 50%;position:relative;display:inline-block;overflow:hidden;line-height:0;direction:ltr}@media screen and (-webkit-min-device-pixel-ratio: 0)and (min-resolution: 0.001dpcm){:host{outline-offset:1px}}:host(:focus){outline:2px solid -webkit-focus-ring-color}::slotted(*){-webkit-user-drag:none;-khtml-user-drag:none;-moz-user-drag:none;-o-user-drag:none;user-drag:none;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.first{position:absolute;left:0;top:0;right:0;line-height:normal;font-size:100%;max-height:100%;height:100%;width:100%;--exposure: 50%;--keyboard-transition-time: 0ms;--default-transition-time: 0ms;--transition-time: var(--default-transition-time)}.first .first-overlay-container{position:relative;clip-path:inset(0 var(--exposure) 0 0);transition:clip-path var(--transition-time);height:100%}.first .first-overlay{overflow:hidden;height:100%}.first.focused{will-change:clip-path}.first.focused .first-overlay-container{will-change:clip-path}.second{position:relative}.handle-container{transform:translateX(50%);position:absolute;top:0;right:var(--exposure);height:100%;transition:right var(--transition-time),bottom var(--transition-time)}.focused .handle-container{will-change:right}.divider{position:absolute;height:100%;width:100%;left:0;top:0;display:flex;align-items:center;justify-content:center;flex-direction:column}.divider:after{content:\\\" \\\";display:block;height:100%;border-left-width:var(--divider-width);border-left-style:solid;border-left-color:var(--divider-color);box-shadow:var(--divider-shadow)}.handle{position:absolute;top:var(--handle-position-start);pointer-events:none;box-sizing:border-box;margin-left:1px;transform:translate(calc(-50% - 0.5px), -50%);line-height:0}.default-handle{width:var(--default-handle-width);opacity:var(--default-handle-opacity);transition:all 1s;filter:drop-shadow(var(--default-handle-shadow))}.default-handle path{stroke:var(--default-handle-color)}.vertical .first-overlay-container{clip-path:inset(0 0 var(--exposure) 0)}.vertical .handle-container{transform:translateY(50%);height:auto;top:unset;bottom:var(--exposure);width:100%;left:0;flex-direction:row}.vertical .divider:after{height:1px;width:100%;border-top-width:var(--divider-width);border-top-style:solid;border-top-color:var(--divider-color);border-left:0}.vertical .handle{top:auto;left:var(--handle-position-start);transform:translate(calc(-50% - 0.5px), -50%) rotate(90deg)}\", \"\"]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","\"use strict\";\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\n// eslint-disable-next-line func-names\nmodule.exports = function (cssWithMappingToString) {\n var list = []; // return the list of modules as css string\n\n list.toString = function toString() {\n return this.map(function (item) {\n var content = cssWithMappingToString(item);\n\n if (item[2]) {\n return \"@media \".concat(item[2], \" {\").concat(content, \"}\");\n }\n\n return content;\n }).join(\"\");\n }; // import a list of modules into the list\n // eslint-disable-next-line func-names\n\n\n list.i = function (modules, mediaQuery, dedupe) {\n if (typeof modules === \"string\") {\n // eslint-disable-next-line no-param-reassign\n modules = [[null, modules, \"\"]];\n }\n\n var alreadyImportedModules = {};\n\n if (dedupe) {\n for (var i = 0; i < this.length; i++) {\n // eslint-disable-next-line prefer-destructuring\n var id = this[i][0];\n\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n }\n\n for (var _i = 0; _i < modules.length; _i++) {\n var item = [].concat(modules[_i]);\n\n if (dedupe && alreadyImportedModules[item[0]]) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n if (mediaQuery) {\n if (!item[2]) {\n item[2] = mediaQuery;\n } else {\n item[2] = \"\".concat(mediaQuery, \" and \").concat(item[2]);\n }\n }\n\n list.push(item);\n }\n };\n\n return list;\n};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// Module\nvar code = \"
\";\n// Exports\nexport default code;","export const TABINDEX = 0;\nexport const RENDERED_CLASS = 'rendered';\n","export const isMouseEvent = (event) => {\n return event.type === 'mousedown';\n};\nexport const isElementAffected = (element, e) => {\n const rect = element.getBoundingClientRect();\n let eventX, eventY;\n if (isMouseEvent(e)) {\n eventX = e.clientX;\n eventY = e.clientY;\n }\n else {\n eventX = e.touches[0].clientX;\n eventY = e.touches[0].clientY;\n }\n return (eventX >= rect.x &&\n eventX <= rect.x + rect.width &&\n eventY >= rect.y &&\n eventY <= rect.y + rect.height);\n};\n","import styles from './styles.scss';\nimport { inBetween } from './inBetween';\nimport templateHtml from './template.html';\nimport { TABINDEX, RENDERED_CLASS } from './defaults';\nimport { isElementAffected } from './isElementAffected';\nlet templateElement;\nconst KeySlideOffset = {\n ArrowLeft: -1,\n ArrowRight: 1,\n};\nconst slideDirections = ['horizontal', 'vertical'];\nconst getTouchPagePoint = (e) => ({\n x: e.touches[0].pageX,\n y: e.touches[0].pageY,\n});\nconst getMousePagePoint = (e) => ({\n x: e.pageX,\n y: e.pageY,\n});\nconst slideAnimationPeriod = 1000 / 60;\nconst HTMLElement = typeof window !== 'undefined' && (window === null || window === void 0 ? void 0 : window.HTMLElement);\nexport class HTMLImgComparisonSliderElement extends HTMLElement {\n constructor() {\n super();\n this.exposure = this.hasAttribute('value')\n ? parseFloat(this.getAttribute('value'))\n : 50;\n this.slideOnHover = false;\n this.slideDirection = 'horizontal';\n this.keyboard = 'enabled';\n this.isMouseDown = false;\n this.animationDirection = 0;\n this.isFocused = false;\n this.dragByHandle = false;\n this.onMouseMove = (e) => {\n if (this.isMouseDown || this.slideOnHover) {\n const currentPoint = getMousePagePoint(e);\n this.slideToPage(currentPoint);\n }\n };\n this.bodyUserSelectStyle = '';\n this.bodyWebkitUserSelectStyle = '';\n this.onMouseDown = (e) => {\n if (this.slideOnHover) {\n return;\n }\n if (this.handle && !isElementAffected(this.handleElement, e)) {\n return;\n }\n e.preventDefault();\n window.addEventListener('mousemove', this.onMouseMove);\n window.addEventListener('mouseup', this.onWindowMouseUp);\n this.isMouseDown = true;\n this.enableTransition();\n const currentPoint = getMousePagePoint(e);\n this.slideToPage(currentPoint);\n this.focus();\n this.bodyUserSelectStyle = window.document.body.style.userSelect;\n this.bodyWebkitUserSelectStyle =\n window.document.body.style.webkitUserSelect;\n window.document.body.style.userSelect = 'none';\n window.document.body.style.webkitUserSelect = 'none';\n };\n this.onWindowMouseUp = () => {\n this.isMouseDown = false;\n window.document.body.style.userSelect = this.bodyUserSelectStyle;\n window.document.body.style.webkitUserSelect =\n this.bodyWebkitUserSelectStyle;\n window.removeEventListener('mousemove', this.onMouseMove);\n window.removeEventListener('mouseup', this.onWindowMouseUp);\n };\n this.touchStartPoint = null;\n this.isTouchComparing = false;\n this.hasTouchMoved = false;\n this.onTouchStart = (e) => {\n if (this.dragByHandle && !isElementAffected(this.handleElement, e)) {\n return;\n }\n this.touchStartPoint = getTouchPagePoint(e);\n if (this.isFocused) {\n this.enableTransition();\n this.slideToPage(this.touchStartPoint);\n }\n };\n this.onTouchMove = (e) => {\n if (this.touchStartPoint === null) {\n return;\n }\n const currentPoint = getTouchPagePoint(e);\n if (this.isTouchComparing) {\n this.slideToPage(currentPoint);\n e.preventDefault();\n return false;\n }\n if (!this.hasTouchMoved) {\n const offsetY = Math.abs(currentPoint.y - this.touchStartPoint.y);\n const offsetX = Math.abs(currentPoint.x - this.touchStartPoint.x);\n if ((this.slideDirection === 'horizontal' && offsetY < offsetX) ||\n (this.slideDirection === 'vertical' && offsetY > offsetX)) {\n this.isTouchComparing = true;\n this.focus();\n this.slideToPage(currentPoint);\n e.preventDefault();\n return false;\n }\n this.hasTouchMoved = true;\n }\n };\n this.onTouchEnd = () => {\n this.isTouchComparing = false;\n this.hasTouchMoved = false;\n this.touchStartPoint = null;\n };\n this.onBlur = () => {\n this.stopSlideAnimation();\n this.isFocused = false;\n this.firstElement.classList.remove('focused');\n };\n this.onFocus = () => {\n this.isFocused = true;\n this.firstElement.classList.add('focused');\n };\n this.onKeyDown = (e) => {\n if (this.keyboard === 'disabled') {\n return;\n }\n const direction = KeySlideOffset[e.key];\n if (this.animationDirection === direction) {\n return;\n }\n if (direction === undefined) {\n return;\n }\n this.animationDirection = direction;\n this.startSlideAnimation();\n };\n this.onKeyUp = (e) => {\n if (this.keyboard === 'disabled') {\n return;\n }\n const direction = KeySlideOffset[e.key];\n if (direction === undefined) {\n return;\n }\n if (this.animationDirection !== direction) {\n return;\n }\n this.stopSlideAnimation();\n };\n this.resetDimensions = () => {\n this.imageWidth = this.offsetWidth;\n this.imageHeight = this.offsetHeight;\n };\n const shadowRoot = this.attachShadow({ mode: 'open' });\n const styleEl = document.createElement('style');\n styleEl.innerHTML = styles;\n if (this.getAttribute('nonce')) {\n styleEl.setAttribute('nonce', this.getAttribute('nonce'));\n }\n shadowRoot.appendChild(styleEl);\n shadowRoot.appendChild(templateElement.content.cloneNode(true));\n this.firstElement = shadowRoot.getElementById('first');\n this.handleElement = shadowRoot.getElementById('handle');\n }\n set handle(newValue) {\n this.dragByHandle = newValue.toString().toLowerCase() !== 'false';\n }\n get handle() {\n return this.dragByHandle;\n }\n get value() {\n return this.exposure;\n }\n set value(newValue) {\n const newExposure = parseFloat(newValue);\n if (newExposure === this.exposure) {\n return;\n }\n this.exposure = newExposure;\n this.enableTransition();\n this.setExposure();\n }\n get hover() {\n return this.slideOnHover;\n }\n set hover(newValue) {\n this.slideOnHover = newValue.toString().toLowerCase() !== 'false';\n this.removeEventListener('mousemove', this.onMouseMove);\n if (this.slideOnHover) {\n this.addEventListener('mousemove', this.onMouseMove);\n }\n }\n get direction() {\n return this.slideDirection;\n }\n set direction(newValue) {\n this.slideDirection = newValue.toString().toLowerCase();\n this.slide(0);\n this.firstElement.classList.remove(...slideDirections);\n if (!slideDirections.includes(this.slideDirection)) {\n return;\n }\n this.firstElement.classList.add(this.slideDirection);\n }\n static get observedAttributes() {\n return ['hover', 'direction'];\n }\n connectedCallback() {\n if (!this.hasAttribute('tabindex')) {\n this.tabIndex = TABINDEX;\n }\n this.addEventListener('dragstart', (e) => {\n e.preventDefault();\n return false;\n });\n const resizeObserver = new ResizeObserver(this.resetDimensions);\n resizeObserver.observe(this);\n this.setExposure(0);\n this.keyboard =\n this.hasAttribute('keyboard') &&\n this.getAttribute('keyboard') === 'disabled'\n ? 'disabled'\n : 'enabled';\n this.addEventListener('keydown', this.onKeyDown);\n this.addEventListener('keyup', this.onKeyUp);\n this.addEventListener('focus', this.onFocus);\n this.addEventListener('blur', this.onBlur);\n this.addEventListener('touchstart', this.onTouchStart, {\n passive: true,\n });\n this.addEventListener('touchmove', this.onTouchMove, {\n passive: false,\n });\n this.addEventListener('touchend', this.onTouchEnd);\n this.addEventListener('mousedown', this.onMouseDown);\n this.handle = this.hasAttribute('handle')\n ? this.getAttribute('handle')\n : this.dragByHandle;\n this.hover = this.hasAttribute('hover')\n ? this.getAttribute('hover')\n : this.slideOnHover;\n this.direction = this.hasAttribute('direction')\n ? this.getAttribute('direction')\n : this.slideDirection;\n this.resetDimensions();\n if (!this.classList.contains(RENDERED_CLASS)) {\n this.classList.add(RENDERED_CLASS);\n }\n }\n disconnectedCallback() {\n if (this.transitionTimer) {\n window.clearTimeout(this.transitionTimer);\n }\n }\n attributeChangedCallback(name, oldValue, newValue) {\n if (name === 'hover') {\n this.hover = newValue;\n }\n if (name === 'direction') {\n this.direction = newValue;\n }\n if (name === 'keyboard') {\n this.keyboard = newValue === 'disabled' ? 'disabled' : 'enabled';\n }\n }\n setExposure(increment = 0) {\n this.exposure = inBetween(this.exposure + increment, 0, 100);\n this.firstElement.style.setProperty('--exposure', `${100 - this.exposure}%`);\n }\n slide(increment = 0) {\n this.setExposure(increment);\n const event = new Event('slide');\n this.dispatchEvent(event);\n }\n slideToPage(currentPoint) {\n if (this.slideDirection === 'horizontal') {\n this.slideToPageX(currentPoint.x);\n }\n if (this.slideDirection === 'vertical') {\n this.slideToPageY(currentPoint.y);\n }\n }\n slideToPageX(pageX) {\n const x = pageX - this.getBoundingClientRect().left - window.scrollX;\n this.exposure = (x / this.imageWidth) * 100;\n this.slide(0);\n }\n slideToPageY(pageY) {\n const y = pageY - this.getBoundingClientRect().top - window.scrollY;\n this.exposure = (y / this.imageHeight) * 100;\n this.slide(0);\n }\n enableTransition() {\n const transitionTime = 100;\n this.firstElement.style.setProperty('--transition-time', `${transitionTime}ms`);\n this.transitionTimer = window.setTimeout(() => {\n this.firstElement.style.setProperty('--transition-time', `var(--default-transition-time)`);\n this.transitionTimer = null;\n }, transitionTime);\n }\n startSlideAnimation() {\n let lastTimestamp = null;\n let initialDirection = this.animationDirection;\n this.firstElement.style.setProperty('--transition-time', `var(--keyboard-transition-time)`);\n const slide = (now) => {\n if (this.animationDirection === 0 ||\n initialDirection !== this.animationDirection) {\n return;\n }\n if (lastTimestamp === null) {\n lastTimestamp = now;\n }\n const interval = now - lastTimestamp, distance = (interval / slideAnimationPeriod) * this.animationDirection;\n this.slide(distance);\n // This is necessary to speed up the key up event in Desktop Safari\n setTimeout(() => window.requestAnimationFrame(slide), 0);\n lastTimestamp = now;\n };\n window.requestAnimationFrame(slide);\n }\n stopSlideAnimation() {\n this.animationDirection = 0;\n this.firstElement.style.setProperty('--transition-time', `var(--default-transition-time)`);\n }\n}\nif (typeof window !== 'undefined') {\n if (window.document) {\n templateElement = document.createElement('template');\n templateElement.innerHTML = templateHtml;\n }\n window.customElements.define('img-comparison-slider', HTMLImgComparisonSliderElement);\n}\n","export const inBetween = (actual, min, max) => {\n if (actual < min) {\n return min;\n }\n if (actual > max) {\n return max;\n }\n return actual;\n};\n"],"names":["___CSS_LOADER_EXPORT___","i","push","module","id","exports","cssWithMappingToString","list","toString","this","map","item","content","concat","join","modules","mediaQuery","dedupe","alreadyImportedModules","length","_i","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","RENDERED_CLASS","isElementAffected","element","e","rect","getBoundingClientRect","eventX","eventY","type","clientX","clientY","touches","x","width","y","height","templateElement","KeySlideOffset","ArrowLeft","ArrowRight","slideDirections","getTouchPagePoint","pageX","pageY","getMousePagePoint","HTMLElement","window","document","createElement","innerHTML","customElements","define","constructor","super","exposure","hasAttribute","parseFloat","getAttribute","slideOnHover","slideDirection","keyboard","isMouseDown","animationDirection","isFocused","dragByHandle","onMouseMove","currentPoint","slideToPage","bodyUserSelectStyle","bodyWebkitUserSelectStyle","onMouseDown","handle","handleElement","preventDefault","addEventListener","onWindowMouseUp","enableTransition","focus","body","style","userSelect","webkitUserSelect","removeEventListener","touchStartPoint","isTouchComparing","hasTouchMoved","onTouchStart","onTouchMove","offsetY","Math","abs","offsetX","onTouchEnd","onBlur","stopSlideAnimation","firstElement","classList","remove","onFocus","add","onKeyDown","direction","startSlideAnimation","onKeyUp","resetDimensions","imageWidth","offsetWidth","imageHeight","offsetHeight","shadowRoot","attachShadow","mode","styleEl","styles","setAttribute","appendChild","cloneNode","getElementById","newValue","toLowerCase","value","newExposure","setExposure","hover","slide","includes","observedAttributes","connectedCallback","tabIndex","ResizeObserver","observe","passive","contains","disconnectedCallback","transitionTimer","clearTimeout","attributeChangedCallback","name","oldValue","increment","actual","setProperty","event","Event","dispatchEvent","slideToPageX","slideToPageY","left","scrollX","top","scrollY","setTimeout","lastTimestamp","initialDirection","now","distance","requestAnimationFrame"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/stylesheets/img-comparison-slider.css b/docs/stylesheets/img-comparison-slider.css new file mode 100644 index 0000000..f73d40f --- /dev/null +++ b/docs/stylesheets/img-comparison-slider.css @@ -0,0 +1,15 @@ +img-comparison-slider { + visibility: hidden; +} + +img-comparison-slider [slot='second'] { + display: none; +} + +img-comparison-slider.rendered { + visibility: inherit; +} + +img-comparison-slider.rendered [slot='second'] { + display: unset; +} diff --git a/docs/theme-overrides/layouts/home.html b/docs/theme-overrides/layouts/home.html index 447ab19..3ab842e 100644 --- a/docs/theme-overrides/layouts/home.html +++ b/docs/theme-overrides/layouts/home.html @@ -300,6 +300,59 @@ background: var(--md-accent-fg-color--transparent); } +.before, +.after { + margin: 0; +} + +.after figcaption { + background: #fff; + font-weight: bold; + border: 1px solid #c0c0c0; + color: #000000; + opacity: 0.9; + padding: 9px; + position: absolute; + top: 100%; + transform: translateY(-100%); + line-height: 100%; +} + +.before figcaption { + background: #000; + font-weight: bold; + border: 1px solid #c0c0c0; + color: #ffffff; + opacity: 0.9; + padding: 9px; + position: absolute; + top: 100%; + transform: translateY(-100%); + line-height: 100%; +} + +.before figcaption { + left: 0px; +} +.after figcaption { + right: 0px; +} +.custom-animated-handle { + transition: transform 0.2s; +} + +.slider-with-animated-handle:hover .custom-animated-handle { + transform: scale(1.2); +} +.md-typeset img-comparison-slider figure { + margin: initial; +} + +.first-overlay { + color: #000; +} + + @@ -326,11 +379,34 @@
- +
+ +
+ Light Mode +
Light Mode
+
+
+ Dark Mode +
Dark Mode
+
+ + + + + + + + +
+
diff --git a/mkdocs.yml b/mkdocs.yml index 7184e3c..5bf7686 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,8 +6,12 @@ repo_name: h44z/wg-portal repo_url: https://github.com/h44z/wg-portal copyright: Copyright © 2023-2025 WireGuard Portal Project +extra_javascript: + - javascript/img-comparison-slider.js + extra_css: - stylesheets/extra.css + - stylesheets/img-comparison-slider.css theme: name: material