diff --git a/nightwatch/desktop/src/css/lib.bundle.css b/nightwatch/desktop/src/css/lib.bundle.css deleted file mode 100644 index 3f53864..0000000 --- a/nightwatch/desktop/src/css/lib.bundle.css +++ /dev/null @@ -1,2 +0,0 @@ -/* https://github.com/f3oall/awesome-notifications/blob/master/dist/style.css */ -@keyframes awn-fade-in{0%{opacity:0}to{opacity:1}}@keyframes awn-fade-out{0%{opacity:1}to{opacity:0}}@keyframes awn-slide-right{0%{left:100%;opacity:0}to{left:0;opacity:1}}@keyframes awn-slide-left{0%{opacity:0;right:100%}to{opacity:1;right:0}}@keyframes awn-bar{0%{right:100%}to{right:0}}.awn-popup-loading-dots,.awn-popup-loading-dots:after,.awn-popup-loading-dots:before{animation-fill-mode:both;animation:awn-loading-dots 1s ease-in-out infinite;background:#fff;border-radius:50%;height:6px;width:6px}.awn-popup-loading-dots{animation-delay:-.16s;color:#fff;display:inline-block;margin-left:24px;position:relative}.awn-popup-loading-dots:after,.awn-popup-loading-dots:before{content:"";position:absolute;top:0}.awn-popup-loading-dots:before{animation-delay:-.32s;left:-16px}.awn-popup-loading-dots:after{left:16px}@keyframes awn-loading-dots{0%,80%,to{box-shadow:0 0 0 0}40%{box-shadow:0 0 0 2px}}#awn-popup-wrapper{align-items:center;animation-fill-mode:both;animation-name:awn-fade-in;animation-timing-function:ease-out;background:rgba(0,0,0,.7);bottom:0;display:flex;justify-content:center;left:0;opacity:0;position:fixed;right:0;top:0;z-index:99999}#awn-popup-wrapper.awn-hiding{animation-name:awn-fade-out}#awn-popup-wrapper .awn-popup-body{background:#fff;border-radius:6px;font-size:14px;max-width:500px;min-width:320px;padding:24px;position:relative;word-break:break-word}#awn-popup-wrapper .awn-popup-body.awn-popup-confirm{align-items:center;display:flex;flex-direction:column}#awn-popup-wrapper .awn-popup-body.awn-popup-confirm .fa{color:#c26700;font-size:44px}#awn-popup-wrapper .awn-popup-body.awn-popup-async-block{background:transparent;color:#fff;font-size:32px;font-weight:700;text-align:center}#awn-popup-wrapper .awn-popup-title{font-size:14px;font-weight:700;margin-top:8px;text-transform:uppercase}#awn-popup-wrapper .awn-buttons{display:flex;justify-content:space-between;margin-top:24px;width:100%}#awn-popup-wrapper .awn-buttons .awn-btn{border:0;border-radius:4px;color:#fff;font-size:14px;font-weight:700;line-height:32px;transition:background .2s linear;width:45%}#awn-popup-wrapper .awn-buttons-1 .awn-btn{width:100%}#awn-popup-wrapper .awn-buttons .awn-btn-success{background:#40871d}#awn-popup-wrapper .awn-buttons .awn-btn-success:hover{background:#367218}#awn-popup-wrapper .awn-buttons .awn-btn-cancel{background:#1c76a6}#awn-popup-wrapper .awn-buttons .awn-btn-cancel:hover{background:#186690}#awn-toast-container{bottom:24px;box-sizing:border-box;position:fixed;right:24px;z-index:99998}#awn-toast-container.awn-top-left,#awn-toast-container.awn-top-right{bottom:auto;top:24px}#awn-toast-container.awn-top-left .awn-toast:first-child,#awn-toast-container.awn-top-right .awn-toast:first-child{margin-top:16px}#awn-toast-container.awn-bottom-left,#awn-toast-container.awn-top-left{left:24px;right:auto}#awn-toast-container.awn-bottom-left .awn-toast,#awn-toast-container.awn-top-left .awn-toast{animation-name:awn-slide-left;right:100%}#awn-toast-container.awn-bottom-left .awn-toast.awn-hiding,#awn-toast-container.awn-top-left .awn-toast.awn-hiding{right:0}#awn-toast-container.awn-bottom-right .awn-toast,#awn-toast-container.awn-top-right .awn-toast{animation-name:awn-slide-right;left:100%}#awn-toast-container.awn-bottom-right .awn-toast.awn-hiding,#awn-toast-container.awn-top-right .awn-toast.awn-hiding{left:0}.awn-toast{animation-fill-mode:both;animation-timing-function:linear;background:#ebebeb;border-radius:6px;color:gray;cursor:pointer;font-size:14px;margin-top:16px;opacity:0;overflow:hidden;position:relative;width:320px}.awn-toast-content{word-break:break-word}.awn-toast-label{color:gray;display:block;font-size:18px;text-transform:uppercase}.awn-toast-icon{align-items:center;bottom:0;display:flex;justify-content:flex-end;position:absolute;right:16px;top:6px}.awn-toast-icon .fa{color:gray;font-size:44px}.awn-toast-wrapper{border:2px solid #d1d1d1;border-radius:6px;padding:22px 88px 16px 16px}.awn-toast-progress-bar{height:6px;left:0;position:absolute;right:0;top:0}.awn-toast-progress-bar:after{animation-duration:inherit;animation-fill-mode:both;animation-name:awn-bar;animation-timing-function:linear;background:gray;content:" ";height:6px;position:absolute;right:100%;top:0;width:100%}.awn-toast.awn-toast-progress-bar-paused .awn-toast-progress-bar:after{animation-play-state:paused}.awn-toast.awn-hiding{animation-name:awn-fade-out!important}.awn-toast.awn-toast-success{background:#dff8d3;color:#40871d}.awn-toast.awn-toast-success .awn-toast-wrapper{border-color:#a7d590}.awn-toast.awn-toast-success .fa,.awn-toast.awn-toast-success b{color:#40871d}.awn-toast.awn-toast-success .awn-toast-progress-bar:after{background:#40871d}.awn-toast.awn-toast-info{background:#d3ebf8;color:#1c76a6}.awn-toast.awn-toast-info .awn-toast-wrapper{border-color:#9fd3ef}.awn-toast.awn-toast-info .fa,.awn-toast.awn-toast-info b{color:#1c76a6}.awn-toast.awn-toast-info .awn-toast-progress-bar:after{background:#1c76a6}.awn-toast.awn-toast-alert{background:#f8d5d3;color:#a92019}.awn-toast.awn-toast-alert .awn-toast-wrapper{border-color:#f0a29d}.awn-toast.awn-toast-alert .fa,.awn-toast.awn-toast-alert b{color:#a92019}.awn-toast.awn-toast-alert .awn-toast-progress-bar:after{background:#a92019}.awn-toast.awn-toast-warning{background:#ffe7cc;color:#c26700}.awn-toast.awn-toast-warning .awn-toast-wrapper{border-color:#ffc480}.awn-toast.awn-toast-warning .fa,.awn-toast.awn-toast-warning b{color:#c26700}.awn-toast.awn-toast-warning .awn-toast-progress-bar:after{background:#c26700}[class^=awn-]{box-sizing:border-box} \ No newline at end of file diff --git a/nightwatch/desktop/src/css/main.css b/nightwatch/desktop/src/css/main.css index d3d0053..f7faa45 100644 --- a/nightwatch/desktop/src/css/main.css +++ b/nightwatch/desktop/src/css/main.css @@ -31,7 +31,6 @@ body { background-color: var(--sd-black-d10); } ul.nav { - gap: 10px; margin-top: 10px; margin-left: 10px; margin-right: 10px; @@ -63,8 +62,8 @@ ul.nav { /* Popper popups */ div.nw-tooltip { - background: #222; - color: white; + background: var(--sd-black-d5); + color: var(--sd-white); font-weight: bold; padding: 5px; border-radius: 4px; diff --git a/nightwatch/desktop/src/frames/errors/login.html b/nightwatch/desktop/src/frames/errors/login.html index 97ad69d..04af64b 100644 --- a/nightwatch/desktop/src/frames/errors/login.html +++ b/nightwatch/desktop/src/frames/errors/login.html @@ -1,18 +1,19 @@
\ No newline at end of file diff --git a/nightwatch/desktop/src/frames/errors/network.html b/nightwatch/desktop/src/frames/errors/network.html index 2c9020f..e5ba334 100644 --- a/nightwatch/desktop/src/frames/errors/network.html +++ b/nightwatch/desktop/src/frames/errors/network.html @@ -1,11 +1,12 @@
\ No newline at end of file diff --git a/nightwatch/desktop/src/frames/server/add.html b/nightwatch/desktop/src/frames/server/add.html index 24f3656..f7041a3 100644 --- a/nightwatch/desktop/src/frames/server/add.html +++ b/nightwatch/desktop/src/frames/server/add.html @@ -14,6 +14,8 @@ \ No newline at end of file diff --git a/nightwatch/desktop/src/frames/settings.html b/nightwatch/desktop/src/frames/settings.html index 85a11d2..551591a 100644 --- a/nightwatch/desktop/src/frames/settings.html +++ b/nightwatch/desktop/src/frames/settings.html @@ -1,10 +1,30 @@
-
Settings
+
Client Settings

-

This is eventually where settings will go.

+
+
+

Auth Server

+ +
+

+ \ No newline at end of file diff --git a/nightwatch/desktop/src/frames/welcome.html b/nightwatch/desktop/src/frames/welcome.html index c284bd3..85e593d 100644 --- a/nightwatch/desktop/src/frames/welcome.html +++ b/nightwatch/desktop/src/frames/welcome.html @@ -1,8 +1,9 @@
\ No newline at end of file diff --git a/nightwatch/desktop/src/index.html b/nightwatch/desktop/src/index.html index 9d127e8..e42ba27 100644 --- a/nightwatch/desktop/src/index.html +++ b/nightwatch/desktop/src/index.html @@ -8,22 +8,59 @@ - Nightwatch - +
- + + + + + + + + + + + + diff --git a/nightwatch/desktop/src/js/bootstrap/base-component.js b/nightwatch/desktop/src/js/bootstrap/base-component.js new file mode 100644 index 0000000..a5af19f --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/base-component.js @@ -0,0 +1,84 @@ +/*! + * Bootstrap base-component.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./util/config.js'), require('./util/index.js')) : + typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './util/config', './util/index'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BaseComponent = factory(global.Data, global.EventHandler, global.Config, global.Index)); +})(this, (function (Data, EventHandler, Config, index_js) { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap base-component.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const VERSION = '5.3.3'; + + /** + * Class definition + */ + + class BaseComponent extends Config { + constructor(element, config) { + super(); + element = index_js.getElement(element); + if (!element) { + return; + } + this._element = element; + this._config = this._getConfig(config); + Data.set(this._element, this.constructor.DATA_KEY, this); + } + + // Public + dispose() { + Data.remove(this._element, this.constructor.DATA_KEY); + EventHandler.off(this._element, this.constructor.EVENT_KEY); + for (const propertyName of Object.getOwnPropertyNames(this)) { + this[propertyName] = null; + } + } + _queueCallback(callback, element, isAnimated = true) { + index_js.executeAfterTransition(callback, element, isAnimated); + } + _getConfig(config) { + config = this._mergeConfigObj(config, this._element); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + + // Static + static getInstance(element) { + return Data.get(index_js.getElement(element), this.DATA_KEY); + } + static getOrCreateInstance(element, config = {}) { + return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); + } + static get VERSION() { + return VERSION; + } + static get DATA_KEY() { + return `bs.${this.NAME}`; + } + static get EVENT_KEY() { + return `.${this.DATA_KEY}`; + } + static eventName(name) { + return `${name}${this.EVENT_KEY}`; + } + } + + return BaseComponent; + +})); +//# sourceMappingURL=base-component.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/config.js b/nightwatch/desktop/src/js/bootstrap/config.js new file mode 100644 index 0000000..0e1b2c7 --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/config.js @@ -0,0 +1,68 @@ +/*! + * Bootstrap config.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/manipulator.js'), require('./index.js')) : + typeof define === 'function' && define.amd ? define(['../dom/manipulator', './index'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Config = factory(global.Manipulator, global.Index)); +})(this, (function (Manipulator, index_js) { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/config.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Class definition + */ + + class Config { + // Getters + static get Default() { + return {}; + } + static get DefaultType() { + return {}; + } + static get NAME() { + throw new Error('You have to implement the static method "NAME", for each component!'); + } + _getConfig(config) { + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + _configAfterMerge(config) { + return config; + } + _mergeConfigObj(config, element) { + const jsonConfig = index_js.isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse + + return { + ...this.constructor.Default, + ...(typeof jsonConfig === 'object' ? jsonConfig : {}), + ...(index_js.isElement(element) ? Manipulator.getDataAttributes(element) : {}), + ...(typeof config === 'object' ? config : {}) + }; + } + _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { + for (const [property, expectedTypes] of Object.entries(configTypes)) { + const value = config[property]; + const valueType = index_js.isElement(value) ? 'element' : index_js.toType(value); + if (!new RegExp(expectedTypes).test(valueType)) { + throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); + } + } + } + } + + return Config; + +})); +//# sourceMappingURL=config.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/data.js b/nightwatch/desktop/src/js/bootstrap/data.js new file mode 100644 index 0000000..23d4cb9 --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/data.js @@ -0,0 +1,63 @@ +/*! + * Bootstrap data.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Data = factory()); +})(this, (function () { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/data.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + /** + * Constants + */ + + const elementMap = new Map(); + const data = { + set(element, key, instance) { + if (!elementMap.has(element)) { + elementMap.set(element, new Map()); + } + const instanceMap = elementMap.get(element); + + // make it clear we only want one instance per element + // can be removed later when multiple key/instances are fine to be used + if (!instanceMap.has(key) && instanceMap.size !== 0) { + // eslint-disable-next-line no-console + console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); + return; + } + instanceMap.set(key, instance); + }, + get(element, key) { + if (elementMap.has(element)) { + return elementMap.get(element).get(key) || null; + } + return null; + }, + remove(element, key) { + if (!elementMap.has(element)) { + return; + } + const instanceMap = elementMap.get(element); + instanceMap.delete(key); + + // free up element references if there are no instances left for an element + if (instanceMap.size === 0) { + elementMap.delete(element); + } + } + }; + + return data; + +})); +//# sourceMappingURL=data.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/dropdown.js b/nightwatch/desktop/src/js/bootstrap/dropdown.js new file mode 100644 index 0000000..259e384 --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/dropdown.js @@ -0,0 +1,370 @@ +/*! + * Bootstrap dropdown.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js')) : + typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global.Popper, global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index)); +})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js) { 'use strict'; + const NAME = 'dropdown'; + const DATA_KEY = 'bs.dropdown'; + const EVENT_KEY = `.${DATA_KEY}`; + const DATA_API_KEY = '.data-api'; + const ESCAPE_KEY = 'Escape'; + const TAB_KEY = 'Tab'; + const ARROW_UP_KEY = 'ArrowUp'; + const ARROW_DOWN_KEY = 'ArrowDown'; + const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button + + const EVENT_HIDE = `hide${EVENT_KEY}`; + const EVENT_HIDDEN = `hidden${EVENT_KEY}`; + const EVENT_SHOW = `show${EVENT_KEY}`; + const EVENT_SHOWN = `shown${EVENT_KEY}`; + const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`; + const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`; + const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`; + const CLASS_NAME_SHOW = 'show'; + const CLASS_NAME_DROPUP = 'dropup'; + const CLASS_NAME_DROPEND = 'dropend'; + const CLASS_NAME_DROPSTART = 'dropstart'; + const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; + const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; + const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; + const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`; + const SELECTOR_MENU = '.dropdown-menu'; + const SELECTOR_NAVBAR = '.navbar'; + const SELECTOR_NAVBAR_NAV = '.navbar-nav'; + const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; + const PLACEMENT_TOP = index_js.isRTL() ? 'top-end' : 'top-start'; + const PLACEMENT_TOPEND = index_js.isRTL() ? 'top-start' : 'top-end'; + const PLACEMENT_BOTTOM = index_js.isRTL() ? 'bottom-end' : 'bottom-start'; + const PLACEMENT_BOTTOMEND = index_js.isRTL() ? 'bottom-start' : 'bottom-end'; + const PLACEMENT_RIGHT = index_js.isRTL() ? 'left-start' : 'right-start'; + const PLACEMENT_LEFT = index_js.isRTL() ? 'right-start' : 'left-start'; + const PLACEMENT_TOPCENTER = 'top'; + const PLACEMENT_BOTTOMCENTER = 'bottom'; + const Default = { + autoClose: true, + boundary: 'clippingParents', + display: 'dynamic', + offset: [0, 2], + popperConfig: null, + reference: 'toggle' + }; + const DefaultType = { + autoClose: '(boolean|string)', + boundary: '(string|element)', + display: 'string', + offset: '(array|string|function)', + popperConfig: '(null|object|function)', + reference: '(string|element|object)' + }; + + /** + * Class definition + */ + + class Dropdown extends BaseComponent { + constructor(element, config) { + super(element, config); + this._popper = null; + this._parent = this._element.parentNode; // dropdown wrapper + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); + this._inNavbar = this._detectNavbar(); + } + + // Getters + static get Default() { + return Default; + } + static get DefaultType() { + return DefaultType; + } + static get NAME() { + return NAME; + } + + // Public + toggle() { + return this._isShown() ? this.hide() : this.show(); + } + show() { + if (index_js.isDisabled(this._element) || this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget); + if (showEvent.defaultPrevented) { + return; + } + this._createPopper(); + + // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', index_js.noop); + } + } + this._element.focus(); + this._element.setAttribute('aria-expanded', true); + this._menu.classList.add(CLASS_NAME_SHOW); + this._element.classList.add(CLASS_NAME_SHOW); + EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget); + } + hide() { + if (index_js.isDisabled(this._element) || !this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + this._completeHide(relatedTarget); + } + dispose() { + if (this._popper) { + this._popper.destroy(); + } + super.dispose(); + } + update() { + this._inNavbar = this._detectNavbar(); + if (this._popper) { + this._popper.update(); + } + } + + // Private + _completeHide(relatedTarget) { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget); + if (hideEvent.defaultPrevented) { + return; + } + + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', index_js.noop); + } + } + if (this._popper) { + this._popper.destroy(); + } + this._menu.classList.remove(CLASS_NAME_SHOW); + this._element.classList.remove(CLASS_NAME_SHOW); + this._element.setAttribute('aria-expanded', 'false'); + Manipulator.removeDataAttribute(this._menu, 'popper'); + EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget); + } + _getConfig(config) { + config = super._getConfig(config); + if (typeof config.reference === 'object' && !index_js.isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { + // Popper virtual elements require a getBoundingClientRect method + throw new TypeError(`${NAME.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); + } + return config; + } + _createPopper() { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)'); + } + let referenceElement = this._element; + if (this._config.reference === 'parent') { + referenceElement = this._parent; + } else if (index_js.isElement(this._config.reference)) { + referenceElement = index_js.getElement(this._config.reference); + } else if (typeof this._config.reference === 'object') { + referenceElement = this._config.reference; + } + const popperConfig = this._getPopperConfig(); + this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig); + } + _isShown() { + return this._menu.classList.contains(CLASS_NAME_SHOW); + } + _getPlacement() { + const parentDropdown = this._parent; + if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { + return PLACEMENT_RIGHT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { + return PLACEMENT_LEFT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { + return PLACEMENT_TOPCENTER; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { + return PLACEMENT_BOTTOMCENTER; + } + + // We need to trim the value because custom properties can also include spaces + const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { + return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; + } + return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; + } + _detectNavbar() { + return this._element.closest(SELECTOR_NAVBAR) !== null; + } + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } + _getPopperConfig() { + const defaultBsPopperConfig = { + placement: this._getPlacement(), + modifiers: [{ + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, { + name: 'offset', + options: { + offset: this._getOffset() + } + }] + }; + + // Disable Popper if we have a static display or Dropdown is in Navbar + if (this._inNavbar || this._config.display === 'static') { + Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove + defaultBsPopperConfig.modifiers = [{ + name: 'applyStyles', + enabled: false + }]; + } + return { + ...defaultBsPopperConfig, + ...index_js.execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } + _selectMenuItem({ + key, + target + }) { + const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => index_js.isVisible(element)); + if (!items.length) { + return; + } + + // if target isn't included in items (e.g. when expanding the dropdown) + // allow cycling to get the last item in case key equals ARROW_UP_KEY + index_js.getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus(); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Dropdown.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + static clearMenus(event) { + if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY) { + return; + } + const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); + for (const toggle of openToggles) { + const context = Dropdown.getInstance(toggle); + if (!context || context._config.autoClose === false) { + continue; + } + const composedPath = event.composedPath(); + const isMenuTarget = composedPath.includes(context._menu); + if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { + continue; + } + + // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu + if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY || /input|select|option|textarea|form/i.test(event.target.tagName))) { + continue; + } + const relatedTarget = { + relatedTarget: context._element + }; + if (event.type === 'click') { + relatedTarget.clickEvent = event; + } + context._completeHide(relatedTarget); + } + } + static dataApiKeydownHandler(event) { + // If not an UP | DOWN | ESCAPE key => not a dropdown command + // If input/textarea && if key is other than ESCAPE => not a dropdown command + + const isInput = /input|textarea/i.test(event.target.tagName); + const isEscapeEvent = event.key === ESCAPE_KEY; + const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key); + if (!isUpOrDownEvent && !isEscapeEvent) { + return; + } + if (isInput && !isEscapeEvent) { + return; + } + event.preventDefault(); + + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode); + const instance = Dropdown.getOrCreateInstance(getToggleButton); + if (isUpOrDownEvent) { + event.stopPropagation(); + instance.show(); + instance._selectMenuItem(event); + return; + } + if (instance._isShown()) { + // else is escape and we check if it is shown + event.stopPropagation(); + instance.hide(); + getToggleButton.focus(); + } + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus); + EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); + EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { + event.preventDefault(); + Dropdown.getOrCreateInstance(this).toggle(); + }); + + /** + * jQuery + */ + + index_js.defineJQueryPlugin(Dropdown); + + return Dropdown; + +})); +//# sourceMappingURL=dropdown.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/event-handler.js b/nightwatch/desktop/src/js/bootstrap/event-handler.js new file mode 100644 index 0000000..3d9d839 --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/event-handler.js @@ -0,0 +1,237 @@ +/*! + * Bootstrap event-handler.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) : + typeof define === 'function' && define.amd ? define(['../util/index'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.EventHandler = factory(global.Index)); +})(this, (function (index_js) { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/event-handler.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const namespaceRegex = /[^.]*(?=\..*)\.|.*/; + const stripNameRegex = /\..*/; + const stripUidRegex = /::\d+$/; + const eventRegistry = {}; // Events storage + let uidEvent = 1; + const customEvents = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); + + /** + * Private methods + */ + + function makeEventUid(element, uid) { + return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; + } + function getElementEvents(element) { + const uid = makeEventUid(element); + element.uidEvent = uid; + eventRegistry[uid] = eventRegistry[uid] || {}; + return eventRegistry[uid]; + } + function bootstrapHandler(element, fn) { + return function handler(event) { + hydrateObj(event, { + delegateTarget: element + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, fn); + } + return fn.apply(element, [event]); + }; + } + function bootstrapDelegationHandler(element, selector, fn) { + return function handler(event) { + const domElements = element.querySelectorAll(selector); + for (let { + target + } = event; target && target !== this; target = target.parentNode) { + for (const domElement of domElements) { + if (domElement !== target) { + continue; + } + hydrateObj(event, { + delegateTarget: target + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, selector, fn); + } + return fn.apply(target, [event]); + } + } + }; + } + function findHandler(events, callable, delegationSelector = null) { + return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); + } + function normalizeParameters(originalTypeEvent, handler, delegationFunction) { + const isDelegated = typeof handler === 'string'; + // TODO: tooltip passes `false` instead of selector, so we need to check + const callable = isDelegated ? delegationFunction : handler || delegationFunction; + let typeEvent = getTypeEvent(originalTypeEvent); + if (!nativeEvents.has(typeEvent)) { + typeEvent = originalTypeEvent; + } + return [isDelegated, callable, typeEvent]; + } + function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + + // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position + // this prevents the handler from being dispatched the same way as mouseover or mouseout does + if (originalTypeEvent in customEvents) { + const wrapFunction = fn => { + return function (event) { + if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { + return fn.call(this, event); + } + }; + }; + callable = wrapFunction(callable); + } + const events = getElementEvents(element); + const handlers = events[typeEvent] || (events[typeEvent] = {}); + const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); + if (previousFunction) { + previousFunction.oneOff = previousFunction.oneOff && oneOff; + return; + } + const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); + const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); + fn.delegationSelector = isDelegated ? handler : null; + fn.callable = callable; + fn.oneOff = oneOff; + fn.uidEvent = uid; + handlers[uid] = fn; + element.addEventListener(typeEvent, fn, isDelegated); + } + function removeHandler(element, events, typeEvent, handler, delegationSelector) { + const fn = findHandler(events[typeEvent], handler, delegationSelector); + if (!fn) { + return; + } + element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); + delete events[typeEvent][fn.uidEvent]; + } + function removeNamespacedHandlers(element, events, typeEvent, namespace) { + const storeElementEvent = events[typeEvent] || {}; + for (const [handlerKey, event] of Object.entries(storeElementEvent)) { + if (handlerKey.includes(namespace)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + } + function getTypeEvent(event) { + // allow to get the native events from namespaced events ('click.bs.button' --> 'click') + event = event.replace(stripNameRegex, ''); + return customEvents[event] || event; + } + const EventHandler = { + on(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, false); + }, + one(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, true); + }, + off(element, originalTypeEvent, handler, delegationFunction) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + const inNamespace = typeEvent !== originalTypeEvent; + const events = getElementEvents(element); + const storeElementEvent = events[typeEvent] || {}; + const isNamespace = originalTypeEvent.startsWith('.'); + if (typeof callable !== 'undefined') { + // Simplest case: handler is passed, remove that listener ONLY. + if (!Object.keys(storeElementEvent).length) { + return; + } + removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); + return; + } + if (isNamespace) { + for (const elementEvent of Object.keys(events)) { + removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); + } + } + for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { + const handlerKey = keyHandlers.replace(stripUidRegex, ''); + if (!inNamespace || originalTypeEvent.includes(handlerKey)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + }, + trigger(element, event, args) { + if (typeof event !== 'string' || !element) { + return null; + } + const $ = index_js.getjQuery(); + const typeEvent = getTypeEvent(event); + const inNamespace = event !== typeEvent; + let jQueryEvent = null; + let bubbles = true; + let nativeDispatch = true; + let defaultPrevented = false; + if (inNamespace && $) { + jQueryEvent = $.Event(event, args); + $(element).trigger(jQueryEvent); + bubbles = !jQueryEvent.isPropagationStopped(); + nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); + defaultPrevented = jQueryEvent.isDefaultPrevented(); + } + const evt = hydrateObj(new Event(event, { + bubbles, + cancelable: true + }), args); + if (defaultPrevented) { + evt.preventDefault(); + } + if (nativeDispatch) { + element.dispatchEvent(evt); + } + if (evt.defaultPrevented && jQueryEvent) { + jQueryEvent.preventDefault(); + } + return evt; + } + }; + function hydrateObj(obj, meta = {}) { + for (const [key, value] of Object.entries(meta)) { + try { + obj[key] = value; + } catch (_unused) { + Object.defineProperty(obj, key, { + configurable: true, + get() { + return value; + } + }); + } + } + return obj; + } + + return EventHandler; + +})); +//# sourceMappingURL=event-handler.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/index.js b/nightwatch/desktop/src/js/bootstrap/index.js new file mode 100644 index 0000000..f5495d6 --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/index.js @@ -0,0 +1,281 @@ +/*! + * Bootstrap index.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Index = {})); +})(this, (function (exports) { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/index.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const MAX_UID = 1000000; + const MILLISECONDS_MULTIPLIER = 1000; + const TRANSITION_END = 'transitionend'; + + /** + * Properly escape IDs selectors to handle weird IDs + * @param {string} selector + * @returns {string} + */ + const parseSelector = selector => { + if (selector && window.CSS && window.CSS.escape) { + // document.querySelector needs escaping to handle IDs (html5+) containing for instance / + selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); + } + return selector; + }; + + // Shout-out Angus Croll (https://goo.gl/pxwQGp) + const toType = object => { + if (object === null || object === undefined) { + return `${object}`; + } + return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); + }; + + /** + * Public Util API + */ + + const getUID = prefix => { + do { + prefix += Math.floor(Math.random() * MAX_UID); + } while (document.getElementById(prefix)); + return prefix; + }; + const getTransitionDurationFromElement = element => { + if (!element) { + return 0; + } + + // Get transition-duration of the element + let { + transitionDuration, + transitionDelay + } = window.getComputedStyle(element); + const floatTransitionDuration = Number.parseFloat(transitionDuration); + const floatTransitionDelay = Number.parseFloat(transitionDelay); + + // Return 0 if element or transition duration is not found + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } + + // If multiple durations are defined, take the first + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }; + const triggerTransitionEnd = element => { + element.dispatchEvent(new Event(TRANSITION_END)); + }; + const isElement = object => { + if (!object || typeof object !== 'object') { + return false; + } + if (typeof object.jquery !== 'undefined') { + object = object[0]; + } + return typeof object.nodeType !== 'undefined'; + }; + const getElement = object => { + // it's a jQuery object or a node element + if (isElement(object)) { + return object.jquery ? object[0] : object; + } + if (typeof object === 'string' && object.length > 0) { + return document.querySelector(parseSelector(object)); + } + return null; + }; + const isVisible = element => { + if (!isElement(element) || element.getClientRects().length === 0) { + return false; + } + const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; + // Handle `details` element as its content may falsie appear visible when it is closed + const closedDetails = element.closest('details:not([open])'); + if (!closedDetails) { + return elementIsVisible; + } + if (closedDetails !== element) { + const summary = element.closest('summary'); + if (summary && summary.parentNode !== closedDetails) { + return false; + } + if (summary === null) { + return false; + } + } + return elementIsVisible; + }; + const isDisabled = element => { + if (!element || element.nodeType !== Node.ELEMENT_NODE) { + return true; + } + if (element.classList.contains('disabled')) { + return true; + } + if (typeof element.disabled !== 'undefined') { + return element.disabled; + } + return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; + }; + const findShadowRoot = element => { + if (!document.documentElement.attachShadow) { + return null; + } + + // Can find the shadow root otherwise it'll return the document + if (typeof element.getRootNode === 'function') { + const root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + if (element instanceof ShadowRoot) { + return element; + } + + // when we don't find a shadow root + if (!element.parentNode) { + return null; + } + return findShadowRoot(element.parentNode); + }; + const noop = () => {}; + + /** + * Trick to restart an element's animation + * + * @param {HTMLElement} element + * @return void + * + * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation + */ + const reflow = element => { + element.offsetHeight; // eslint-disable-line no-unused-expressions + }; + const getjQuery = () => { + if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + return window.jQuery; + } + return null; + }; + const DOMContentLoadedCallbacks = []; + const onDOMContentLoaded = callback => { + if (document.readyState === 'loading') { + // add listener on the first call when the document is in loading state + if (!DOMContentLoadedCallbacks.length) { + document.addEventListener('DOMContentLoaded', () => { + for (const callback of DOMContentLoadedCallbacks) { + callback(); + } + }); + } + DOMContentLoadedCallbacks.push(callback); + } else { + callback(); + } + }; + const isRTL = () => document.documentElement.dir === 'rtl'; + const defineJQueryPlugin = plugin => { + onDOMContentLoaded(() => { + const $ = getjQuery(); + /* istanbul ignore if */ + if ($) { + const name = plugin.NAME; + const JQUERY_NO_CONFLICT = $.fn[name]; + $.fn[name] = plugin.jQueryInterface; + $.fn[name].Constructor = plugin; + $.fn[name].noConflict = () => { + $.fn[name] = JQUERY_NO_CONFLICT; + return plugin.jQueryInterface; + }; + } + }); + }; + const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { + return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue; + }; + const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { + if (!waitForTransition) { + execute(callback); + return; + } + const durationPadding = 5; + const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; + let called = false; + const handler = ({ + target + }) => { + if (target !== transitionElement) { + return; + } + called = true; + transitionElement.removeEventListener(TRANSITION_END, handler); + execute(callback); + }; + transitionElement.addEventListener(TRANSITION_END, handler); + setTimeout(() => { + if (!called) { + triggerTransitionEnd(transitionElement); + } + }, emulatedDuration); + }; + + /** + * Return the previous/next element of a list. + * + * @param {array} list The list of elements + * @param activeElement The active element + * @param shouldGetNext Choose to get next or previous element + * @param isCycleAllowed + * @return {Element|elem} The proper element + */ + const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { + const listLength = list.length; + let index = list.indexOf(activeElement); + + // if the element does not exist in the list return an element + // depending on the direction and if cycle is allowed + if (index === -1) { + return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; + } + index += shouldGetNext ? 1 : -1; + if (isCycleAllowed) { + index = (index + listLength) % listLength; + } + return list[Math.max(0, Math.min(index, listLength - 1))]; + }; + + exports.defineJQueryPlugin = defineJQueryPlugin; + exports.execute = execute; + exports.executeAfterTransition = executeAfterTransition; + exports.findShadowRoot = findShadowRoot; + exports.getElement = getElement; + exports.getNextActiveElement = getNextActiveElement; + exports.getTransitionDurationFromElement = getTransitionDurationFromElement; + exports.getUID = getUID; + exports.getjQuery = getjQuery; + exports.isDisabled = isDisabled; + exports.isElement = isElement; + exports.isRTL = isRTL; + exports.isVisible = isVisible; + exports.noop = noop; + exports.onDOMContentLoaded = onDOMContentLoaded; + exports.parseSelector = parseSelector; + exports.reflow = reflow; + exports.toType = toType; + exports.triggerTransitionEnd = triggerTransitionEnd; + + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + +})); +//# sourceMappingURL=index.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/manipulator.js b/nightwatch/desktop/src/js/bootstrap/manipulator.js new file mode 100644 index 0000000..b037a6e --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/manipulator.js @@ -0,0 +1,72 @@ +/*! + * Bootstrap manipulator.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Manipulator = factory()); +})(this, (function () { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/manipulator.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + function normalizeData(value) { + if (value === 'true') { + return true; + } + if (value === 'false') { + return false; + } + if (value === Number(value).toString()) { + return Number(value); + } + if (value === '' || value === 'null') { + return null; + } + if (typeof value !== 'string') { + return value; + } + try { + return JSON.parse(decodeURIComponent(value)); + } catch (_unused) { + return value; + } + } + function normalizeDataKey(key) { + return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); + } + const Manipulator = { + setDataAttribute(element, key, value) { + element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); + }, + removeDataAttribute(element, key) { + element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); + }, + getDataAttributes(element) { + if (!element) { + return {}; + } + const attributes = {}; + const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); + for (const key of bsKeys) { + let pureKey = key.replace(/^bs/, ''); + pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); + attributes[pureKey] = normalizeData(element.dataset[key]); + } + return attributes; + }, + getDataAttribute(element, key) { + return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); + } + }; + + return Manipulator; + +})); +//# sourceMappingURL=manipulator.js.map diff --git a/nightwatch/desktop/src/js/bootstrap/selector-engine.js b/nightwatch/desktop/src/js/bootstrap/selector-engine.js new file mode 100644 index 0000000..993a88f --- /dev/null +++ b/nightwatch/desktop/src/js/bootstrap/selector-engine.js @@ -0,0 +1,104 @@ +/*! + * Bootstrap selector-engine.js v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) : + typeof define === 'function' && define.amd ? define(['../util/index'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SelectorEngine = factory(global.Index)); +})(this, (function (index_js) { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/selector-engine.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const getSelector = element => { + let selector = element.getAttribute('data-bs-target'); + if (!selector || selector === '#') { + let hrefAttribute = element.getAttribute('href'); + + // The only valid content that could double as a selector are IDs or classes, + // so everything starting with `#` or `.`. If a "real" URL is used as the selector, + // `document.querySelector` will rightfully complain it is invalid. + // See https://github.com/twbs/bootstrap/issues/32273 + if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { + return null; + } + + // Just in case some CMS puts out a full URL with the anchor appended + if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { + hrefAttribute = `#${hrefAttribute.split('#')[1]}`; + } + selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; + } + return selector ? selector.split(',').map(sel => index_js.parseSelector(sel)).join(',') : null; + }; + const SelectorEngine = { + find(selector, element = document.documentElement) { + return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); + }, + findOne(selector, element = document.documentElement) { + return Element.prototype.querySelector.call(element, selector); + }, + children(element, selector) { + return [].concat(...element.children).filter(child => child.matches(selector)); + }, + parents(element, selector) { + const parents = []; + let ancestor = element.parentNode.closest(selector); + while (ancestor) { + parents.push(ancestor); + ancestor = ancestor.parentNode.closest(selector); + } + return parents; + }, + prev(element, selector) { + let previous = element.previousElementSibling; + while (previous) { + if (previous.matches(selector)) { + return [previous]; + } + previous = previous.previousElementSibling; + } + return []; + }, + // TODO: this is now unused; remove later along with prev() + next(element, selector) { + let next = element.nextElementSibling; + while (next) { + if (next.matches(selector)) { + return [next]; + } + next = next.nextElementSibling; + } + return []; + }, + focusableChildren(element) { + const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); + return this.find(focusables, element).filter(el => !index_js.isDisabled(el) && index_js.isVisible(el)); + }, + getSelectorFromElement(element) { + const selector = getSelector(element); + if (selector) { + return SelectorEngine.findOne(selector) ? selector : null; + } + return null; + }, + getElementFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.findOne(selector) : null; + }, + getMultipleElementsFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.find(selector) : []; + } + }; + + return SelectorEngine; + +})); +//# sourceMappingURL=selector-engine.js.map diff --git a/nightwatch/desktop/src/js/frame.js b/nightwatch/desktop/src/js/frame.js index d753f01..65fcb8a 100644 --- a/nightwatch/desktop/src/js/frame.js +++ b/nightwatch/desktop/src/js/frame.js @@ -1,65 +1,71 @@ // Copyright (c) 2024 iiPython // Handle loading frames -const main = document.querySelector("main"); -async function load_frame(identifier) { - main.innerHTML = ""; - main.append(document.createRange().createContextualFragment( - await (await fetch(`/frames/${identifier}.html`)).text() - )); -} - -async function load_frame_as_modal(identifier) { - open_modal(document.createRange().createContextualFragment( - await (await fetch(`/frames/${identifier}.html`)).text() - )); -} - -// Handle modals -function close_modal() { - const active_modal = document.getElementById("nw-modal"); - if (active_modal) active_modal.remove(); -} -function open_modal(dom) { - let active_modal = document.getElementById("nw-modal"); - if (!active_modal) { - active_modal = document.createElement("div"); - parent = document.createElement("div"); - parent.id = "nw-modal"; - parent.appendChild(active_modal); - document.body.append(parent); +class NightwatchFrameHandler { + constructor() { + this.main = document.querySelector("main"); + } + async load(id) { + this.main.innerHTML = ""; + this.main.append(document.createRange().createContextualFragment( + await (await fetch(`/frames/${id}.html`)).text() + )); + } + async load_as_modal(id) { + this.open_modal(document.createRange().createContextualFragment( + await (await fetch(`/frames/${id}.html`)).text() + )); + } - // Handle click to close - active_modal.addEventListener("click", (e) => e.stopPropagation()); - parent.addEventListener("click", () => parent.remove()); - }; - active_modal.innerHTML = ""; - active_modal.append(dom); -} + // Handle modals + close_modal() { + const active_modal = document.getElementById("nw-modal"); + if (active_modal) active_modal.remove(); + } + open_modal(dom) { + let active_modal = document.getElementById("nw-modal"); + if (!active_modal) { + active_modal = document.createElement("div"); + parent = document.createElement("div"); + parent.id = "nw-modal"; + parent.appendChild(active_modal); + document.body.append(parent); + + // Handle click to close + active_modal.addEventListener("click", (e) => e.stopPropagation()); + parent.addEventListener("click", () => parent.remove()); + }; + active_modal.innerHTML = ""; + active_modal.append(dom); + } -// Handle tooltips -function remove_tooltip() { - const tooltip = document.getElementById("tooltip"); - if (tooltip) tooltip.remove(); - if (window.popper) { - window.popper.destroy(); - delete window.popper; + // Handle tooltips + remove_tooltip() { + const tooltip = document.getElementById("tooltip"); + if (tooltip) tooltip.remove(); + if (this.popper) { + this.popper.destroy(); + delete this.popper; + } + } + create_tooltip(element, text) { + this.remove_tooltip(); + const tooltip = document.createElement("div"); + tooltip.innerHTML = `

${text}

`; + tooltip.id = "tooltip"; + tooltip.classList.add("nw-tooltip") + document.body.appendChild(tooltip); + this.popper = Popper.createPopper(element, tooltip, { + modifiers: [ + { + name: "offset", + options: { offset: [ 0, 8 ] } + } + ], + placement: "bottom" + }); } } -function create_tooltip(element, text) { - remove_tooltip(); - const tooltip = document.createElement("div"); - tooltip.innerHTML = `

${text}

`; - tooltip.id = "tooltip"; - tooltip.classList.add("nw-tooltip") - document.body.appendChild(tooltip); - window.popper = Popper.createPopper(element, tooltip, { - modifiers: [ - { - name: "offset", - options: { offset: [ 0, 8 ] } - } - ], - placement: "bottom" - }); -} + +// Exporting +nightwatch.frame = new NightwatchFrameHandler(); diff --git a/nightwatch/desktop/src/js/frames/auth.js b/nightwatch/desktop/src/js/frames/auth.js index 1ab5496..03df9b1 100644 --- a/nightwatch/desktop/src/js/frames/auth.js +++ b/nightwatch/desktop/src/js/frames/auth.js @@ -1,22 +1,27 @@ (() => { + + // Importing + const { auth, frame } = nightwatch; + + // Handle form submission const form = document.querySelector("form"), button = document.querySelector("button[type = submit]"); form.addEventListener("submit", async (e) => { e.preventDefault(); button.innerHTML = `
`; // Handle authentication - const response = await nightwatch[form.getAttribute("data-nightwatch-action")]( + const response = await auth[form.getAttribute("data-nightwatch-action")]( document.getElementById("usernameInput").value, document.getElementById("passwordInput").value ) if (response.code !== 200) { button.innerText = "Continue"; - return notifier.alert(response.data); + return console.error(response.data); } - await nightwatch.authenticate(); - if (nightwatch._error) return load_frame(`errors/${nightwatch._error.type}`); - load_frame("welcome"); + await auth.authenticate(); + if (auth._error) return frame.load(`errors/${auth._error.type}`); + frame.load("welcome"); }); - document.getElementById("authServer").innerText = nightwatch.auth_server; + document.getElementById("authServer").innerText = auth.server; })(); diff --git a/nightwatch/desktop/src/js/lib.bundle.js b/nightwatch/desktop/src/js/lib.bundle.js index 12cb07a..4421249 100644 --- a/nightwatch/desktop/src/js/lib.bundle.js +++ b/nightwatch/desktop/src/js/lib.bundle.js @@ -1,6 +1,3 @@ -// https://github.com/f3oall/awesome-notifications/blob/master/dist/modern.var.js -var AWN;(()=>{var e={628:(e,t,s)=>{"use strict";s.d(t,{default:()=>m});const i={maxNotifications:10,animationDuration:300,position:"bottom-right",labels:{tip:"Tip",info:"Info",success:"Success",warning:"Attention",alert:"Error",async:"Loading",confirm:"Confirmation required",confirmOk:"OK",confirmCancel:"Cancel"},icons:{tip:"question-circle",info:"info-circle",success:"check-circle",warning:"exclamation-circle",alert:"exclamation-triangle",async:"cog fa-spin",confirm:"exclamation-triangle",prefix:"",enabled:!0},replacements:{tip:null,info:null,success:null,warning:null,alert:null,async:null,"async-block":null,modal:null,confirm:null,general:{"