UNPKG

@jinntec/fore

Version:

Fore - declarative user interfaces in plain HTML

154 lines (133 loc) 5.95 kB
import { evaluateXPathToFirstNode } from './xpath-evaluation.js'; import { XPathUtil } from './xpath-util.js'; /** * @param {Node} node * @returns {HTMLElement} */ function _getElement(node) { if (node && node.nodeType && node.nodeType === Node.ATTRIBUTE_NODE) { // The context of an attribute is the ref of the element it's defined on return node.ownerElement; } if (node.nodeType === Node.ELEMENT_NODE) { // The context of a query should be the element having a ref return node; } // For text nodes, just start looking from the parent element return node.parentNode; } function _getForeContext(node) { return XPathUtil.getClosest('fx-fore', node); } function _getModelInContext(node) { // const ownerForm = node.closest('fx-fore'); const ownerForm = _getForeContext(node); return ownerForm.getModel(); } function _getInitialContext(node, ref) { const parentBind = XPathUtil.getClosest('[ref]', node); const localFore = XPathUtil.getClosest('fx-fore', node); const model = _getModelInContext(node); if (parentBind !== null) { /* make sure that the closest ref belongs to the same fx-fore element */ const parentBindFore = parentBind.closest('fx-fore'); if (localFore === parentBindFore) { return parentBind.nodeset; } return model.getDefaultInstance().getDefaultContext(); } if (XPathUtil.isAbsolutePath(ref)) { const instanceId = XPathUtil.getInstanceId(ref, node); if (instanceId) { return model.getInstance(instanceId).getDefaultContext(); } return model.getDefaultInstance().getDefaultContext(); } // should always return default context if all other fails return model.getDefaultInstance().getDefaultContext(); } /** * Get the inscope context for an XPath defined on an element, attribute or in a textnode. Uses the * current iterate status, repeats, etcetera * * @param {Node} node The context node at this point. Can be an attribute * @param {string} ref The XPath to resolve for * @return {Node} The context item for this XPath */ export default function getInScopeContext(node, ref) { // console.log('getInScopeContext', ref, node); //todo: check for multi-step pathes const parentElement = _getElement(node); // console.log('getInScopeContext parent', parentElement); if (parentElement.nodeName === 'FX-FORE') { const context = parentElement.getModel().getDefaultInstance()?.getDefaultContext(); if (!context) { // Edge-case, we are in an inner fore. Use the outer fore's default context return getInScopeContext(parentElement.parentNode, ref); } return context; } const parentBind = XPathUtil.getClosest('[ref]', parentElement.parentNode); if (parentBind && (parentBind.nodeName === 'FX-GROUP' || parentBind.nodeName === 'FX-CONTROL')) { return parentBind.nodeset; } const parentActionWithIterateExpr = parentElement.matches('[iterate]') ? parentElement : XPathUtil.getClosest('[iterate]', parentElement.parentNode); if (parentActionWithIterateExpr && parentActionWithIterateExpr.currentContext) { return parentActionWithIterateExpr.currentContext; } const repeatItem = XPathUtil.getClosest('fx-repeatitem', parentElement); if (repeatItem) { if (node.nodeName === 'context') { return evaluateXPathToFirstNode( node.nodeValue, repeatItem.nodeset, _getForeContext(parentElement), ); } return repeatItem.nodeset; } // ### check for repeatitems created by fx-repeat-attributes - this could possibly be unified with standard repeats // const repeatItemFromAttrs = XPathUtil.getClosest('.fx-repeatitem', parentElement); // const repeatItemFromAttrs = XPathUtil.getClosest('.fx-repeatitem', parentElement); const repeatItemFromAttrs = parentElement.closest('.fx-repeatitem'); if (repeatItemFromAttrs) { // Determine the context by using the cache present on the fx-repeat-attributes. Do not attempt // to use the index here, it might change when deleting / inserting items const repeatFromAttributes = /** @type {import('./ui/fx-repeat-attributes.js').FxRepeatAttributes} */ ( XPathUtil.getClosest('fx-repeat-attributes', parentElement) ); return repeatFromAttributes.getContextForRepeatItem(repeatItemFromAttrs); } if (parentElement.hasAttribute('context')) { const initialContext = _getInitialContext(parentElement.parentNode, ref); const contextAttr = parentElement.getAttribute('context'); return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement)); } if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'context') { const initialContext = _getInitialContext(node.ownerElement.parentNode, ref); const contextAttr = node.ownerElement.getAttribute('context'); return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement)); } if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') { // Note: do not consider the ref of the owner element since it should not be used to define the // context if (node.ownerElement.hasAttribute('context')) { const initialContext = _getInitialContext(node.ownerElement.parentNode, ref); const contextAttr = node.ownerElement.getAttribute('context'); return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement)); } // Never resolve the context from a ref itself! return _getInitialContext(parentElement.parentNode, ref); } // if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) { // const initialContext = _getInitialContext(node.parentNode, ref); // const contextAttr = node.getAttribute('context'); // return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement)); // } return _getInitialContext(parentElement, ref); }