@dbp-toolkit/check-in-place-select
Version:
You can install this component via npm:
1,565 lines (1,477 loc) • 61.6 kB
JavaScript
import { A as AdapterLitElement, c as createInstance$1, J as JSONLD, a as directive, N as NodePart, i as isPrimitive, S as ScopedElementsMixin, M as MiniSpinner, g as getThemeCSS, b as css, h as html, C as CheckInPlaceSelect, e as getGeneralCSS, d as defineCustomElement } from './shared/check-in-place-select.b9181cbe.es.js';
class DBPLitElement extends AdapterLitElement {
_(selector) {
return this.shadowRoot === null ? this.querySelector(selector) : this.shadowRoot.querySelector(selector);
}
}
var login$1 = "Einloggen";
var logout$1 = "Ausloggen";
var de = {
login: login$1,
logout: logout$1
};
var login = "Login";
var logout = "Logout";
var en = {
login: login,
logout: logout
};
function createInstance() {
return createInstance$1({en: en, de: de}, 'de', 'en', 'dbp-auth');
}
/**
* Assert a condition.
* @param condition The condition that it should satisfy.
* @param message The error message.
* @param args The arguments for replacing placeholders in the message.
*/
function assertType(condition, message, ...args) {
if (!condition) {
throw new TypeError(format(message, args));
}
}
/**
* Convert a text and arguments to one string.
* @param message The formating text
* @param args The arguments.
*/
function format(message, args) {
let i = 0;
return message.replace(/%[os]/gu, () => anyToString(args[i++]));
}
/**
* Convert a value to a string representation.
* @param x The value to get the string representation.
*/
function anyToString(x) {
if (typeof x !== "object" || x === null) {
return String(x);
}
return Object.prototype.toString.call(x);
}
let currentErrorHandler;
/**
* Print a error message.
* @param maybeError The error object.
*/
function reportError(maybeError) {
try {
const error = maybeError instanceof Error
? maybeError
: new Error(anyToString(maybeError));
// Call the user-defined error handler if exists.
if (currentErrorHandler) ;
// Dispatch an `error` event if this is on a browser.
if (typeof dispatchEvent === "function" &&
typeof ErrorEvent === "function") {
dispatchEvent(new ErrorEvent("error", { error, message: error.message }));
}
// Emit an `uncaughtException` event if this is on Node.js.
//istanbul ignore else
else if (typeof process !== "undefined" &&
typeof process.emit === "function") {
process.emit("uncaughtException", error);
return;
}
// Otherwise, print the error.
console.error(error);
}
catch (_a) {
// ignore.
}
}
/**
* The global object.
*/
//istanbul ignore next
const Global = typeof window !== "undefined"
? window
: typeof self !== "undefined"
? self
: typeof global !== "undefined"
? global
: typeof globalThis !== "undefined"
? globalThis
: undefined;
let currentWarnHandler;
/**
* The warning information.
*/
class Warning {
constructor(code, message) {
this.code = code;
this.message = message;
}
/**
* Report this warning.
* @param args The arguments of the warning.
*/
warn(...args) {
var _a;
try {
// Call the user-defined warning handler if exists.
if (currentWarnHandler) ;
// Otherwise, print the warning.
const stack = ((_a = new Error().stack) !== null && _a !== void 0 ? _a : "").replace(/^(?:.+?\n){2}/gu, "\n");
console.warn(this.message, ...args, stack);
}
catch (_b) {
// Ignore.
}
}
}
const InitEventWasCalledWhileDispatching = new Warning("W01", "Unable to initialize event under dispatching.");
const FalsyWasAssignedToCancelBubble = new Warning("W02", "Assigning any falsy value to 'cancelBubble' property has no effect.");
const TruthyWasAssignedToReturnValue = new Warning("W03", "Assigning any truthy value to 'returnValue' property has no effect.");
const NonCancelableEventWasCanceled = new Warning("W04", "Unable to preventDefault on non-cancelable events.");
const CanceledInPassiveListener = new Warning("W05", "Unable to preventDefault inside passive event listener invocation.");
const EventListenerWasDuplicated = new Warning("W06", "An event listener wasn't added because it has been added already: %o, %o");
const OptionWasIgnored = new Warning("W07", "The %o option value was abandoned because the event listener wasn't added as duplicated.");
const InvalidEventListener = new Warning("W08", "The 'callback' argument must be a function or an object that has 'handleEvent' method: %o");
new Warning("W09", "Event attribute handler must be a function: %o");
/*eslint-disable class-methods-use-this */
/**
* An implementation of `Event` interface, that wraps a given event object.
* `EventTarget` shim can control the internal state of this `Event` objects.
* @see https://dom.spec.whatwg.org/#event
*/
class Event {
/**
* @see https://dom.spec.whatwg.org/#dom-event-none
*/
static get NONE() {
return NONE;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-capturing_phase
*/
static get CAPTURING_PHASE() {
return CAPTURING_PHASE;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-at_target
*/
static get AT_TARGET() {
return AT_TARGET;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-bubbling_phase
*/
static get BUBBLING_PHASE() {
return BUBBLING_PHASE;
}
/**
* Initialize this event instance.
* @param type The type of this event.
* @param eventInitDict Options to initialize.
* @see https://dom.spec.whatwg.org/#dom-event-event
*/
constructor(type, eventInitDict) {
Object.defineProperty(this, "isTrusted", {
value: false,
enumerable: true,
});
const opts = eventInitDict !== null && eventInitDict !== void 0 ? eventInitDict : {};
internalDataMap.set(this, {
type: String(type),
bubbles: Boolean(opts.bubbles),
cancelable: Boolean(opts.cancelable),
composed: Boolean(opts.composed),
target: null,
currentTarget: null,
stopPropagationFlag: false,
stopImmediatePropagationFlag: false,
canceledFlag: false,
inPassiveListenerFlag: false,
dispatchFlag: false,
timeStamp: Date.now(),
});
}
/**
* The type of this event.
* @see https://dom.spec.whatwg.org/#dom-event-type
*/
get type() {
return $(this).type;
}
/**
* The event target of the current dispatching.
* @see https://dom.spec.whatwg.org/#dom-event-target
*/
get target() {
return $(this).target;
}
/**
* The event target of the current dispatching.
* @deprecated Use the `target` property instead.
* @see https://dom.spec.whatwg.org/#dom-event-srcelement
*/
get srcElement() {
return $(this).target;
}
/**
* The event target of the current dispatching.
* @see https://dom.spec.whatwg.org/#dom-event-currenttarget
*/
get currentTarget() {
return $(this).currentTarget;
}
/**
* The event target of the current dispatching.
* This doesn't support node tree.
* @see https://dom.spec.whatwg.org/#dom-event-composedpath
*/
composedPath() {
const currentTarget = $(this).currentTarget;
if (currentTarget) {
return [currentTarget];
}
return [];
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-none
*/
get NONE() {
return NONE;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-capturing_phase
*/
get CAPTURING_PHASE() {
return CAPTURING_PHASE;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-at_target
*/
get AT_TARGET() {
return AT_TARGET;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-bubbling_phase
*/
get BUBBLING_PHASE() {
return BUBBLING_PHASE;
}
/**
* The current event phase.
* @see https://dom.spec.whatwg.org/#dom-event-eventphase
*/
get eventPhase() {
return $(this).dispatchFlag ? 2 : 0;
}
/**
* Stop event bubbling.
* Because this shim doesn't support node tree, this merely changes the `cancelBubble` property value.
* @see https://dom.spec.whatwg.org/#dom-event-stoppropagation
*/
stopPropagation() {
$(this).stopPropagationFlag = true;
}
/**
* `true` if event bubbling was stopped.
* @deprecated
* @see https://dom.spec.whatwg.org/#dom-event-cancelbubble
*/
get cancelBubble() {
return $(this).stopPropagationFlag;
}
/**
* Stop event bubbling if `true` is set.
* @deprecated Use the `stopPropagation()` method instead.
* @see https://dom.spec.whatwg.org/#dom-event-cancelbubble
*/
set cancelBubble(value) {
if (value) {
$(this).stopPropagationFlag = true;
}
else {
FalsyWasAssignedToCancelBubble.warn();
}
}
/**
* Stop event bubbling and subsequent event listener callings.
* @see https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation
*/
stopImmediatePropagation() {
const data = $(this);
data.stopPropagationFlag = data.stopImmediatePropagationFlag = true;
}
/**
* `true` if this event will bubble.
* @see https://dom.spec.whatwg.org/#dom-event-bubbles
*/
get bubbles() {
return $(this).bubbles;
}
/**
* `true` if this event can be canceled by the `preventDefault()` method.
* @see https://dom.spec.whatwg.org/#dom-event-cancelable
*/
get cancelable() {
return $(this).cancelable;
}
/**
* `true` if the default behavior will act.
* @deprecated Use the `defaultPrevented` proeprty instead.
* @see https://dom.spec.whatwg.org/#dom-event-returnvalue
*/
get returnValue() {
return !$(this).canceledFlag;
}
/**
* Cancel the default behavior if `false` is set.
* @deprecated Use the `preventDefault()` method instead.
* @see https://dom.spec.whatwg.org/#dom-event-returnvalue
*/
set returnValue(value) {
if (!value) {
setCancelFlag($(this));
}
else {
TruthyWasAssignedToReturnValue.warn();
}
}
/**
* Cancel the default behavior.
* @see https://dom.spec.whatwg.org/#dom-event-preventdefault
*/
preventDefault() {
setCancelFlag($(this));
}
/**
* `true` if the default behavior was canceled.
* @see https://dom.spec.whatwg.org/#dom-event-defaultprevented
*/
get defaultPrevented() {
return $(this).canceledFlag;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-composed
*/
get composed() {
return $(this).composed;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-istrusted
*/
//istanbul ignore next
get isTrusted() {
return false;
}
/**
* @see https://dom.spec.whatwg.org/#dom-event-timestamp
*/
get timeStamp() {
return $(this).timeStamp;
}
/**
* @deprecated Don't use this method. The constructor did initialization.
*/
initEvent(type, bubbles = false, cancelable = false) {
const data = $(this);
if (data.dispatchFlag) {
InitEventWasCalledWhileDispatching.warn();
return;
}
internalDataMap.set(this, {
...data,
type: String(type),
bubbles: Boolean(bubbles),
cancelable: Boolean(cancelable),
target: null,
currentTarget: null,
stopPropagationFlag: false,
stopImmediatePropagationFlag: false,
canceledFlag: false,
});
}
}
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const NONE = 0;
const CAPTURING_PHASE = 1;
const AT_TARGET = 2;
const BUBBLING_PHASE = 3;
/**
* Private data for event wrappers.
*/
const internalDataMap = new WeakMap();
/**
* Get private data.
* @param event The event object to get private data.
* @param name The variable name to report.
* @returns The private data of the event.
*/
function $(event, name = "this") {
const retv = internalDataMap.get(event);
assertType(retv != null, "'%s' must be an object that Event constructor created, but got another one: %o", name, event);
return retv;
}
/**
* https://dom.spec.whatwg.org/#set-the-canceled-flag
* @param data private data.
*/
function setCancelFlag(data) {
if (data.inPassiveListenerFlag) {
CanceledInPassiveListener.warn();
return;
}
if (!data.cancelable) {
NonCancelableEventWasCanceled.warn();
return;
}
data.canceledFlag = true;
}
// Set enumerable
Object.defineProperty(Event, "NONE", { enumerable: true });
Object.defineProperty(Event, "CAPTURING_PHASE", { enumerable: true });
Object.defineProperty(Event, "AT_TARGET", { enumerable: true });
Object.defineProperty(Event, "BUBBLING_PHASE", { enumerable: true });
const keys = Object.getOwnPropertyNames(Event.prototype);
for (let i = 0; i < keys.length; ++i) {
if (keys[i] === "constructor") {
continue;
}
Object.defineProperty(Event.prototype, keys[i], { enumerable: true });
}
// Ensure `event instanceof window.Event` is `true`.
if (typeof Global !== "undefined" && typeof Global.Event !== "undefined") {
Object.setPrototypeOf(Event.prototype, Global.Event.prototype);
}
/**
* Create a new InvalidStateError instance.
* @param message The error message.
*/
function createInvalidStateError(message) {
if (Global.DOMException) {
return new Global.DOMException(message, "InvalidStateError");
}
if (DOMException == null) {
DOMException = class DOMException extends Error {
constructor(msg) {
super(msg);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DOMException);
}
}
// eslint-disable-next-line class-methods-use-this
get code() {
return 11;
}
// eslint-disable-next-line class-methods-use-this
get name() {
return "InvalidStateError";
}
};
Object.defineProperties(DOMException.prototype, {
code: { enumerable: true },
name: { enumerable: true },
});
defineErrorCodeProperties(DOMException);
defineErrorCodeProperties(DOMException.prototype);
}
return new DOMException(message);
}
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
let DOMException;
const ErrorCodeMap = {
INDEX_SIZE_ERR: 1,
DOMSTRING_SIZE_ERR: 2,
HIERARCHY_REQUEST_ERR: 3,
WRONG_DOCUMENT_ERR: 4,
INVALID_CHARACTER_ERR: 5,
NO_DATA_ALLOWED_ERR: 6,
NO_MODIFICATION_ALLOWED_ERR: 7,
NOT_FOUND_ERR: 8,
NOT_SUPPORTED_ERR: 9,
INUSE_ATTRIBUTE_ERR: 10,
INVALID_STATE_ERR: 11,
SYNTAX_ERR: 12,
INVALID_MODIFICATION_ERR: 13,
NAMESPACE_ERR: 14,
INVALID_ACCESS_ERR: 15,
VALIDATION_ERR: 16,
TYPE_MISMATCH_ERR: 17,
SECURITY_ERR: 18,
NETWORK_ERR: 19,
ABORT_ERR: 20,
URL_MISMATCH_ERR: 21,
QUOTA_EXCEEDED_ERR: 22,
TIMEOUT_ERR: 23,
INVALID_NODE_TYPE_ERR: 24,
DATA_CLONE_ERR: 25,
};
function defineErrorCodeProperties(obj) {
const keys = Object.keys(ErrorCodeMap);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
const value = ErrorCodeMap[key];
Object.defineProperty(obj, key, {
get() {
return value;
},
configurable: true,
enumerable: true,
});
}
}
/**
* An implementation of `Event` interface, that wraps a given event object.
* This class controls the internal state of `Event`.
* @see https://dom.spec.whatwg.org/#interface-event
*/
class EventWrapper extends Event {
/**
* Wrap a given event object to control states.
* @param event The event-like object to wrap.
*/
static wrap(event) {
return new (getWrapperClassOf(event))(event);
}
constructor(event) {
super(event.type, {
bubbles: event.bubbles,
cancelable: event.cancelable,
composed: event.composed,
});
if (event.cancelBubble) {
super.stopPropagation();
}
if (event.defaultPrevented) {
super.preventDefault();
}
internalDataMap$1.set(this, { original: event });
// Define accessors
const keys = Object.keys(event);
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
if (!(key in this)) {
Object.defineProperty(this, key, defineRedirectDescriptor(event, key));
}
}
}
stopPropagation() {
super.stopPropagation();
const { original } = $$1(this);
if ("stopPropagation" in original) {
original.stopPropagation();
}
}
get cancelBubble() {
return super.cancelBubble;
}
set cancelBubble(value) {
super.cancelBubble = value;
const { original } = $$1(this);
if ("cancelBubble" in original) {
original.cancelBubble = value;
}
}
stopImmediatePropagation() {
super.stopImmediatePropagation();
const { original } = $$1(this);
if ("stopImmediatePropagation" in original) {
original.stopImmediatePropagation();
}
}
get returnValue() {
return super.returnValue;
}
set returnValue(value) {
super.returnValue = value;
const { original } = $$1(this);
if ("returnValue" in original) {
original.returnValue = value;
}
}
preventDefault() {
super.preventDefault();
const { original } = $$1(this);
if ("preventDefault" in original) {
original.preventDefault();
}
}
get timeStamp() {
const { original } = $$1(this);
if ("timeStamp" in original) {
return original.timeStamp;
}
return super.timeStamp;
}
}
/**
* Private data for event wrappers.
*/
const internalDataMap$1 = new WeakMap();
/**
* Get private data.
* @param event The event object to get private data.
* @returns The private data of the event.
*/
function $$1(event) {
const retv = internalDataMap$1.get(event);
assertType(retv != null, "'this' is expected an Event object, but got", event);
return retv;
}
/**
* Cache for wrapper classes.
* @type {WeakMap<Object, Function>}
* @private
*/
const wrapperClassCache = new WeakMap();
// Make association for wrappers.
wrapperClassCache.set(Object.prototype, EventWrapper);
if (typeof Global !== "undefined" && typeof Global.Event !== "undefined") {
wrapperClassCache.set(Global.Event.prototype, EventWrapper);
}
/**
* Get the wrapper class of a given prototype.
* @param originalEvent The event object to wrap.
*/
function getWrapperClassOf(originalEvent) {
const prototype = Object.getPrototypeOf(originalEvent);
if (prototype == null) {
return EventWrapper;
}
let wrapper = wrapperClassCache.get(prototype);
if (wrapper == null) {
wrapper = defineWrapper(getWrapperClassOf(prototype), prototype);
wrapperClassCache.set(prototype, wrapper);
}
return wrapper;
}
/**
* Define new wrapper class.
* @param BaseEventWrapper The base wrapper class.
* @param originalPrototype The prototype of the original event.
*/
function defineWrapper(BaseEventWrapper, originalPrototype) {
class CustomEventWrapper extends BaseEventWrapper {
}
const keys = Object.keys(originalPrototype);
for (let i = 0; i < keys.length; ++i) {
Object.defineProperty(CustomEventWrapper.prototype, keys[i], defineRedirectDescriptor(originalPrototype, keys[i]));
}
return CustomEventWrapper;
}
/**
* Get the property descriptor to redirect a given property.
*/
function defineRedirectDescriptor(obj, key) {
const d = Object.getOwnPropertyDescriptor(obj, key);
return {
get() {
const original = $$1(this).original;
const value = original[key];
if (typeof value === "function") {
return value.bind(original);
}
return value;
},
set(value) {
const original = $$1(this).original;
original[key] = value;
},
configurable: d.configurable,
enumerable: d.enumerable,
};
}
/**
* Create a new listener.
* @param callback The callback function.
* @param capture The capture flag.
* @param passive The passive flag.
* @param once The once flag.
* @param signal The abort signal.
* @param signalListener The abort event listener for the abort signal.
*/
function createListener(callback, capture, passive, once, signal, signalListener) {
return {
callback,
flags: (capture ? 1 /* Capture */ : 0) |
(passive ? 2 /* Passive */ : 0) |
(once ? 4 /* Once */ : 0),
signal,
signalListener,
};
}
/**
* Set the `removed` flag to the given listener.
* @param listener The listener to check.
*/
function setRemoved(listener) {
listener.flags |= 8 /* Removed */;
}
/**
* Check if the given listener has the `capture` flag or not.
* @param listener The listener to check.
*/
function isCapture(listener) {
return (listener.flags & 1 /* Capture */) === 1 /* Capture */;
}
/**
* Check if the given listener has the `passive` flag or not.
* @param listener The listener to check.
*/
function isPassive(listener) {
return (listener.flags & 2 /* Passive */) === 2 /* Passive */;
}
/**
* Check if the given listener has the `once` flag or not.
* @param listener The listener to check.
*/
function isOnce(listener) {
return (listener.flags & 4 /* Once */) === 4 /* Once */;
}
/**
* Check if the given listener has the `removed` flag or not.
* @param listener The listener to check.
*/
function isRemoved(listener) {
return (listener.flags & 8 /* Removed */) === 8 /* Removed */;
}
/**
* Call an event listener.
* @param listener The listener to call.
* @param target The event target object for `thisArg`.
* @param event The event object for the first argument.
* @param attribute `true` if this callback is an event attribute handler.
*/
function invokeCallback({ callback }, target, event) {
try {
if (typeof callback === "function") {
callback.call(target, event);
}
else if (typeof callback.handleEvent === "function") {
callback.handleEvent(event);
}
}
catch (thrownError) {
reportError(thrownError);
}
}
/**
* Find the index of given listener.
* This returns `-1` if not found.
* @param list The listener list.
* @param callback The callback function to find.
* @param capture The capture flag to find.
*/
function findIndexOfListener({ listeners }, callback, capture) {
for (let i = 0; i < listeners.length; ++i) {
if (listeners[i].callback === callback &&
isCapture(listeners[i]) === capture) {
return i;
}
}
return -1;
}
/**
* Add the given listener.
* Does copy-on-write if needed.
* @param list The listener list.
* @param callback The callback function.
* @param capture The capture flag.
* @param passive The passive flag.
* @param once The once flag.
* @param signal The abort signal.
*/
function addListener(list, callback, capture, passive, once, signal) {
let signalListener;
if (signal) {
signalListener = removeListener.bind(null, list, callback, capture);
signal.addEventListener("abort", signalListener);
}
const listener = createListener(callback, capture, passive, once, signal, signalListener);
if (list.cow) {
list.cow = false;
list.listeners = [...list.listeners, listener];
}
else {
list.listeners.push(listener);
}
return listener;
}
/**
* Remove a listener.
* @param list The listener list.
* @param callback The callback function to find.
* @param capture The capture flag to find.
* @returns `true` if it mutated the list directly.
*/
function removeListener(list, callback, capture) {
const index = findIndexOfListener(list, callback, capture);
if (index !== -1) {
return removeListenerAt(list, index);
}
return false;
}
/**
* Remove a listener.
* @param list The listener list.
* @param index The index of the target listener.
* @param disableCow Disable copy-on-write if true.
* @returns `true` if it mutated the `listeners` array directly.
*/
function removeListenerAt(list, index, disableCow = false) {
const listener = list.listeners[index];
// Set the removed flag.
setRemoved(listener);
// Dispose the abort signal listener if exists.
if (listener.signal) {
listener.signal.removeEventListener("abort", listener.signalListener);
}
// Remove it from the array.
if (list.cow && !disableCow) {
list.cow = false;
list.listeners = list.listeners.filter((_, i) => i !== index);
return false;
}
list.listeners.splice(index, 1);
return true;
}
/**
* Create a new `ListenerListMap` object.
*/
function createListenerListMap() {
return Object.create(null);
}
/**
* Get the listener list of the given type.
* If the listener list has not been initialized, initialize and return it.
* @param listenerMap The listener list map.
* @param type The event type to get.
*/
function ensureListenerList(listenerMap, type) {
var _a;
return ((_a = listenerMap[type]) !== null && _a !== void 0 ? _a : (listenerMap[type] = {
attrCallback: undefined,
attrListener: undefined,
cow: false,
listeners: [],
}));
}
/**
* An implementation of the `EventTarget` interface.
* @see https://dom.spec.whatwg.org/#eventtarget
*/
class EventTarget {
/**
* Initialize this instance.
*/
constructor() {
internalDataMap$2.set(this, createListenerListMap());
}
// Implementation
addEventListener(type0, callback0, options0) {
const listenerMap = $$2(this);
const { callback, capture, once, passive, signal, type, } = normalizeAddOptions(type0, callback0, options0);
if (callback == null || (signal === null || signal === void 0 ? void 0 : signal.aborted)) {
return;
}
const list = ensureListenerList(listenerMap, type);
// Find existing listener.
const i = findIndexOfListener(list, callback, capture);
if (i !== -1) {
warnDuplicate(list.listeners[i], passive, once, signal);
return;
}
// Add the new listener.
addListener(list, callback, capture, passive, once, signal);
}
// Implementation
removeEventListener(type0, callback0, options0) {
const listenerMap = $$2(this);
const { callback, capture, type } = normalizeOptions(type0, callback0, options0);
const list = listenerMap[type];
if (callback != null && list) {
removeListener(list, callback, capture);
}
}
// Implementation
dispatchEvent(e) {
const list = $$2(this)[String(e.type)];
if (list == null) {
return true;
}
const event = e instanceof Event ? e : EventWrapper.wrap(e);
const eventData = $(event, "event");
if (eventData.dispatchFlag) {
throw createInvalidStateError("This event has been in dispatching.");
}
eventData.dispatchFlag = true;
eventData.target = eventData.currentTarget = this;
if (!eventData.stopPropagationFlag) {
const { cow, listeners } = list;
// Set copy-on-write flag.
list.cow = true;
// Call listeners.
for (let i = 0; i < listeners.length; ++i) {
const listener = listeners[i];
// Skip if removed.
if (isRemoved(listener)) {
continue;
}
// Remove this listener if has the `once` flag.
if (isOnce(listener) && removeListenerAt(list, i, !cow)) {
// Because this listener was removed, the next index is the
// same as the current value.
i -= 1;
}
// Call this listener with the `passive` flag.
eventData.inPassiveListenerFlag = isPassive(listener);
invokeCallback(listener, this, event);
eventData.inPassiveListenerFlag = false;
// Stop if the `event.stopImmediatePropagation()` method was called.
if (eventData.stopImmediatePropagationFlag) {
break;
}
}
// Restore copy-on-write flag.
if (!cow) {
list.cow = false;
}
}
eventData.target = null;
eventData.currentTarget = null;
eventData.stopImmediatePropagationFlag = false;
eventData.stopPropagationFlag = false;
eventData.dispatchFlag = false;
return !eventData.canceledFlag;
}
}
/**
* Internal data.
*/
const internalDataMap$2 = new WeakMap();
/**
* Get private data.
* @param target The event target object to get private data.
* @param name The variable name to report.
* @returns The private data of the event.
*/
function $$2(target, name = "this") {
const retv = internalDataMap$2.get(target);
assertType(retv != null, "'%s' must be an object that EventTarget constructor created, but got another one: %o", name, target);
return retv;
}
/**
* Normalize options.
* @param options The options to normalize.
*/
function normalizeAddOptions(type, callback, options) {
var _a;
assertCallback(callback);
if (typeof options === "object" && options !== null) {
return {
type: String(type),
callback: callback !== null && callback !== void 0 ? callback : undefined,
capture: Boolean(options.capture),
passive: Boolean(options.passive),
once: Boolean(options.once),
signal: (_a = options.signal) !== null && _a !== void 0 ? _a : undefined,
};
}
return {
type: String(type),
callback: callback !== null && callback !== void 0 ? callback : undefined,
capture: Boolean(options),
passive: false,
once: false,
signal: undefined,
};
}
/**
* Normalize options.
* @param options The options to normalize.
*/
function normalizeOptions(type, callback, options) {
assertCallback(callback);
if (typeof options === "object" && options !== null) {
return {
type: String(type),
callback: callback !== null && callback !== void 0 ? callback : undefined,
capture: Boolean(options.capture),
};
}
return {
type: String(type),
callback: callback !== null && callback !== void 0 ? callback : undefined,
capture: Boolean(options),
};
}
/**
* Assert the type of 'callback' argument.
* @param callback The callback to check.
*/
function assertCallback(callback) {
if (typeof callback === "function" ||
(typeof callback === "object" &&
callback !== null &&
typeof callback.handleEvent === "function")) {
return;
}
if (callback == null || typeof callback === "object") {
InvalidEventListener.warn(callback);
return;
}
throw new TypeError(format(InvalidEventListener.message, [callback]));
}
/**
* Print warning for duplicated.
* @param listener The current listener that is duplicated.
* @param passive The passive flag of the new duplicated listener.
* @param once The once flag of the new duplicated listener.
* @param signal The signal object of the new duplicated listener.
*/
function warnDuplicate(listener, passive, once, signal) {
EventListenerWasDuplicated.warn(isCapture(listener) ? "capture" : "bubble", listener.callback);
if (isPassive(listener) !== passive) {
OptionWasIgnored.warn("passive");
}
if (isOnce(listener) !== once) {
OptionWasIgnored.warn("once");
}
if (listener.signal !== signal) {
OptionWasIgnored.warn("signal");
}
}
// Set enumerable
const keys$1 = Object.getOwnPropertyNames(EventTarget.prototype);
for (let i = 0; i < keys$1.length; ++i) {
if (keys$1[i] === "constructor") {
continue;
}
Object.defineProperty(EventTarget.prototype, keys$1[i], { enumerable: true });
}
// Ensure `eventTarget instanceof window.EventTarget` is `true`.
if (typeof Global !== "undefined" &&
typeof Global.EventTarget !== "undefined") {
Object.setPrototypeOf(EventTarget.prototype, Global.EventTarget.prototype);
}
/**
* Imports the keycloak JS API as if it was a module.
*
* @param baseUrl {string}
*/
async function importKeycloak(baseUrl) {
const keycloakSrc = baseUrl + '/js/keycloak.min.js';
// Importing will write it to window so we take it from there
await import(keycloakSrc);
if (importKeycloak._keycloakMod !== undefined)
return importKeycloak._keycloakMod;
importKeycloak._keycloakMod = window.Keycloak;
delete window.Keycloak;
return importKeycloak._keycloakMod;
}
const promiseTimeout = function(ms, promise) {
let timeout = new Promise((resolve, reject) => {
let id = setTimeout(() => {
clearTimeout(id);
reject('Timed out in '+ ms + 'ms.');
}, ms);
});
return Promise.race([
promise,
timeout
]);
};
/**
* Returns a URL for a relative path or URL
*
* @param {string} urlOrPath
*/
const ensureURL = function(urlOrPath) {
try {
return new URL(urlOrPath).href;
} catch (e) {
return new URL(urlOrPath, window.location.href).href;
}
};
/**
* Wraps the keycloak API to support async/await, adds auto token refreshing and consolidates all
* events into one native "changed" event
*
* The "changed" event has the real keycloak instance as "detail"
*/
class KeycloakWrapper extends EventTarget {
constructor(baseURL, realm, clientId, silentCheckSsoUri, idpHint) {
super();
this._baseURL = baseURL;
this._realm = realm;
this._clientId = clientId;
this._keycloak = null;
this._initDone = false;
this._silentCheckSsoUri = silentCheckSsoUri;
this._idpHint = idpHint;
this._checkId = null;
/* Minimum validity of the token in seconds */
this.MIN_VALIDITY = 20;
/* Interval at which the token validity is checked, in seconds */
this.CHECK_INTERVAL = 10;
/* Enables extra debug logging */
this.DEBUG = false;
this._onVisibilityChanged = this._onVisibilityChanged.bind(this);
document.addEventListener("visibilitychange", this._onVisibilityChanged);
}
/**
* This needs to be called or the instance will leak;
*/
close() {
document.removeEventListener("visibilitychange", this._onVisibilityChanged);
}
_onVisibilityChanged() {
let isVisible = (document.visibilityState === 'visible');
if (isVisible && this._keycloak.authenticated) {
this._checkTokeHasExpired();
}
}
_onChanged() {
const event = new CustomEvent("changed", {
detail: this._keycloak,
bubbles: true,
composed: true
});
this.dispatchEvent(event);
}
_onReady(authenticated) {
// Avoid emitting changed when nothing has changed on init()
if (authenticated)
this._onChanged();
}
async _onTokenExpired() {
console.log('Token has expired');
let refreshed = false;
try {
// -1 means force a refresh
refreshed = await this._keycloak.updateToken(-1);
} catch (error) {
console.log('Failed to refresh the token', error);
return;
}
console.assert(refreshed, "token should have been refreshed");
}
async _checkTokeHasExpired() {
let refreshed;
let minValidity = this.MIN_VALIDITY + this.CHECK_INTERVAL;
if (this.DEBUG) {
console.log(`Updating token if not valid for at least ${minValidity}s`);
}
try {
refreshed = await this._keycloak.updateToken(minValidity);
} catch (error) {
console.log('Failed to refresh the token', error);
}
if (this.DEBUG && refreshed)
console.log("token has been refreshed");
}
async _onAuthSuccess() {
// We check every once in a while if the token is still valid and
// and refresh it if needed.
if (this._checkId !== null) {
clearInterval(this._checkId);
this._checkId = null;
}
this._checkId = setInterval(this._checkTokeHasExpired.bind(this), this.CHECK_INTERVAL * 1000);
this._onChanged();
}
async _onAuthLogout() {
if (this._checkId !== null) {
clearInterval(this._checkId);
this._checkId = null;
}
this._onChanged();
}
async _ensureInstance() {
if (this._keycloak !== null)
return;
const Keycloak = await importKeycloak(this._baseURL);
this._keycloak = Keycloak({
url: this._baseURL,
realm: this._realm,
clientId: this._clientId,
});
this._keycloak.onTokenExpired = this._onTokenExpired.bind(this);
this._keycloak.onAuthRefreshSuccess = this._onChanged.bind(this);
this._keycloak.onAuthRefreshError = this._onChanged.bind(this);
this._keycloak.onAuthLogout = this._onAuthLogout.bind(this);
this._keycloak.onAuthSuccess = this._onAuthSuccess.bind(this);
this._keycloak.onAuthError = this._onChanged.bind(this);
this._keycloak.onReady = this._onReady.bind(this);
}
async _keycloakInit(options) {
// https://gitlab.tugraz.at/dbp/topics/library/issues/41
// retry the keycloak init in case it fails, maybe it helps :/
try {
return await this._keycloak.init(options);
} catch (e) {
return await this._keycloak.init(options);
}
}
async _ensureInit() {
await this._ensureInstance();
if (this._initDone)
return;
this._initDone = true;
const options = {
promiseType: 'native',
pkceMethod: 'S256',
};
if (this.DEBUG) {
options['enableLogging'] = true;
}
if (this._silentCheckSsoUri) {
options['onLoad'] = 'check-sso';
options['silentCheckSsoRedirectUri'] = ensureURL(this._silentCheckSsoUri);
// When silent-sso-check is active but the iframe doesn't load/work we will
// never return here, so add a timeout and emit a signal so the app can continue
await promiseTimeout(5000, this._keycloakInit(options)).catch(() => {
console.log('Login timed out');
this._onChanged();
});
} else {
await this._keycloakInit(options);
}
}
/**
* If this returns true you need to call login() at one point to finish the login process.
*/
isLoggingIn() {
const href = window.location.href;
return (href.search('[&#]state=') >= 0 && href.search('[&#]session_state=') >= 0);
}
/**
* Logs the user in. Might lead to a site refresh or the user needing to authenticate.
*
* @param {object} options
* @param {string} [options.lang] - The locale to use on the keycloak login page
*/
async login(options) {
await this._ensureInit();
options = options || {};
const language = options['lang'] || 'en';
const scope = options['scope'] || '';
if (!this._keycloak.authenticated) {
await this._keycloak.login({
kcLocale: language, // Keycloak < 9.0
locale: language,
scope: scope,
idpHint: this._idpHint,
});
}
}
/**
* Logs the user in if it is possible without leaving the page or the user needing to authenticate again.
*/
async tryLogin() {
await this._ensureInit();
}
/**
* Logs the user out locally, but not with keycloak. Login will instantly log the user back in without
* requiring a re-auth.
*/
async localLogout() {
this._keycloak.clearToken();
}
/**
* Log the user out from keycloak.
*/
async logout() {
await this._ensureInit();
this._keycloak.logout();
}
}
const LoginStatus = Object.freeze({
UNKNOWN: 'unknown',
LOGGING_IN: 'logging-in',
LOGGED_IN: 'logged-in',
LOGGING_OUT: 'logging-out',
LOGGED_OUT: 'logged-out',
});
/**
* Keycloak auth web component
* https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter
*
* Emits a dbp-set-property event for the attribute "auth":
* auth.subject: Keycloak username
* auth.login-status: Login status (see object LoginStatus)
* auth.token: Keycloak token to send with your requests
* auth.user-full-name: Full name of the user
* auth.person-id: Person identifier of the user
* auth.person: Person json object of the user (optional, enable by setting the `load-person` attribute)
*/
class AuthKeycloak extends AdapterLitElement {
constructor() {
super();
this.lang = 'de';
this.forceLogin = false;
this.loadPerson = false;
this.token = "";
this.subject = "";
this.name = "";
this.personId = "";
this.tryLogin = false;
this.person = null;
this.entryPointUrl = '';
this._loginStatus = LoginStatus.UNKNOWN;
this.requestedLoginStatus = LoginStatus.UNKNOWN;
this._i18n = createInstance();
// Keycloak config
this.keycloakUrl = null;
this.realm = null;
this.clientId = null;
this.silentCheckSsoRedirectUri = null;
this.scope = null;
this.idpHint = '';
this._onKCChanged = this._onKCChanged.bind(this);
}
update(changedProperties) {
// console.log("changedProperties", changedProperties);
changedProperties.forEach((oldValue, propName) => {
switch (propName) {
case 'lang':
this._i18n.changeLanguage(this.lang);
break;
case 'entryPointUrl':
// for preloading the instance
JSONLD.getInstance(this.entryPointUrl, this.lang);
break;
case 'requestedLoginStatus':
console.log("requested-login-status changed", this.requestedLoginStatus);
switch(this.requestedLoginStatus) {
case LoginStatus.LOGGED_IN:
this._kcwrapper.login({lang: this.lang, scope: this.scope || ''});
break;
case LoginStatus.LOGGED_OUT:
// Keycloak will redirect right away without emitting events, so we have
// to do this manually here
if (this._loginStatus === LoginStatus.LOGGED_IN) {
this._setLoginStatus(LoginStatus.LOGGING_OUT);
}
this._kcwrapper.logout();
// In case logout was aborted, for example with beforeunload,
// revert back to being logged in
if (this._loginStatus === LoginStatus.LOGGING_OUT) {
this._setLoginStatus(LoginStatus.LOGGED_IN);
}
break;
}
break;
}
});
super.update(changedProperties);
}
_onKCChanged(event) {
const kc = event.detail;
let newPerson = false;
if (kc.authenticated) {
let tokenChanged = (this.token !== kc.token);
this.name = kc.idTokenParsed.name;
this.token = kc.token;
this.subject = kc.subject;
const personId = kc.idTokenParsed.preferred_username;
if (personId !== this.personId) {
this.person = null;
newPerson = true;
}
this.personId = personId;
this.sendSetPropertyEvents();
this._setLoginStatus(LoginStatus.LOGGED_IN, tokenChanged || newPerson);
} else {
if (this._loginStatus === LoginStatus.LOGGED_IN) {
this._setLoginStatus(LoginStatus.LOGGING_OUT);
}
this.name = "";
this.token = "";
this.subject = "";
this.personId = "";
this.person = null;
this.sendSetPropertyEvents();
this._setLoginStatus(LoginStatus.LOGGED_OUT);
}
const that = this;
if (newPerson && this.loadPerson) {
JSONLD.getInstance(this.entryPointUrl).then((jsonld) => {
// find the correct api url for the current person
// we are fetching the logged-in person directly to respect the REST philosophy
// see: https://github.com/api-platform/api-platform/issues/337
const apiUrl = jsonld.getApiUrlForEntityName("Person") + '/' + that.personId;
fetch(apiUrl, {
headers: {
'Content-Type': 'application/ld+json',
'Authorization': 'Bearer ' + that.token,
},
})
.then(response => response.json())
.then((person) => {
that.person = person;
this.sendSetPropertyEvents();
this._setLoginStatus(this._loginStatus, true);
});
}, {}, that.lang);
}
}
sendSetPropertyEvents() {
const auth = {
'login-status': this._loginStatus,
'subject': this.subject,
'token': this.token,
'user-full-name': this.name,
'person-id': this.personId,
'person': this.person,
};
// inject a window.DBPAuth variable for cypress
if (window.Cypress) {
window.DBPAuth = auth;
}
this.sendSetPropertyEvent('auth', auth);
}
_setLoginStatus(status, force) {
if (this._loginStatus === status && !force)
return;
this._loginStatus = status;
this.sendSetPropertyEvents();
}
static get properties() {
return {
...super.properties,
lang: { type: String },
forceLogin: { type: Boolean, attribute: 'force-login' },
tryLogin: { type: Boolean, attribute: 'try-login' },
loadPerson: { type: Boolean, attribute: 'load-person' },
entryPointUrl: { type: String, attribute: 'entry-point-url' },
name: { type: String, attribute: false },
token: { type: String, attribute: false },
subject: { type: String, attribute: false },
personId: { type: String, attribute: false },
person: { type: Object, attribute: false },
_loginStatus: { type: String, attribute: false },
keycloakUrl: { type: String, attribute: 'url' },
realm: { type: String },
clientId: { type: String, attribute: 'client-id' },
silentCheckSsoRedirectUri: { type: String, attribute: 'silent-check-sso-redirect-uri' },
scope: { type: String },
idpHint: { type: String, attribute: 'idp-hint' },
requestedLoginStatus: { type: String, attribute: 'requested-login-status' },
};
}
connectedCallback() {
super.connectedCallback();
if (!this.keycloakUrl)
throw Error("url not set");
if (!this.realm)
throw Error("realm not set");
if (!this.clientId)
throw Error("client-id not set");
this._kcwrapper = new KeycloakWrapper(this.keycloakUrl, this.realm, this.clientId, this.silentCheckSsoRedirectUri, this.idpHint);
this._kcwrapper.addEventListener('changed', this._onKCChanged);
const handleLogin = async () => {
if (this.forceLogin || this._kcwrapper.isLoggingIn()) {
this._setLoginStatus(LoginStatus.LOGGING_IN);
await this._kcwrapper.login({lang: this.lang, scope: this.scope || ''});
} else if (this.tryLogin) {
this._setLoginStatus(LoginStatus.LOGGING_IN);
await this._kcwrapper.tryLogin();