'use strict'; if ('undefined' == typeof globalThis) { const e = 'undefined' != typeof global ? global : 'undefined' != typeof window ? window : 'undefined' != typeof self ? self : {}; e.globalThis = e; } Object.defineProperty(exports, '__esModule', { value: true }); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n['default'] = e; return Object.freeze(n); } /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ function flattenArray(array, dst) { // Yes this function is just Array.flat, but we need to run on old versions of Node. if (!dst) dst = []; for (const item of array) { if (Array.isArray(item)) { flattenArray(item, dst); } else { dst.push(item); } } return dst; } /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ // minification can replace the `globalThis.qDev` with `false` // which will remove all dev code within from the build const qDev = globalThis.qDev !== false; const qTest = globalThis.describe !== undefined; const qGlobal = globalThis; /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ const EMPTY_ARRAY = []; const EMPTY_OBJ = {}; if (qDev) { Object.freeze(EMPTY_ARRAY); Object.freeze(EMPTY_OBJ); } /** * @public */ function jsx(type, props, key) { return new JSXNodeImpl(type, props, key); } class JSXNodeImpl { constructor(type, props, key) { this.type = type; this.props = props; this.key = key; if (props && props.children !== undefined) { if (Array.isArray(props.children)) { this.children = props.children; } else { this.children = [props.children]; } } else { this.children = EMPTY_ARRAY; } } } const isJSXNode = (n) => { if (qDev) { if (n instanceof JSXNodeImpl) { return true; } if (n && typeof n === 'object' && n.constructor.name === JSXNodeImpl.name) { throw new Error(`Duplicate implementations of "JSXNodeImpl" found`); } return false; } else { return n instanceof JSXNodeImpl; } }; /** * @public */ const Fragment = {}; /* eslint-disable */ /** * @public */ function h(type, props, ...children) { // Using legacy h() jsx transform and morphing it // so it can use the modern vdom structure // https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html // https://www.typescriptlang.org/tsconfig#jsxImportSource const normalizedProps = { children: arguments.length > 2 ? flattenArray(children) : EMPTY_ARRAY, }; let key; let i; for (i in props) { if (i == 'key') key = props[i]; else normalizedProps[i] = props[i]; } return new JSXNodeImpl(type, normalizedProps, key); } /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ function assertDefined(value, text) { if (qDev) { if (value != null) return; throw newError(text || 'Expected defined value.'); } } function assertNotEqual(value1, value2, text) { if (qDev) { if (value1 !== value2) return; throw newError(text || `Expected '${value1}' !== '${value2}'.`); } } function assertEqual(value1, value2, text) { if (qDev) { if (value1 === value2) return; throw newError(text || `Expected '${value1}' === '${value2}'.`); } } function assertGreaterOrEqual(value1, value2, text) { if (qDev) { if (value1 >= value2) return; throw newError(text || `Expected '${value1}' >= '${value2}'.`); } } function assertGreater(value1, value2, text) { if (qDev) { if (value1 > value2) return; throw newError(text || `Expected '${value1}' > '${value2}'.`); } } function newError(text) { debugger; // eslint-disable-line no-debugger const error = new Error(text); console.error(error); // eslint-disable-line no-console return error; } /** * Returns true if the `node` is `Element` and of the right `tagName`. * * @param node * @private */ function isDomElementWithTagName(node, tagName) { return isHtmlElement(node) && node.tagName.toUpperCase() == tagName.toUpperCase(); } /** * @private */ function isTemplateElement(node) { return isDomElementWithTagName(node, 'template'); } /** * @private */ function isQSLotTemplateElement(node) { return isTemplateElement(node) && node.hasAttribute("q:slot" /* QSlotAttr */); } /** * @private */ function isComponentElement(node) { return isHtmlElement(node) && node.hasAttribute("on:q-render" /* OnRender */); } /** * @private */ function isHtmlElement(node) { return node ? node.nodeType === 1 /* ELEMENT_NODE */ : false; } /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ function isNode(value) { return value && typeof value.nodeType == 'number'; } function isDocument(value) { return value && value.nodeType == 9 /* DOCUMENT_NODE */; } function isComment(value) { return isNode(value) && value.nodeType == 8 /* COMMENT_NODE */; } const createPlatform = (doc) => { let queuePromise; let storePromise; return { import: (url) => Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(url)); }), toPath: (url) => { url = new URL(String(url)); url.hash = ''; url.search = ''; return url.href + '.js'; }, queueRender: (renderMarked) => { if (!queuePromise) { queuePromise = new Promise((resolve, reject) => doc.defaultView.requestAnimationFrame(() => { queuePromise = null; renderMarked(doc).then(resolve, reject); })); } return queuePromise; }, queueStoreFlush: (flushStore) => { if (!storePromise) { storePromise = new Promise((resolve, reject) => doc.defaultView.requestAnimationFrame(() => { storePromise = null; flushStore(doc).then(resolve, reject); })); } return storePromise; }, }; }; /** * @public */ const setPlatform = (doc, plt) => (doc[DocumentPlatform] = plt); /** * @public */ const getPlatform = (docOrNode) => { const doc = isDocument(docOrNode) ? docOrNode : docOrNode.ownerDocument; return (doc[DocumentPlatform] || (doc[DocumentPlatform] = createPlatform(doc))); }; const DocumentPlatform = /*@__PURE__*/ Symbol(); /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ function stringifyDebug(value) { if (value == null) return String(value); if (typeof value === 'function') return value.name; if (isHtmlElement(value)) return stringifyElement(value); if (value instanceof URL) return String(value); if (typeof value === 'object') return JSON.stringify(value, function (key, value) { if (isHtmlElement(value)) return stringifyElement(value); return value; }); return String(value); } function stringifyElement(element) { let html = '<' + element.tagName.toLowerCase(); const attributes = element.attributes; const names = []; for (let i = 0; i < attributes.length; i++) { names.push(attributes[i].name); } names.sort(); for (let i = 0; i < names.length; i++) { const name = names[i]; let value = element.getAttribute(name); if (value === null || value === void 0 ? void 0 : value.startsWith('file:/')) { value = value.replace(/(file:\/\/).*(\/.*)$/, (all, protocol, file) => protocol + '...' + file); } html += ' ' + name + (value == null || value == '' ? '' : "='" + value.replace("'", ''') + "'"); } return html + '>'; } /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ function qError(code, ...args) { if (qDev) { const text = codeToText(code); const parts = text.split('{}'); const error = parts .map((value, index) => { return value + (index === parts.length - 1 ? '' : stringifyDebug(args[index])); }) .join(''); debugger; // eslint-disable-line no-debugger return new Error(error); } else { return new Error(`QError ` + code); } } function codeToText(code) { const area = { 0: 'ERROR', 1: 'QRL-ERROR', 2: 'INJECTOR-ERROR', 3: 'SERVICE-ERROR', 4: 'COMPONENT-ERROR', 5: 'PROVIDER-ERROR', 6: 'RENDER-ERROR', 7: 'EVENT-ERROR', }[Math.floor(code / 100)]; const text = { [1 /* Core_qConfigNotFound_path */]: "QConfig not found in path '{}'.", [2 /* Core_unrecognizedStack_frame */]: "Unrecognized stack format '{}'", [3 /* Core_noAttribute_atr1_element */]: "Could not find entity state '{}' at '{}' or any of it's parents.", [4 /* Core_noAttribute_atr1_attr2_element */]: "Could not find entity state '{}' ( or entity provider '{}') at '{}' or any of it's parents.", [5 /* Core_missingProperty_name_props */]: "Missing property '{}' in props '{}'.", [6 /* Core_missingExport_name_url_props */]: "Missing export '{}' from '{}'. Exported symbols are: {}", ////////////// [100 /* QRL_expectFunction_url_actual */]: "QRL '${}' should point to function, was '{}'.", ////////////// [200 /* Injector_noHost_element */]: "Can't find host element above '{}'.", [201 /* Injector_expectedSpecificInjector_expected_actual */]: "Provider is expecting '{}' but got '{}'.", [202 /* Injector_notElement_arg */]: "Expected 'Element' was '{}'.", [203 /* Injector_wrongMethodThis_expected_actual */]: "Expected injection 'this' to be of type '{}', but was of type '{}'.", [204 /* Injector_missingSerializedState_entityKey_element */]: "Entity key '{}' is found on '{}' but does not contain state. Was 'serializeState()' not run during dehydration?", [206 /* Injector_notFound_element */]: "No injector can be found starting at '{}'.", [207 /* Injector_eventInjectorNotSerializable */]: 'EventInjector does not support serialization.', ////////////// [300 /* Entity_notValidKey_key */]: "Data key '{}' is not a valid key.\n" + ' - Data key can only contain characters (preferably lowercase) or number\n' + ' - Data key is prefixed with entity name\n' + " - Data key is made up from parts that are separated with ':'.", [301 /* Entity_keyAlreadyExists_key */]: "A entity with key '{}' already exists.", [303 /* Entity_invalidAttribute_name */]: "'{}' is not a valid attribute. " + "Attributes can only contain 'a-z' (lowercase), '0-9', '-' and '_'.", [304 /* Entity_missingExpandoOrState_attrName */]: "Found '{}' but expando did not have entity and attribute did not have state.", [305 /* Entity_elementMissingEntityAttr_element_attr */]: "Element '{}' is missing entity attribute definition '{}'.", [306 /* Entity_noState_entity_props */]: "Unable to create state for entity '{}' with props '{}' because no state found and '$newState()' method was not defined on entity.", [307 /* Entity_expected_obj */]: "'{}' is not an instance of 'Entity'.", [308 /* Entity_overridesConstructor_entity */]: "'{}' overrides 'constructor' property preventing 'EntityType' retrieval.", [311 /* Entity_no$keyProps_entity */]: "Entity '{}' does not define '$keyProps'.", [310 /* Entity_no$type_entity */]: "Entity '{}' must have static '$type' property defining the name of the entity.", [312 /* Entity_no$qrl_entity */]: "Entity '{}' must have static '$qrl' property defining the import location of the entity.", [313 /* Entity_nameCollision_name_currentQrl_expectedQrl */]: "Name collision. Already have entity named '{}' with QRL '{}' but expected QRL '{}'.", [309 /* Entity_keyMissingParts_key_key */]: "Entity key '{}' is missing values. Expecting '{}:someValue'.", [314 /* Entity_keyTooManyParts_entity_parts_key */]: "Entity '{}' defines '$keyProps' as '{}'. Actual key '{}' has more parts than entity defines.", [315 /* Entity_keyNameMismatch_key_name_entity_name */]: "Key '{}' belongs to entity named '{}', but expected entity '{}' with name '{}'.", [316 /* Entity_stateMissingKey_state */]: "Entity state is missing '$key'. Are you sure you passed in state? Got '{}'.", ////////////// [400 /* Component_bindNeedsKey */]: "'bind:' must have an key. (Example: 'bind:key=\"propertyName\"').", [401 /* Component_bindNeedsValue */]: "'bind:id' must have a property name. (Example: 'bind:key=\"propertyName\"').", [402 /* Component_needsState */]: "Can't find state on host element.", [403 /* Component_needsInjectionContext_constructor */]: "Components must be instantiated inside an injection context. Use '{}.new(...)' for creation.", [404 /* Component_noProperty_propName_props_host */]: "Property '{}' not found in '{}' on component '{}'.", [405 /* Component_notFound_component */]: "Unable to find '{}' component.", [406 /* Component_doesNotMatch_component_actual */]: "Requesting component type '{}' does not match existing component instance '{}'.", [408 /* Component_noState_component_props */]: "Unable to create state for component '{}' with props '{}' because no state found and '$newState()' method was not defined on component.", ////////////// [500 /* Provider_unrecognizedFormat_value */]: "Unrecognized expression format '{}'.", ////////////// [600 /* Render_unexpectedJSXNodeType_type */]: 'Unexpected JSXNode<{}> type.', [601 /* Render_unsupportedFormat_obj_attr */]: "Value '{}' can't be written into '{}' attribute.", [602 /* Render_expectingEntity_entity */]: "Expecting entity object, got '{}'.", [603 /* Render_expectingEntityArray_obj */]: "Expecting array of entities, got '{}'.", [604 /* Render_expectingEntityOrComponent_obj */]: "Expecting Entity or Component got '{}'.", [699 /* Render_stateMachineStuck */]: 'Render state machine did not advance.', ////////////// [700 /* Event_emitEventRequiresName_url */]: "Missing '$type' attribute in the '{}' url.", [701 /* Event_emitEventCouldNotFindListener_event_element */]: "Re-emitting event '{}' but no listener found at '{}' or any of its parents.", }[code]; let textCode = '000' + code; textCode = textCode.substr(textCode.length - 3); return `${area}(Q-${textCode}): ${text}`; } /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ /** * Lazy load a `QRL` symbol and returns the resulting value. * * @param element - Location of the URL to resolve against. * @param url - A relative URL (as `string` or `QRL`) or fully qualified `URL` * @returns A cached value synchronously or promise of imported value. * @public */ function qImport(element, url) { if (isParsedQRL(url)) { assertDefined(url._serialized); url = Array.isArray(url._serialized) ? url._serialized[0] : url._serialized; } if (qTest) { // This code is here for testing purposes only, and should never end up in production. const testSymbol = fromQRL(url); if (testSymbol) { return Promise.resolve(testSymbol); } } const doc = element.ownerDocument; const corePlatform = getPlatform(doc); const normalizedUrl = toUrl(doc, element, url); const importPath = corePlatform.toPath(normalizedUrl); const exportName = qExport(normalizedUrl); const cacheKey = importPath + '#' + exportName; const cacheValue = (doc[ImportCacheKey] || (doc[ImportCacheKey] = new Map())).get(cacheKey); if (cacheValue) return cacheValue; const promise = corePlatform.import(importPath).then((module) => { const handler = module[exportName]; if (!handler) if (qDev) { throw qError(6 /* Core_missingExport_name_url_props */, exportName, importPath, Object.keys(module)); } else { throw qError(6 /* Core_missingExport_name_url_props */); } qImportSet(doc, cacheKey, handler); return handler; }); qImportSet(doc, cacheKey, promise); return promise; } function qImportSet(doc, cacheKey, value) { doc[ImportCacheKey].set(cacheKey, value); } /** * Convert relative base URI and relative URL into a fully qualified URL. * * @param base -`QRL`s are relative, and therefore they need a base for resolution. * - `Element` use `base.ownerDocument.baseURI` * - `Document` use `base.baseURI` * - `string` use `base` as is * - `QConfig` use `base.baseURI` * @param url - relative URL * @returns fully qualified URL. */ function toUrl(doc, element, url) { let _url; let _base = undefined; if (url === undefined) { // recursive call if (element) { _url = element.getAttribute('q:base'); _base = toUrl(doc, element.parentNode && element.parentNode.closest('[q\\:base]')); } else { _url = doc.baseURI; } } else if (url) { (_url = url), (_base = toUrl(doc, element.closest('[q\\:base]'))); } else { throw new Error('INTERNAL ERROR'); } return new URL(String(_url), _base); } /** * Extract the QRL export name from a URL. * * This name is encoded in the hash of the URL, before any `?`. */ function qExport(url) { // 1 - optional `#` at the start. // 2 - capture group `$1` containing the export name, stopping at the first `?`. // 3 - the rest from the first `?` to the end. // The hash string is replaced by the captured group that contains only the export name. // 1112222222333 return url.hash.replace(/^#?([^?]*).*$/, '$1') || 'default'; } const ImportCacheKey = /*@__PURE__*/ Symbol(); /** * @license * Copyright Builder.io, Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ const camelToKebabCase = new Map(); function fromCamelToKebabCase(text, includeFirst = false) { if (typeof text != 'string') return text; const value = camelToKebabCase.get(text); if (value != null) return value; let converted = ''; for (let x = 0; x < text.length; x++) { const ch = text.charAt(x); if (isUpperCase(ch)) { converted += (x != 0 || includeFirst ? '-' : '') + ch.toLowerCase(); } else { converted += ch; } } camelToKebabCase.set(text, converted); return converted; } function isUpperCase(ch) { return 'A' <= ch && ch <= 'Z'; } function QStore_hydrate(doc) { const script = doc.querySelector('script[type="qwik/json"]'); let map = null; if (script) { script.parentElement.removeChild(script); map = JSON.parse(script.textContent || '{}'); reviveQObjects(map); reviveNestedQObjects(map, map); } return map; } function QStore_dehydrate(doc) { const map = {}; doc.querySelectorAll('[q\\:obj]').forEach((node) => { const props = qProps(node); const qMap = props.__qRefs__; clearQProps(node); assertDefined(qMap); qMap.forEach((v, k) => { map[k] = v.obj; collectQObjects(v, new Set(), (k, v) => (map[k] = v)); }); }); const script = doc.createElement('script'); script.setAttribute('type', 'qwik/json'); script.textContent = JSON.stringify(map, function (key, value) { if (this === map) return value; if (key.startsWith('__')) return undefined; const id = getQObjectId(value); if (id) return JSON_OBJ_PREFIX + id; return value; }, qDev ? ' ' : undefined); doc.body.appendChild(script); clearQPropsMap(doc); } function reviveQObjects(map) { for (const key in map) { if (Object.prototype.hasOwnProperty.call(map, key)) { const value = map[key]; map[key] = _restoreQObject(value, key); } } } function reviveNestedQObjects(obj, map) { if (obj && typeof obj == 'object') { if (Array.isArray(obj)) { for (let i = 0; i < obj.length; i++) { const value = obj[i]; if (typeof value == 'string' && value.startsWith(JSON_OBJ_PREFIX)) { obj[i] = map[value.substring(JSON_OBJ_PREFIX.length)]; } else { reviveNestedQObjects(value, map); } } } else { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = obj[key]; if (typeof value == 'string' && value.startsWith(JSON_OBJ_PREFIX)) { obj[key] = map[value.substring(JSON_OBJ_PREFIX.length)]; } else { reviveNestedQObjects(value, map); } } } } } } function collectQObjects(obj, seen, foundFn) { if (obj && typeof obj == 'object') { if (seen.has(obj)) return; seen.add(obj); if (Array.isArray(obj)) { for (let i = 0; i < obj.length; i++) { collectQObjects(obj[i], seen, foundFn); } } else { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = obj[key]; const id = getQObjectId(value); if (id) foundFn(id, value); collectQObjects(value, seen, foundFn); } } } } } const Q_OBJECT_ATTR = 'q:obj'; function updateSubscriptions(element, map, idSubscriptionSet) { map.forEach((value, key) => { const qObj = value.obj; if (idSubscriptionSet.has(value.obj)) { // do nothing; already subscribed if (!value.isSub) { setMapFacade(map, getQObjectId(qObj), qObj, element, true, 1); } } else if (value.isSub) { // Unsubscribe value.isSub = false; releaseRef(value, map, key); } idSubscriptionSet.delete(qObj); }); idSubscriptionSet.forEach((qObj) => setMapFacade(map, getQObjectId(qObj), qObj, element, true, 1)); writeQObjAttr(element, map); } function writeQObjAttr(element, map) { const list = []; map.forEach((v, k) => { if (v.isSub) k = '!' + k; v.count == 1 ? list.push(k) : list.push('#' + v.count, k); }); if (list.length) { element.setAttribute(Q_OBJECT_ATTR, list.join(' ')); } else { element.removeAttribute(Q_OBJECT_ATTR); } } function createMapFacade(element, map) { return { forEach(fn) { return map.forEach((v, k) => { fn(v.obj, k); }); }, get(key) { const value = map.get(key); return value === null || value === void 0 ? void 0 : value.obj; }, set(key, qObj) { setMapFacade(map, key, qObj, element, false, 1); writeQObjAttr(element, map); }, }; } function setMapFacade(map, key, qObj, element, subscribed, count) { assertDefined(key); let value = map.get(key); if (qObj) { QObject_addDoc(qObj, element.ownerDocument); if (value) { value.count += count; value.isSub = value.isSub || subscribed; } else { map.set(key, (value = { obj: qObj, count, isSub: subscribed })); } } else { if (value) { value = releaseRef(value, map, key); } } return value; } function releaseRef(value, map, key) { value.count--; if (value.count == 0) { map.delete(key); return undefined; } return value; } function loadObjectsFromState(element, storeMap) { const qProps = newQProps(element); const objs = element.getAttribute(Q_OBJECT_ATTR); if (objs) { const parts = objs.split(' '); const qMap = qProps.__qRefs__; let lastCount = 1; parts.forEach((key) => { if (key.startsWith('#')) { lastCount = Number(key.substr(1)); } else { let isSubscribed = false; if (key.startsWith('!')) { key = key.substr(1); isSubscribed = true; } const qObj = storeMap[key]; setMapFacade(qMap, key, qObj, element, isSubscribed, lastCount); } }); } } // TODO(misko): For better debugger experience the qProps should never store Proxy, always naked objects to make it easier to traverse in the debugger. const Q_IS_HYDRATED = '__isHydrated__'; const Q_PROP = 'qProps'; function hydrateIfNeeded(element) { const doc = element.ownerDocument; const isHydrated = doc[Q_IS_HYDRATED]; if (!isHydrated) { doc[Q_IS_HYDRATED] = true; const map = QStore_hydrate(element.ownerDocument); if (map) { doc.querySelectorAll(Q_OBJECT_ATTR_SELECTOR).forEach((element) => { loadObjectsFromState(element, map); }); } } } function clearQPropsMap(doc) { doc[Q_IS_HYDRATED] = undefined; } function clearQProps(element) { element[Q_PROP] = undefined; } const Q_OBJECT_ATTR_SELECTOR = '[q\\:obj]'; const STATE_PREFIX = 'state:'; const ON_PREFIX = 'on:'; function newQProps(element) { const qObjRefMap = new Map(); const qObjMap = createMapFacade(element, qObjRefMap); const cache = { __element__: element, __qRefs__: qObjRefMap, __qMap__: qObjMap, __mutation__: false, }; return (element[Q_PROP] = new Proxy(cache, { get: (target, prop) => { if (typeof prop == 'string') { if (prop === '__mutation__') { const mutation = target.__mutation__; target.__mutation__ = false; return mutation; } else if (prop == '__parent__') { const parent = element.parentElement; return parent && qProps(parent); } else if (prop.startsWith(ON_PREFIX)) { return createInvokeFn(cache, qObjMap, prop); } if (prop in cache) { return target[prop]; } if (prop.startsWith(STATE_PREFIX)) { return (cache[prop] = findState(qObjMap, prop.substr(STATE_PREFIX.length))); } else { return (cache[prop] = readAttribute(element, qObjMap, prop)); } } }, set: (target, prop, value) => { if (typeof prop == 'string') { if (prop === 'children') return true; if (prop.startsWith(STATE_PREFIX)) { const id = getQObjectId(value); assertDefined(id); assertEqual(id.startsWith(prop.substr(STATE_PREFIX.length)), true); qObjMap.set(id, (target[prop] = value)); } else if (prop.startsWith(ON_PREFIX)) { addQrlListener(cache, qObjMap, prop, value); } else if (prop === ':subscriptions') { updateSubscriptions(element, qObjRefMap, value); } else { value = wrap(value); const existingValue = prop in target ? target[prop] : (target[prop] = readAttribute(element, qObjMap, prop)); /** const qObjs = diff(existingValue, value); if (qObjs) { qObjs.forEach((id) => qObjMap.set(id, null!)); writeAttribute(element, qObjMap, prop, (target[prop] = value)); target.__mutation__ = true; } */ if (value !== existingValue) { const existingId = getQObjectId(existingValue); existingId && qObjMap.set(existingId, null); writeAttribute(element, qObjMap, prop, (target[prop] = value)); target.__mutation__ = true; } } return true; } else { // TODO(misko): Better error/test throw new Error('Only string keys are supported'); } }, })); } function readAttribute(element, map, propName) { if (propName.startsWith(ON_PREFIX)) { const attrName = fromCamelToKebabCase(propName.substr(3)); const attrValue = element.getAttribute(attrName); const listeners = []; attrValue === null || attrValue === void 0 ? void 0 : attrValue.split('\n').forEach((qrl) => { listeners.push(parseQRL(qrl, map)); }); return listeners; } else { const attrName = fromCamelToKebabCase(propName); const attrValue = element.getAttribute(attrName); if (attrValue === null) { return undefined; } else { return qJsonParse(attrValue, map); } } } function writeAttribute(element, map, propName, value) { const attrName = fromCamelToKebabCase(propName); if (propName == 'class') { element.setAttribute('class', stringifyClassOrStyle(value, true)); } else if (propName == 'style') { element.setAttribute('style', stringifyClassOrStyle(value, false)); } else if (propName === 'innerHTML' || propName === 'innerText') { element.setAttribute(attrName, ''); element[propName] = value; } else { const newValue = qJsonStringify(value, map); if (value === undefined) { element.removeAttribute(attrName); } else { element.setAttribute(attrName, newValue); } } if ((propName == 'value' || propName == 'checked') && element.tagName === 'INPUT') { // INPUT properties `value` and `checked` are special because they can go out of sync // between the attribute and what the user entered, so they have special treatment. element[propName] = value; } } function findState(map, stateName) { let state = null; stateName += Q_OBJECT_PREFIX_SEP; map.forEach((v, k) => { if (k.startsWith(stateName)) { state = v; } }); return state; } function addQrlListener(cache, map, prop, value) { if (!value) return; if (typeof value == 'string' || value instanceof String) { value = parseQRL(value, undefined /** Don't expect objects in strings */); } if (value instanceof ParsedQRL) { const existingQRLs = getExistingQRLs(cache, map, prop); let found = false; for (let index = 0; index < existingQRLs.length; index++) { const existingQRL = existingQRLs[index]; if (isSameHandler(existingQRL, value)) { found = true; replaceQRL(existingQRLs, map, index, value); break; } } if (!found) { replaceQRL(existingQRLs, map, existingQRLs.length, value); } const kababProp = ON_PREFIX + fromCamelToKebabCase(prop.substr(ON_PREFIX.length)); cache.__element__.setAttribute(kababProp, serializeQRLs(existingQRLs)); } else { // TODO(misko): Test/better text throw new Error(`Not QRL: prop: ${prop}; value: ` + value); } } function getExistingQRLs(cache, map, prop) { if (prop in cache) return cache[prop]; const kababProp = ON_PREFIX + fromCamelToKebabCase(prop.substr(ON_PREFIX.length)); const parts = []; (cache.__element__.getAttribute(kababProp) || '').split('\n').forEach((qrl) => { if (qrl) { parts.push(parseQRL(qrl, map)); } }); return (cache[prop] = parts); } function isSameHandler(existing, actual) { return (existing.url == actual.url && existing.symbol == actual.symbol && existing.getState() == actual.getState()); } function serializeQRLs(existingQRLs) { return existingQRLs .map((qrl) => { assertDefined(qrl._serialized); return qrl._serialized; }) .join('\n'); } function replaceQRL(existingQRLs, map, index, newQrl) { const existing = index < existingQRLs.length ? existingQRLs[index] : null; if (existing && Array.isArray(existing._serialized)) { existing._serialized.forEach((key, index) => { if (index) { // need to skip the first one. map.set(key, null); } }); } stringifyQRL(newQrl, map); existingQRLs[index] = newQrl; } function createInvokeFn(cache, map, prop) { const existingQRLs = getExistingQRLs(cache, map, prop); if (existingQRLs.length === 0) return null; return (event) => { return Promise.all(existingQRLs.map(async (qrl) => { const fn = await qImport(cache.__element__, qrl); const element = cache.__element__; const qrlString = Array.isArray(qrl._serialized) ? qrl._serialized[0] : qrl._serialized; const url = new URL(qrlString, element.ownerDocument.baseURI); return { state: qrl.getState(), value: await fn(element, event, url) }; })); }; } function didQPropsChange(qProps) { return qProps.__mutation__; } /** * Turn an `Array` or object literal into a `class` or `style` * * @param obj `string`, `Array` or object literal * @param isClass `true` if expecting `class` output * @returns `string` */ function stringifyClassOrStyle(obj, isClass) { if (obj == null) return ''; if (typeof obj == 'object') { let text = ''; let sep = ''; if (Array.isArray(obj)) { if (!isClass) { throw qError(601 /* Render_unsupportedFormat_obj_attr */, obj, 'style'); } for (let i = 0; i < obj.length; i++) { text += sep + obj[i]; sep = ' '; } } else { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = obj[key]; text += isClass ? (value ? sep + key : '') : sep + key + ':' + value; sep = isClass ? ' ' : ';'; } } } return text; } return String(obj); } /** * @public */ function qProps(element) { hydrateIfNeeded(element); let qProps = element[Q_PROP]; if (!qProps) { qProps = newQProps(element); } return qProps; } /** * Remove item from array (Same as `Array.splice()` but faster.) * * `Array.splice()` is not as fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * https://jsperf.com/fast-array-splice (About 20x faster) * * @param array Array to splice * @param index Index of element in array to remove. * @param count Number of items to remove. */ /** * Same as `Array.splice2(index, 0, value1, value2)` but faster. * * `Array.splice()` is not fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * @param array Array to splice. * @param index Index in array where the `value` should be added. * @param value1 Value to add to array. * @param value2 Value to add to array. */ function arrayInsert2(array, index, value1, value2) { let end = array.length; if (end == index) { // inserting at the end. array.push(value1, value2); } else if (end === 1) { // corner case when we have less items in array than we have items to insert. array.push(value2, array[0]); array[0] = value1; } else { end--; array.push(array[end - 1], array[end]); while (end > index) { const previousEnd = end - 2; array[end] = array[previousEnd]; end--; } array[index] = value1; array[index + 1] = value2; } } function keyValueArrayGet(keyValueArray, key, notFoundFactory) { const index = keyValueArrayIndexOf(keyValueArray, key); if (index >= 0) { // if we found it retrieve it. return keyValueArray[index | 1]; } if (notFoundFactory) { const value = notFoundFactory(); arrayInsert2(keyValueArray, ~index, key, value); return value; } return undefined; } /** * Retrieve a `key` index value in the array or `-1` if not found. * * @param keyValueArray to search. * @param key The key to locate. * @returns index of where the key is (or should have been.) * - positive (even) index if key found. * - negative index if key not found. (`~index` (even) to get the index where it should have * been inserted.) */ function keyValueArrayIndexOf(keyValueArray, key) { return _arrayIndexOfSorted(keyValueArray, key, 1); } /** * INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`. * * NOTE: * - This uses binary search algorithm for fast removals. * * @param array A sorted array to binary search. * @param value The value to look for. * @param shift grouping shift. * - `0` means look at every location * - `1` means only look at every other (even) location (the odd locations are to be ignored as * they are values.) * @returns index of the value. * - positive index if value found. * - negative index if value not found. (`~index` to get the value where it should have been * inserted) */ function _arrayIndexOfSorted(array, value, shift) { let start = 0; let end = array.length >> shift; while (end !== start) { const middle = start + ((end - start) >> 1); // find the middle. const current = array[middle << shift]; if (value === current) { return middle << shift; } else if (current > value) { end = middle; } else { start = middle + 1; // We already searched middle so make it non-inclusive by adding 1 } } return ~(end << shift); } function isSlotMap(value) { return Array.isArray(value); } /** * Retrieves the current `SlotMap` from `QComponent` * * * This method collects the content `Node`s for a given component. * * @param component * @returns */ function getSlotMap(component) { const slots = []; const host = component.hostElement; const firstChild = host.firstElementChild; if (isQSlotTemplate(firstChild)) { slotMapAddChildren(slots, firstChild.content, null); } const previousSlots = []; host.querySelectorAll("Q\\:SLOT" /* QSlotSelector */).forEach((qSlot) => { for (const parent of previousSlots) { if (parent.contains(qSlot)) { // When we do `querySelectorAll` it is possible that we get `` // which are children of existing ``. This check is here // to make sure that we don't get `` recursively. // // // // // return; } } previousSlots.push(qSlot); const name = qSlot.getAttribute('name') || ''; slotMapAddChildren(slots, qSlot, name); }); return slots; } /** * Determines if the `node` is `