!function(h){"use strict";var i=function(i,o,r){var u,s,v,c,t={},m=0,a=0,l={sensitivity:7,interval:100,timeout:0};function f(e){u=e.clientX,s=e.clientY}function e(e){return a&&(a=clearTimeout(a)),i.removeEventListener("mousemove",f),1!==m&&(v=e.clientX,c=e.clientY,i.addEventListener("mousemove",f),a=setTimeout(function(){!function e(t,n){if(a&&(a=clearTimeout(a)),Math.abs(v-u)+Math.abs(c-s) operation && typeof operation === 'object'); } if (options.operation && typeof options.operation === 'object') { return [ options.operation ]; } return []; } /** * Capture one editor-relative selection snapshot before a batched mutation. * * @param {Object|null} editorHost Active schema editor host. * @param {Object} options Executor options. * @returns {Object|null} Saved cursor/selection snapshot. */ function captureSelectionSnapshot(editorHost, options = {}) { if ( options.restoreCursor === false || !editorHost || typeof editorHost.isSelectionInEditor !== 'function' || typeof editorHost.saveCursorPosition !== 'function' || !editorHost.isSelectionInEditor() ) { return null; } return editorHost.saveCursorPosition(); } /** * Resolve the shared list tracker module. * * @returns {Object|null} List tracker module when loaded. */ function getListTrackerModule() { return SFE.ListBlockTracker || null; } /** * Resolve one live tracker for an editor-bound list root. * * @param {Object|null} editorHost Active schema editor host. * @returns {Object|null} Active list tracker. */ function getTrackerForEditor(editorHost) { const listTracker = getListTrackerModule(); if (!listTracker || !editorHost || !isListElement(editorHost.element)) { return null; } if (editorHost.element._mwpListTracker?.listElement) { return editorHost.element._mwpListTracker; } if (listTracker.active?.listElement === editorHost.element) { return listTracker.active; } if (typeof listTracker.init === 'function') { return listTracker.init(editorHost.element, editorHost.originalBlock || {}); } return null; } /** * Sync one editor instance to the tracker's current root list element. * * Root list type changes replace the editable element. All editor-facing * callers should update their element reference through this helper instead * of assuming the original root still exists. * * @param {Object|null} editorHost Active schema editor host. * @param {Object|null} tracker Active list tracker. * @returns {void} */ function syncEditorRoot(editorHost, tracker) { if (!editorHost || !tracker?.listElement) { return; } if (editorHost.element !== tracker.listElement) { editorHost.element = tracker.listElement; } if (typeof editorHost.syncPlaceholderState === 'function') { editorHost.syncPlaceholderState(); } } /** * Restore one saved editor-relative selection after a batched mutation. * * @param {Object|null} editorHost Active schema editor host. * @param {Object|null} savedSelection Saved cursor/selection snapshot. * @returns {void} */ function restoreSelectionSnapshot(editorHost, savedSelection) { if ( !savedSelection || !editorHost || typeof editorHost.restoreCursorPosition !== 'function' ) { return; } try { if (typeof editorHost.element?.focus === 'function') { editorHost.element.focus(); } } catch (error) { // Focus failures are non-fatal; selection restore below may still work. } editorHost.restoreCursorPosition(savedSelection); } /** * Resolve the current list element targeted by one list-editor selection. * * When the selection is inside a nested list item, the target is that * list item's immediate parent list. If the selection cannot be mapped to * a concrete list item, fall back to the editor root list itself. * * @param {Object|null} editorHost Active schema editor host. * @returns {HTMLElement|null} Target list element. */ function getCurrentListElement(editorHost) { if (!editorHost || !isListElement(editorHost.element)) { return null; } const currentListItem = typeof editorHost.getCurrentListItem === 'function' ? editorHost.getCurrentListItem() : null; if (currentListItem?.parentElement && isListElement(currentListItem.parentElement)) { return currentListItem.parentElement; } const currentList = typeof editorHost.getParentList === 'function' ? editorHost.getParentList() : null; return isListElement(currentList) ? currentList : editorHost.element; } /** * Resolve the schema/public-api list path for one concrete list element. * * @param {Object|null} tracker Active list tracker. * @param {HTMLElement|null} listElement Concrete list element. * @returns {string} Root-relative list path. */ function getListPathForElement(tracker, listElement) { const listTracker = getListTrackerModule(); if (!listTracker || !tracker?.listElement || !isListElement(listElement)) { return ''; } if (typeof listTracker.getPathForList === 'function') { return String(listTracker.getPathForList(tracker, listElement) || ''); } return listElement === tracker.listElement ? '' : ''; } /** * Resolve the schema/public-api item path for one concrete list item. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement|null} listItem Concrete list item. * @returns {string} Root-relative item path. */ function getListItemPathForElement(tracker, listItem) { const listTracker = getListTrackerModule(); if ( !listTracker || !tracker?.listElement || !listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI' || typeof listTracker.getPathIndexesForItem !== 'function' ) { return ''; } const pathIndexes = listTracker.getPathIndexesForItem(tracker, listItem); return Array.isArray(pathIndexes) && pathIndexes.length ? pathIndexes.join('_') : ''; } /** * Resolve the current runtime UUID for one concrete list element. * * @param {Object|null} tracker Active list tracker. * @param {HTMLElement|null} listElement Concrete list element. * @returns {string} Session-scoped list UUID. */ function getListRuntimeUuidForElement(tracker, listElement) { const listTracker = getListTrackerModule(); if ( !listTracker || !tracker?.listElement || !isListElement(listElement) || typeof listTracker.getRuntimeUuidForList !== 'function' ) { return ''; } return String(listTracker.getRuntimeUuidForList(tracker, listElement) || ''); } /** * Resolve the current runtime UUID for one concrete list item. * * @param {Object|null} tracker Active list tracker. * @param {HTMLLIElement|null} listItem Concrete list item. * @returns {string} Session-scoped item UUID. */ function getListItemRuntimeUuidForElement(tracker, listItem) { const listTracker = getListTrackerModule(); if ( !listTracker || !tracker?.listElement || !listItem || listItem.nodeType !== Node.ELEMENT_NODE || listItem.tagName !== 'LI' || typeof listTracker.getRuntimeUuidForItem !== 'function' ) { return ''; } return String(listTracker.getRuntimeUuidForItem(tracker, listItem) || ''); } /** * Return the schema-declared editor operations for one active editor. * * @param {Object|null} editorHost Active schema editor host. * @returns {Object[]} Supported schema operation descriptors. */ function getEditorOperations(editorHost) { return Array.isArray(editorHost?.options?.operations) ? editorHost.options.operations.filter(operation => operation && typeof operation === 'object') : []; } /** * Return one component map for the current active editor session. * * @param {Object|null} editorState Active editor state. * @returns {Object} Component metadata keyed by component ID. */ function getActiveEditorComponentMap(editorState) { if (!editorState || !Array.isArray(editorState.editableComponents)) { return {}; } return editorState.editableComponents.reduce((map, component) => { const componentId = String(component?.id || '').trim(); if (!componentId || !component?.element) { return map; } map[componentId] = component; return map; }, {}); } /** * Normalize one candidate string array. * * @param {*} rawValues Candidate values. * @returns {string[]} Trimmed non-empty strings. */ function normalizeStringArray(rawValues) { return Array.isArray(rawValues) ? rawValues .map(value => String(value || '').trim()) .filter(Boolean) : []; } /** * Return one normalized link capability object for the supplied format token. * * @param {Object} inlineCapabilities Available inline format capabilities. * @param {string} formatToken Inline format token. * @returns {Object} Normalized capability data. */ function getInlineLinkCapability(inlineCapabilities, formatToken) { const capability = inlineCapabilities?.[formatToken] && typeof inlineCapabilities[formatToken] === 'object' ? inlineCapabilities[formatToken] : {}; return { ...capability, tag: String(capability.tag || '').trim().toLowerCase(), allowedTargets: normalizeStringArray(capability.allowedTargets), allowedRelTokens: normalizeStringArray(capability.allowedRelTokens).map(value => value.toLowerCase()), allowedProtocols: normalizeStringArray(capability.allowedProtocols).map(value => value.toLowerCase()), autoProtocol: typeof capability.autoProtocol === 'string' ? capability.autoProtocol.trim().toLowerCase() : '', allowsRelativeUrls: capability.allowsRelativeUrls !== false, allowsAnchorLinks: capability.allowsAnchorLinks !== false, preservesUnknownRelTokens: capability.preservesUnknownRelTokens === true, }; } /** * Return whether one candidate attribute payload contains link-like data. * * @param {Object|null} candidateAttributes Candidate link payload. * @returns {boolean} True when link-like keys are present. */ function componentRunAttributesContainLinkData(candidateAttributes) { if (!candidateAttributes || typeof candidateAttributes !== 'object') { return false; } if ( Object.prototype.hasOwnProperty.call(candidateAttributes, 'href') || Object.prototype.hasOwnProperty.call(candidateAttributes, 'url') || Object.prototype.hasOwnProperty.call(candidateAttributes, 'target') || Object.prototype.hasOwnProperty.call(candidateAttributes, 'rel') ) { return true; } return Boolean(candidateAttributes.settings && typeof candidateAttributes.settings === 'object'); } /** * Resolve one raw per-format link attribute payload. * * @param {Object|null} rawFormatAttributes Raw format attribute map. * @param {string} formatToken Current link format token. * @param {string[]} linkFormatTokens Active link format tokens. * @returns {Object} Raw link attribute payload. */ function getComponentRunLinkFormatAttributes(rawFormatAttributes, formatToken, linkFormatTokens) { const source = rawFormatAttributes && typeof rawFormatAttributes === 'object' ? rawFormatAttributes : {}; const directAttributes = source[formatToken]; if (directAttributes && typeof directAttributes === 'object' && !Array.isArray(directAttributes)) { return directAttributes; } if (!Array.isArray(linkFormatTokens) || linkFormatTokens.length !== 1) { return {}; } const misplacedToken = Object.keys(source).find(token => { if (String(token || '').trim() === formatToken) { return false; } return componentRunAttributesContainLinkData(source[token]); }); return misplacedToken ? source[misplacedToken] : {}; } /** * Return whether the supplied rel token is allowed for one link capability. * * @param {string} relToken Candidate rel token. * @param {Object} capability Normalized link capability data. * @returns {boolean} True when the token is allowed. */ function linkCapabilityAllowsRelToken(relToken, capability) { const token = String(relToken || '').trim().toLowerCase(); if (!token) { return false; } return !capability.allowedRelTokens.length || capability.allowedRelTokens.includes(token) || capability.preservesUnknownRelTokens; } /** * Apply one model-facing link settings payload onto raw link attributes. * * @param {Object|null} linkAttributes Current sanitized attributes. * @param {Object|null} settings Model-facing settings payload. * @param {Object} capability Normalized link capability data. * @returns {Object} Merged link attributes. */ function mergeComponentLinkSettingsIntoAttributes(linkAttributes, settings, capability) { const merged = linkAttributes && typeof linkAttributes === 'object' ? { ...linkAttributes } : {}; const source = settings && typeof settings === 'object' ? settings : {}; if (Object.prototype.hasOwnProperty.call(source, 'new_tab')) { if (source.new_tab && capability.allowedTargets.includes('_blank')) { merged.target = '_blank'; } else if (!source.new_tab && merged.target === '_blank') { delete merged.target; } } if (Object.prototype.hasOwnProperty.call(source, 'no_follow')) { const relTokens = Array.from(new Set( String(merged.rel || '') .split(/\s+/) .map(value => String(value || '').trim().toLowerCase()) .filter(Boolean) )); if (source.no_follow && linkCapabilityAllowsRelToken('nofollow', capability)) { relTokens.push('nofollow'); } else if (!source.no_follow) { const nextTokens = relTokens.filter(token => token !== 'nofollow'); relTokens.length = 0; relTokens.push(...nextTokens); } if (relTokens.length) { merged.rel = Array.from(new Set(relTokens)).join(' '); } else { delete merged.rel; } } return merged; } /** * Sanitize one candidate link href against schema capability metadata. * * @param {*} rawHref Candidate raw href or url value. * @param {Object} capability Normalized link capability data. * @returns {string} Sanitized href or an empty string. */ function sanitizeComponentLinkHref(rawHref, capability) { const value = String(rawHref || '').trim(); if (!value) { return ''; } if (value.startsWith('#')) { return capability.allowsAnchorLinks ? value : ''; } if (/^(\/|\.\/|\.\.\/)/.test(value)) { return capability.allowsRelativeUrls ? value : ''; } const protocolMatch = value.match(/^([a-z][a-z0-9+.-]*):/i); if (protocolMatch) { const protocol = String(protocolMatch[1] || '').trim().toLowerCase(); return !capability.allowedProtocols.length || capability.allowedProtocols.includes(protocol) ? value : ''; } if (capability.autoProtocol) { return `${capability.autoProtocol}://${value}`; } return capability.allowsRelativeUrls ? value : ''; } /** * Normalize one raw run format-attribute map into canonical FrontEdit shape. * * @param {Object|null} rawFormatAttributes Raw per-format attribute map. * @param {string[]} formats Active sanitized format tokens. * @param {Object} inlineCapabilities Available inline format capabilities. * @returns {Object} Canonical format-attribute map. */ function normalizeComponentRunFormatAttributes(rawFormatAttributes, formats, inlineCapabilities) { const source = rawFormatAttributes && typeof rawFormatAttributes === 'object' ? rawFormatAttributes : {}; const normalized = {}; const linkFormatTokens = Array.from(new Set( (Array.isArray(formats) ? formats : []) .map(value => String(value || '').trim()) .filter(token => getInlineLinkCapability(inlineCapabilities, token).tag === 'a') )); formats.forEach(formatToken => { const capability = getInlineLinkCapability(inlineCapabilities, formatToken); if (capability.tag !== 'a') { return; } const rawLinkAttributes = getComponentRunLinkFormatAttributes(source, formatToken, linkFormatTokens); if (!rawLinkAttributes || typeof rawLinkAttributes !== 'object') { return; } let linkAttributes = {}; const href = sanitizeComponentLinkHref(rawLinkAttributes.href || rawLinkAttributes.url || '', capability); if (href) { linkAttributes.href = href; } const target = String(rawLinkAttributes.target || '').trim(); if (!capability.allowedTargets.length || capability.allowedTargets.includes(target)) { if (target) { linkAttributes.target = target; } } if (rawLinkAttributes.settings && typeof rawLinkAttributes.settings === 'object') { linkAttributes = mergeComponentLinkSettingsIntoAttributes( linkAttributes, rawLinkAttributes.settings, capability ); } const rel = String(rawLinkAttributes.rel || '').trim(); if (rel) { const normalizedRel = Array.from(new Set( rel .split(/\s+/) .map(value => String(value || '').trim().toLowerCase()) .filter(token => token && linkCapabilityAllowsRelToken(token, capability)) )); if (normalizedRel.length) { linkAttributes.rel = normalizedRel.join(' '); } } if (Object.keys(linkAttributes).length) { normalized[formatToken] = linkAttributes; } }); return normalized; } /** * Normalize one raw structured run list into canonical FrontEdit component runs. * * @param {*} rawRuns Candidate raw run list. * @param {Object} inlineCapabilities Available inline format capabilities. * @returns {Array} Canonical structured runs. */ function normalizeComponentRuns(rawRuns, inlineCapabilities) { if (!Array.isArray(rawRuns)) { return []; } return rawRuns.reduce((runs, run) => { if (!run || typeof run !== 'object' || !Object.prototype.hasOwnProperty.call(run, 'text')) { return runs; } const text = String(run.text || ''); const formats = Array.isArray(run.formats) ? Array.from(new Set( run.formats .map(value => String(value || '').trim()) .filter(token => token && Object.prototype.hasOwnProperty.call(inlineCapabilities || {}, token)) )) : []; const rawFormatAttributes = run.formatAttributes && typeof run.formatAttributes === 'object' ? run.formatAttributes : (run.format_attributes && typeof run.format_attributes === 'object' ? run.format_attributes : {}); runs.push({ text, formats, formatAttributes: normalizeComponentRunFormatAttributes(rawFormatAttributes, formats, inlineCapabilities), }); return runs; }, []); } /** * Read one normalized host-link attribute snapshot from a component element. * * @param {Element|null} element Current host element. * @returns {Object} Existing host link attributes. */ function getExistingComponentHostLinkAttributes(element) { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return {}; } const href = String(element.getAttribute('href') || '').trim(); const target = String(element.getAttribute('target') || '').trim(); const rel = String(element.getAttribute('rel') || '').trim(); const attributes = {}; if (href) { attributes.href = href; } if (target) { attributes.target = target; } if (rel) { attributes.rel = rel; } return attributes; } /** * Build one canonical host-link attribute payload from an operation input. * * @param {Object} operationInput Raw operation payload. * @param {Object} capability Normalized link capability data. * @param {Object} operation Resolved schema operation metadata. * @param {Element} element Host component element. * @returns {Object|null} Canonical link attributes or null when invalid. */ function normalizeComponentHostLinkOperationPayload(operationInput, capability, operation, element) { const payload = operationInput.link && typeof operationInput.link === 'object' ? operationInput.link : (operationInput.attributes && typeof operationInput.attributes === 'object' && !Array.isArray(operationInput.attributes) ? operationInput.attributes : (operationInput.value && typeof operationInput.value === 'object' && !Array.isArray(operationInput.value) ? operationInput.value : operationInput)); let nextAttributes = operation?.preserveUnspecifiedAttributes === false ? {} : getExistingComponentHostLinkAttributes(element); let didReceivePayload = false; if (Object.prototype.hasOwnProperty.call(payload, 'href') || Object.prototype.hasOwnProperty.call(payload, 'url')) { didReceivePayload = true; const href = sanitizeComponentLinkHref(payload.href || payload.url || '', capability); if (href) { nextAttributes.href = href; } else { delete nextAttributes.href; } } if (Object.prototype.hasOwnProperty.call(payload, 'target') || Object.prototype.hasOwnProperty.call(payload, 'linkTarget')) { didReceivePayload = true; const target = String(payload.target || payload.linkTarget || '').trim(); if (!target) { delete nextAttributes.target; } else if (!capability.allowedTargets.length || capability.allowedTargets.includes(target)) { nextAttributes.target = target; } } if (Object.prototype.hasOwnProperty.call(payload, 'rel')) { didReceivePayload = true; const rel = String(payload.rel || '').trim(); if (!rel) { delete nextAttributes.rel; } else { const normalizedRel = Array.from(new Set( rel .split(/\s+/) .map(value => String(value || '').trim().toLowerCase()) .filter(token => token && linkCapabilityAllowsRelToken(token, capability)) )); if (normalizedRel.length) { nextAttributes.rel = normalizedRel.join(' '); } else { delete nextAttributes.rel; } } } const settings = payload.settings && typeof payload.settings === 'object' ? { ...payload.settings } : {}; if (Object.prototype.hasOwnProperty.call(payload, 'new_tab')) { settings.new_tab = payload.new_tab; didReceivePayload = true; } if (Object.prototype.hasOwnProperty.call(payload, 'no_follow')) { settings.no_follow = payload.no_follow; didReceivePayload = true; } if (Object.keys(settings).length) { didReceivePayload = true; nextAttributes = mergeComponentLinkSettingsIntoAttributes(nextAttributes, settings, capability); } return didReceivePayload ? nextAttributes : null; } /** * Apply one normalized host-link attribute payload to a component element. * * @param {Element|null} element Host component element. * @param {Object} linkAttributes Canonical link attributes. * @returns {boolean} True when the mutation applied. */ function applyComponentHostLinkAttributes(element, linkAttributes) { if (!element || element.nodeType !== Node.ELEMENT_NODE || !linkAttributes || typeof linkAttributes !== 'object') { return false; } const href = String(linkAttributes.href || '').trim(); const target = String(linkAttributes.target || '').trim(); const rel = String(linkAttributes.rel || '').trim(); if (href) { element.setAttribute('href', href); if ('href' in element) { element.href = href; } } else { element.removeAttribute('href'); } if (target) { element.setAttribute('target', target); } else { element.removeAttribute('target'); } if (rel) { element.setAttribute('rel', rel); } else { element.removeAttribute('rel'); } return true; } /** * Flatten one line-based rich-text payload into canonical runs. * * This remains an undocumented compatibility input so callers such as ABE * can migrate to direct runs later without FrontEdit reimplementing broker logic * outside the shared executor. * * @param {*} rawLines Candidate raw line list. * @returns {Array} Flat run list with literal newline separators. */ function flattenComponentLineRuns(rawLines) { if (!Array.isArray(rawLines)) { return []; } return rawLines.reduce((runs, line, index) => { if (index > 0) { runs.push({ text: '\n', formats: [], formatAttributes: {}, }); } if (Array.isArray(line?.runs)) { runs.push(...line.runs); } return runs; }, []); } /** * Return the lowercase host tag name for one component element. * * @param {Element|null} hostElement Candidate host element. * @returns {string} Lowercase host tag name. */ function getComponentHostTagName(hostElement) { return hostElement && hostElement.tagName ? String(hostElement.tagName || '').trim().toLowerCase() : ''; } /** * Build one inline format wrapper for a component run token. * * @param {Object} inlineCapabilities Available inline format capabilities. * @param {string} formatToken Format token. * @param {Object|null} formatAttributes Optional format attributes. * @returns {HTMLElement|null} Wrapper element. */ function buildComponentFormatWrapper(inlineCapabilities, formatToken, formatAttributes = null) { const tagName = String(inlineCapabilities?.[formatToken]?.tag || '').trim().toLowerCase(); if (!tagName) { return null; } const element = document.createElement(tagName); if (tagName === 'a' && formatAttributes && typeof formatAttributes === 'object') { const href = String(formatAttributes.href || '').trim(); if (href) { element.setAttribute('href', href); } const target = String(formatAttributes.target || '').trim(); if (target) { element.setAttribute('target', target); } const rel = String(formatAttributes.rel || '').trim(); if (rel) { element.setAttribute('rel', rel); } } return element; } /** * Hoist same-tag inline format attributes onto one host component element. * * @param {Array} runs Structured text runs. * @param {Object} inlineCapabilities Available inline format capabilities. * @param {Element|null} hostElement Live host element. * @returns {{runs:Array,hostAttributes:Object}} Hoisted run data. */ function hoistComponentHostLevelFormats(runs, inlineCapabilities, hostElement = null) { const hostTagName = getComponentHostTagName(hostElement); if (!hostTagName) { return { runs: Array.isArray(runs) ? runs : [], hostAttributes: {}, }; } const hostAttributes = {}; const normalizedRuns = (Array.isArray(runs) ? runs : []).map(run => { const formats = Array.isArray(run?.formats) ? run.formats : []; const formatAttributes = run?.formatAttributes && typeof run.formatAttributes === 'object' ? { ...run.formatAttributes } : {}; const nextFormats = []; formats.forEach(formatToken => { const formatTagName = String(inlineCapabilities?.[formatToken]?.tag || '').trim().toLowerCase(); if (!formatTagName || formatTagName !== hostTagName) { nextFormats.push(formatToken); return; } const attributes = formatAttributes?.[formatToken]; if (attributes && typeof attributes === 'object' && !Array.isArray(attributes)) { Object.assign(hostAttributes, attributes); } delete formatAttributes[formatToken]; }); return { ...run, formats: nextFormats, formatAttributes, }; }); return { runs: normalizedRuns, hostAttributes, }; } /** * Apply hoisted inline-format attributes onto one host element. * * @param {Element|null} hostElement Live host element. * @param {Object|null} hostAttributes Hoisted host attributes. * @returns {void} */ function applyComponentHostLevelFormatAttributes(hostElement, hostAttributes = null) { if (!hostElement || typeof hostElement.setAttribute !== 'function' || typeof hostElement.removeAttribute !== 'function') { return; } const attributes = hostAttributes && typeof hostAttributes === 'object' ? hostAttributes : {}; ['href', 'target', 'rel'].forEach(attributeName => { const value = typeof attributes[attributeName] === 'string' ? attributes[attributeName].trim() : ''; if (value) { hostElement.setAttribute(attributeName, value); return; } hostElement.removeAttribute(attributeName); }); } /** * Build deterministic HTML from one normalized component run sequence. * * @param {Array} runs Structured text runs. * @param {Object} inlineCapabilities Available inline format capabilities. * @param {Object} editorOptions Runtime editor options. * @param {Element|null} hostElement Live host element. * @returns {{html:string,hostAttributes:Object}} Rendered HTML and host attrs. */ function buildComponentHtmlFromRuns(runs, inlineCapabilities, editorOptions = {}, hostElement = null) { const container = document.createElement('div'); const extractionOptions = editorOptions?.options && typeof editorOptions.options === 'object' ? editorOptions.options : {}; const hoisted = hoistComponentHostLevelFormats(runs, inlineCapabilities, hostElement); (Array.isArray(hoisted.runs) ? hoisted.runs : []).forEach(run => { const text = String(run?.text || ''); if (!text) { return; } const segments = extractionOptions.newlinesToBR === true || String(editorOptions?.enterMode || '').trim() === 'linebreak' ? text.split('\n') : [ text ]; segments.forEach((segment, index) => { if (index > 0) { container.appendChild(document.createElement('br')); } if (!segment) { return; } let node = document.createTextNode(segment); (Array.isArray(run.formats) ? run.formats : []).forEach(formatToken => { const wrapper = buildComponentFormatWrapper( inlineCapabilities, formatToken, run.formatAttributes && typeof run.formatAttributes === 'object' ? run.formatAttributes[formatToken] || null : null ); if (!wrapper) { return; } wrapper.appendChild(node); node = wrapper; }); container.appendChild(node); }); }); return { html: container.innerHTML, hostAttributes: hoisted.hostAttributes, }; } /** * Build one plain-text replacement string from normalized runs. * * @param {Array} runs Structured text runs. * @returns {string} Joined plain-text content. */ function buildPlainTextFromComponentRuns(runs) { return (Array.isArray(runs) ? runs : []) .map(run => String(run?.text || '')) .join(''); } /** * Resolve one schema component-content operation from executor options. * * @param {Object|null} editorState Active editor state. * @param {Object} options Executor options. * @returns {Object|null} Matching component operation metadata. */ function resolveComponentOperation(editorState, options = {}) { const operationInput = options.operation && typeof options.operation === 'object' ? options.operation : options; const componentId = String( operationInput.componentId || operationInput.component || operationInput.component_id || '' ).trim(); if (!componentId) { return null; } const component = getActiveEditorComponentMap(editorState)[componentId] || null; const editorOperations = Array.isArray(component?.editorOptions?.operations) ? component.editorOptions.operations.filter(operation => operation && typeof operation === 'object') : []; const suppliedKind = String(operationInput.kind || '').trim(); if (suppliedKind === 'replace_component_content' || suppliedKind === 'text_rewrite') { return { ...operationInput, component: componentId, kind: 'replace_component_content', }; } if (suppliedKind === 'link_change' || suppliedKind === 'inline_link_change') { return { ...operationInput, component: componentId, kind: 'link_change', }; } const suppliedOperationId = String( operationInput.id || operationInput.operationId || '' ).trim(); if (!suppliedOperationId) { return null; } const matched = editorOperations.find(operation => ( String(operation?.id || '').trim() === suppliedOperationId && ( String(operation?.kind || '').trim() === 'text_rewrite' || String(operation?.kind || '').trim() === 'replace_component_content' || String(operation?.kind || '').trim() === 'link_change' ) )) || null; if (!matched) { return null; } return { ...matched, component: componentId, kind: String(matched.kind || '').trim() === 'link_change' ? 'link_change' : 'replace_component_content', }; } /** * Execute one or more schema-backed component-content replacements. * * @param {Object} options Executor options. * @returns {Object|null} Applied-operation summary. */ function executeComponentOperations(options = {}) { const editorState = options.editorState || null; const editorHost = options.editorHost || null; const componentMap = getActiveEditorComponentMap(editorState); const inputOperations = Array.isArray(options.operations) ? options.operations : [ options.operation || options ]; if (!editorState || !editorHost || !Object.keys(componentMap).length || !inputOperations.length) { return null; } const savedSelection = captureSelectionSnapshot(editorHost, options); const results = []; const updatedComponentIds = []; inputOperations.forEach(inputOperation => { const operationInput = inputOperation && typeof inputOperation === 'object' && !Array.isArray(inputOperation) ? inputOperation : {}; const operation = resolveComponentOperation(editorState, operationInput); if (!operation) { return; } const componentId = String(operation.component || '').trim(); const component = componentMap[componentId] || null; if (!component || !component.element) { return; } const inlineCapabilities = ( component.editorOptions?.inlineFormatCapabilities && typeof component.editorOptions.inlineFormatCapabilities === 'object' ) ? component.editorOptions.inlineFormatCapabilities : {}; if (operation.kind === 'link_change') { const formatToken = String(operation.format || operationInput.format || '').trim(); const capability = getInlineLinkCapability(inlineCapabilities, formatToken); const hostTagName = String(component.element?.tagName || '').trim().toLowerCase(); if (capability.tag !== 'a' || hostTagName !== capability.tag) { return; } const linkAttributes = normalizeComponentHostLinkOperationPayload( operationInput, capability, operation, component.element ); if (!linkAttributes || !applyComponentHostLinkAttributes(component.element, linkAttributes)) { return; } } else { const bindingSource = String( operationInput.bindingSource || component?.bindingSource || '' ).trim().toLowerCase(); if (bindingSource !== 'html' && bindingSource !== 'plaintext') { return; } const rawRuns = Array.isArray(operationInput.runs) ? operationInput.runs : flattenComponentLineRuns(operationInput.lines); const hasDirectTextPayload = Object.prototype.hasOwnProperty.call(operationInput, 'text'); const runs = normalizeComponentRuns( hasDirectTextPayload && bindingSource === 'plaintext' && !rawRuns.length ? [ { text: String(operationInput.text || ''), formats: [], formatAttributes: {} } ] : rawRuns, inlineCapabilities ); const hasReplacementPayload = hasDirectTextPayload || Array.isArray(operationInput.runs) || Array.isArray(operationInput.lines); if (!hasReplacementPayload) { return; } if (bindingSource === 'plaintext') { component.element.textContent = hasDirectTextPayload && !rawRuns.length ? String(operationInput.text || '') : buildPlainTextFromComponentRuns(runs); } else { const rendered = buildComponentHtmlFromRuns( runs, inlineCapabilities, component.editorOptions || {}, component.element ); component.element.innerHTML = rendered.html; applyComponentHostLevelFormatAttributes(component.element, rendered.hostAttributes); } } updatedComponentIds.push(componentId); results.push({ id: String(operation.id || '').trim(), kind: operation.kind === 'link_change' ? 'link_change' : 'replace_component_content', componentId, applied: true, }); }); if (!results.length) { return null; } restoreSelectionSnapshot(editorHost, savedSelection); if (typeof options.afterSync === 'function') { options.afterSync({ editorState, editorHost, results, updatedComponentIds: Array.from(new Set(updatedComponentIds)), }); } if (typeof editorHost.updateToolbarState === 'function') { editorHost.updateToolbarState(); } const historyApi = getSessionHistoryApiForHost(editorHost); if (options.saveHistory !== false && typeof historyApi?.saveToHistory === 'function') { historyApi.saveToHistory(); } return { results, updatedComponentIds: Array.from(new Set(updatedComponentIds)), operationsApplied: results .map(result => result.id || result.kind) .filter(Boolean), }; } /** * Resolve one schema component-media operation from executor options. * * @param {Object|null} editorState Active editor state. * @param {Object} options Executor options. * @returns {Object|null} Matching component-media operation metadata. */ function resolveMediaOperation(editorState, options = {}) { const operationInput = options.operation && typeof options.operation === 'object' ? options.operation : options; const componentId = String( operationInput.componentId || operationInput.component || operationInput.component_id || '' ).trim(); if (!componentId) { return null; } const component = getActiveEditorComponentMap(editorState)[componentId] || null; if (!component?.element || !component?.mediaDescriptor) { return null; } const suppliedKind = String(operationInput.kind || '').trim(); if (suppliedKind === 'replace_component_media') { return { ...operationInput, component: componentId, kind: 'replace_component_media', }; } return null; } /** * Execute one or more schema-backed component-media replacements through the * active media-session host. * * @param {Object} options Executor options. * @returns {Object|null} Applied-operation summary. */ function executeMediaOperations(options = {}) { const editorState = options.editorState || null; const editorHost = options.editorHost || null; const componentMap = getActiveEditorComponentMap(editorState); const inputOperations = Array.isArray(options.operations) ? options.operations : [ options.operation || options ]; if ( !editorState || !editorHost || typeof editorHost.applyMediaSelection !== 'function' || !Object.keys(componentMap).length || !inputOperations.length ) { return null; } const activeComponentId = String( editorState.activeComponentId || editorState.activeEditableComponent?.id || '' ).trim(); const results = []; const updatedComponentIds = []; inputOperations.forEach(inputOperation => { const operationInput = inputOperation && typeof inputOperation === 'object' && !Array.isArray(inputOperation) ? inputOperation : {}; const operation = resolveMediaOperation(editorState, operationInput); if (!operation) { return; } const componentId = String(operation.component || '').trim(); const component = componentMap[componentId] || null; if (!component?.element || !component?.mediaDescriptor) { return; } if (activeComponentId && activeComponentId !== componentId) { return; } if (!activeComponentId && editorHost.element && editorHost.element !== component.element) { return; } const payload = operationInput.media && typeof operationInput.media === 'object' ? operationInput.media : (operationInput.value && typeof operationInput.value === 'object' ? operationInput.value : operationInput); const url = String(payload?.url || '').trim(); if (!url) { return; } const attachmentId = Object.prototype.hasOwnProperty.call(payload, 'attachmentId') ? payload.attachmentId : (Object.prototype.hasOwnProperty.call(payload, 'attachment_id') ? payload.attachment_id : null); const source = String( payload?.source || payload?.fromState || '' ).trim().toLowerCase() === 'library' ? 'library' : 'input'; const didApply = editorHost.applyMediaSelection(url, attachmentId, { fromState: source, }); if (didApply === false) { return; } updatedComponentIds.push(componentId); results.push({ id: String(operation.id || '').trim(), kind: 'replace_component_media', componentId, applied: true, }); }); if (!results.length) { return null; } if (typeof options.afterSync === 'function') { options.afterSync({ editorState, editorHost, results, updatedComponentIds: Array.from(new Set(updatedComponentIds)), }); } return { results, updatedComponentIds: Array.from(new Set(updatedComponentIds)), operationsApplied: results .map(result => result.id || result.kind) .filter(Boolean), }; } /** * Normalize one primitive list-operation kind to the canonical * tracker/editor operation kind. * * @param {string} kindRaw Candidate operation kind. * @returns {string} Canonical primitive operation kind or an empty string. */ function normalizePrimitiveListOperationKind(kindRaw) { const kind = String(kindRaw || '').trim().toLowerCase(); const supportedKinds = new Set([ 'insert_list_item', 'remove_list_item', 'move_list_item', 'indent_list_item', 'outdent_list_item', 'update_list_item_text', 'toggle_list_type', ]); return supportedKinds.has(kind) ? kind : ''; } /** * Normalize one public list-operation kind to the documented runtime API * vocabulary. * * Public callers intentionally use a higher-level token family than the * primitive tracker/editor operations so the external contract stays explicit * while FrontEdit keeps the low-level DOM mutation layer internal. * * @param {string} kindRaw Candidate operation kind. * @returns {string} Canonical public operation kind or an empty string. */ function normalizePublicApiListOperationKind(kindRaw) { const kind = String(kindRaw || '').trim().toLowerCase(); const supportedKinds = new Set([ 'update_list_item_text', 'insert_before', 'insert_after', 'insert_child', 'remove_list_item', 'move_before', 'move_after', 'indent_list_item', 'outdent_list_item', 'toggle_list_type', ]); return supportedKinds.has(kind) ? kind : ''; } /** * Normalize one list-operation kind for the current execution surface. * * Public API calls use the higher-level UUID-oriented vocabulary, while * internal editor/schema calls keep the primitive tracker operation kinds. * * @param {string} kindRaw Candidate operation kind. * @param {Object} options Executor options. * @returns {string} Canonical operation kind or an empty string. */ function normalizeListOperationKind(kindRaw, options = {}) { return getListTargetResolutionMode(options) === 'api_uuid' ? normalizePublicApiListOperationKind(kindRaw) : normalizePrimitiveListOperationKind(kindRaw); } /** * Return whether one operation payload explicitly defines a given key. * * @param {Object|null} operation Candidate operation payload. * @param {string} key Key to check. * @returns {boolean} True when the key exists on the payload. */ function hasOwnOperationKey(operation, key) { return !!( operation && typeof operation === 'object' && !Array.isArray(operation) && Object.prototype.hasOwnProperty.call(operation, key) ); } /** * Return the caller target-resolution mode for list operations. * * `selection` allows editor-originated calls to omit the item/list target and * resolve it from the current DOM selection. `explicit` requires path/list * tokens. `api_uuid` requires the public runtime UUID token family. * * @param {Object} options Executor options. * @returns {string} `selection`, `explicit`, or `api_uuid`. */ function getListTargetResolutionMode(options = {}) { const mode = String(options.targetResolutionMode || '').trim().toLowerCase(); if (mode === 'explicit') { return 'explicit'; } if (mode === 'api_uuid') { return 'api_uuid'; } return 'selection'; } /** * Return whether one normalized list operation targets a list item. * * @param {string} kind Canonical list operation kind. * @returns {boolean} True when the operation acts on one item path. */ function isItemTargetListOperation(kind) { return ( kind === 'remove_list_item' || kind === 'indent_list_item' || kind === 'outdent_list_item' || kind === 'update_list_item_text' || kind === 'move_list_item' ); } /** * Return whether one normalized list operation uses structural item-path * anchors without using only one direct `path`. * * @param {string} kind Canonical list operation kind. * @returns {boolean} True when the operation uses before/after/parent paths. */ function isPathAnchoredListOperation(kind) { return ( kind === 'insert_list_item' || kind === 'move_list_item' ); } /** * Return whether one normalized list operation moves an existing item. * * @param {string} kind Canonical list operation kind. * @returns {boolean} True when the operation moves one existing item. */ function isMoveListOperation(kind) { return kind === 'move_list_item'; } /** * Return whether one insert/move operation defines any supported path anchor. * * @param {Object} operationInput Raw operation payload. * @returns {boolean} True when one path anchor exists. */ function hasAnyStructuralPathAnchor(operationInput) { return ( hasOwnOperationKey(operationInput, 'path') || hasOwnOperationKey(operationInput, 'itemPath') || hasOwnOperationKey(operationInput, 'item_path') || hasOwnOperationKey(operationInput, 'beforePath') || hasOwnOperationKey(operationInput, 'before_path') || hasOwnOperationKey(operationInput, 'afterPath') || hasOwnOperationKey(operationInput, 'after_path') || hasOwnOperationKey(operationInput, 'parentPath') || hasOwnOperationKey(operationInput, 'parent_path') ); } /** * Return whether one raw operation includes any public UUID list-item target. * * @param {Object} operationInput Raw operation payload. * @returns {boolean} True when one item UUID token exists. */ function hasAnyUuidItemTarget(operationInput) { return ( hasOwnOperationKey(operationInput, 'itemUuid') || hasOwnOperationKey(operationInput, 'newItemUuid') || hasOwnOperationKey(operationInput, 'targetItemUuid') ); } /** * Return whether one public operation payload contains any path fields. * * @param {Object} operationInput Raw operation payload. * @returns {boolean} True when a path token exists. */ function hasAnyPathTarget(operationInput) { return hasAnyStructuralPathAnchor(operationInput) || hasOwnOperationKey(operationInput, 'listPath') || hasOwnOperationKey(operationInput, 'list_path'); } /** * Return whether one public payload contains only the documented keys for one * public list-operation kind. * * Unknown keys are rejected. The public list API is intentionally strict so * callers must match the documented v1 shape exactly. * * @param {Object} operationInput Raw operation payload. * @param {string} normalizedKind Canonical public operation kind. * @returns {boolean} True when no unsupported keys are present. */ function hasOnlySupportedApiUuidKeys(operationInput, normalizedKind) { const allowedKeys = new Set([ 'kind', 'contentHtml', 'contentText', ]); if ( normalizedKind === 'update_list_item_text' || normalizedKind === 'remove_list_item' || normalizedKind === 'indent_list_item' || normalizedKind === 'outdent_list_item' ) { allowedKeys.add('itemUuid'); } else if (normalizedKind === 'insert_before' || normalizedKind === 'insert_after') { allowedKeys.add('newItemUuid'); allowedKeys.add('targetItemUuid'); } else if (normalizedKind === 'insert_child') { allowedKeys.add('newItemUuid'); allowedKeys.add('targetItemUuid'); } else if (normalizedKind === 'move_before' || normalizedKind === 'move_after') { allowedKeys.add('itemUuid'); allowedKeys.add('targetItemUuid'); } else if (normalizedKind === 'toggle_list_type') { allowedKeys.add('itemUuid'); } return Object.keys(operationInput || {}).every(key => allowedKeys.has(key)); } /** * Validate that one explicit public list operation uses only the documented * target token family for its kind. * * @param {Object} operationInput Raw operation payload. * @param {string} normalizedKind Canonical list operation kind. * @returns {boolean} True when the target token shape is valid. */ function isValidExplicitListOperationTarget(operationInput, normalizedKind) { const hasPath = hasOwnOperationKey(operationInput, 'path') || hasOwnOperationKey(operationInput, 'itemPath') || hasOwnOperationKey(operationInput, 'item_path'); const hasListPath = hasOwnOperationKey(operationInput, 'listPath') || hasOwnOperationKey(operationInput, 'list_path'); if (normalizedKind === 'toggle_list_type') { return hasListPath && !hasPath; } if (isItemTargetListOperation(normalizedKind)) { return hasPath && !hasListPath; } if (isMoveListOperation(normalizedKind)) { return hasPath && !hasListPath; } if (isPathAnchoredListOperation(normalizedKind)) { return hasAnyStructuralPathAnchor(operationInput) && !hasListPath; } return true; } /** * Validate that one public API list operation uses only the UUID token family. * * API callers must treat runtime UUIDs as the external cursor model. Paths * remain internal execution details resolved by FrontEdit immediately before each * operation runs. * * @param {Object} operationInput Raw operation payload. * @param {string} normalizedKind Canonical list operation kind. * @returns {boolean} True when the UUID target shape is valid. */ function isValidApiUuidListOperationTarget(operationInput, normalizedKind) { if (!hasOnlySupportedApiUuidKeys(operationInput, normalizedKind)) { return false; } const hasItemUuid = hasOwnOperationKey(operationInput, 'itemUuid'); const hasNewItemUuid = hasOwnOperationKey(operationInput, 'newItemUuid'); const hasListUuid = hasOwnOperationKey(operationInput, 'listUuid'); const hasTargetItemUuid = hasOwnOperationKey(operationInput, 'targetItemUuid'); if (normalizedKind === 'toggle_list_type') { return hasItemUuid && !hasNewItemUuid && !hasListUuid && !hasTargetItemUuid; } if ( normalizedKind === 'update_list_item_text' || normalizedKind === 'remove_list_item' || normalizedKind === 'indent_list_item' || normalizedKind === 'outdent_list_item' ) { return hasItemUuid && !hasNewItemUuid && !hasListUuid && !hasTargetItemUuid; } if (normalizedKind === 'insert_before' || normalizedKind === 'insert_after') { return !hasItemUuid && hasNewItemUuid && !hasListUuid && hasTargetItemUuid; } if (normalizedKind === 'insert_child') { return !hasItemUuid && hasNewItemUuid && !hasListUuid && hasTargetItemUuid; } if (normalizedKind === 'move_before' || normalizedKind === 'move_after') { return hasItemUuid && !hasNewItemUuid && !hasListUuid && hasTargetItemUuid; } return false; } /** * Expand one public API list operation into one or more public operation * inputs that can be resolved against the live tree one step at a time. * * `insert_child` intentionally fans out into `insert_after` and `indent_list_item` so * the second primitive resolution runs after the new list item exists. * * @param {Object} operationInput Raw public operation payload. * @returns {Object[]} Expanded public operation inputs. */ function expandApiUuidListOperationInputs(operationInput) { const normalizedKind = normalizePublicApiListOperationKind(operationInput?.kind); if (!normalizedKind) { return []; } if (normalizedKind !== 'insert_child') { return [ { ...operationInput, kind: normalizedKind, }, ]; } return [ { ...operationInput, kind: 'insert_after', }, { kind: 'indent_list_item', itemUuid: operationInput.newItemUuid, }, ]; } /** * Resolve one public API UUID-targeted list operation to one internal * primitive operation payload with concrete path tokens. * * This runs against the current live mutated tree so later batched operations * can reuse the same stable runtime UUIDs while FrontEdit recalculates fresh paths * before each mutation. * * @param {Object|null} tracker Active list tracker. * @param {Object} operation Normalized public operation payload. * @returns {Object|null} Path-resolved primitive tracker operation payload. */ function resolveApiUuidListOperationTargets(tracker, operation) { const listTracker = getListTrackerModule(); if (!listTracker || !tracker?.listElement || !operation || typeof operation !== 'object') { return null; } const publicKind = normalizePublicApiListOperationKind(operation.kind); if (!publicKind) { return null; } const resolvedOperation = { ...operation, }; const itemUuid = String(resolvedOperation.itemUuid ?? '').trim(); const newItemUuid = String(resolvedOperation.newItemUuid ?? '').trim(); const listUuid = String(resolvedOperation.listUuid ?? '').trim(); const targetItemUuid = String(resolvedOperation.targetItemUuid ?? '').trim(); let targetItem = null; let currentItem = null; if (itemUuid) { currentItem = typeof listTracker.getItemByRuntimeUuid === 'function' ? listTracker.getItemByRuntimeUuid(tracker, itemUuid) : null; const targetPath = getListItemPathForElement(tracker, currentItem); if (!targetPath) { return null; } resolvedOperation.path = targetPath; } if (targetItemUuid) { targetItem = typeof listTracker.getItemByRuntimeUuid === 'function' ? listTracker.getItemByRuntimeUuid(tracker, targetItemUuid) : null; if (!targetItem) { return null; } } if (listUuid) { const targetList = typeof listTracker.getListByRuntimeUuid === 'function' ? listTracker.getListByRuntimeUuid(tracker, listUuid) : null; if (!targetList) { return null; } resolvedOperation.listPath = getListPathForElement(tracker, targetList); } if (newItemUuid) { resolvedOperation.newItemUuid = newItemUuid; resolvedOperation.itemUuid = newItemUuid; } if ( publicKind === 'update_list_item_text' || publicKind === 'remove_list_item' || publicKind === 'indent_list_item' || publicKind === 'outdent_list_item' ) { resolvedOperation.kind = publicKind; } else if (publicKind === 'insert_before' || publicKind === 'insert_after') { const targetPath = getListItemPathForElement(tracker, targetItem); if (!targetPath) { return null; } resolvedOperation.kind = 'insert_list_item'; if (publicKind === 'insert_before') { resolvedOperation.beforePath = targetPath; } else { resolvedOperation.afterPath = targetPath; } } else if (publicKind === 'move_before' || publicKind === 'move_after') { const targetPath = getListItemPathForElement(tracker, targetItem); if (!targetPath) { return null; } resolvedOperation.kind = 'move_list_item'; if (publicKind === 'move_before') { resolvedOperation.beforePath = targetPath; } else { resolvedOperation.afterPath = targetPath; } } else if (publicKind === 'toggle_list_type') { const containingList = currentItem?.parentElement; const containingListPath = getListPathForElement(tracker, containingList); if (!containingListPath && containingList !== tracker.listElement) { return null; } resolvedOperation.listPath = containingListPath; resolvedOperation.kind = 'toggle_list_type'; } else { return null; } delete resolvedOperation.listUuid; delete resolvedOperation.targetItemUuid; return resolvedOperation; } /** * Resolve one schema list-structure operation from executor options. * * @param {Object|null} editorHost Active schema editor host. * @param {Object} options Executor options. * @returns {Object|null} Matching list operation metadata. */ function resolveListOperation(editorHost, options = {}, executorOptions = {}) { if (options.operation && typeof options.operation === 'object') { const suppliedOperation = options.operation; const suppliedKind = normalizeListOperationKind(suppliedOperation.kind, executorOptions); if (suppliedKind) { return { ...suppliedOperation, kind: suppliedKind, }; } const suppliedOperationId = String( suppliedOperation.id || suppliedOperation.operationId || '' ).trim(); if (suppliedOperationId) { return getEditorOperations(editorHost).find(operation => ( String(operation?.id || '').trim() === suppliedOperationId && !!normalizePrimitiveListOperationKind(operation?.kind) )) || null; } } const operationId = String(options.operationId || options.id || '').trim(); if (!operationId) { return null; } return getEditorOperations(editorHost).find(operation => ( String(operation?.id || '').trim() === operationId && !!normalizePrimitiveListOperationKind(operation?.kind) )) || null; } /** * Resolve one schema block-attribute operation from executor options. * * @param {Object|null} editorHost Active schema editor host. * @param {Object} options Executor options. * @returns {Object|null} Matching block-attribute operation. */ function resolveBlockAttributeOperation(editorHost, options = {}) { if (options.operation && typeof options.operation === 'object') { const suppliedOperation = options.operation; if (String(suppliedOperation.kind || '').trim() === 'block_attribute_change') { return suppliedOperation; } const suppliedOperationId = String( suppliedOperation.id || suppliedOperation.operationId || '' ).trim(); if (suppliedOperationId) { return getEditorOperations(editorHost).find(operation => ( String(operation?.id || '').trim() === suppliedOperationId && String(operation?.kind || '').trim() === 'block_attribute_change' )) || null; } } const operationId = String(options.operationId || options.id || '').trim(); if (!operationId) { return null; } return getEditorOperations(editorHost).find(operation => ( String(operation?.id || '').trim() === operationId && String(operation?.kind || '').trim() === 'block_attribute_change' )) || null; } /** * Build one executable structural list operation for the tracker layer. * * Toolbar actions may provide only a schema operation ID, while public-api * callers generally provide canonical payload kinds. This helper resolves * either shape and fills in the active item/list path when the caller is * targeting the current selection. * * @param {Object|null} editorHost Active schema editor host. * @param {Object|null} tracker Active list tracker. * @param {Object} input Raw executor operation input. * @returns {Object|null} Normalized tracker operation payload. */ function buildExecutableListOperation(editorHost, tracker, input = {}, options = {}) { const operationInput = input && typeof input === 'object' && !Array.isArray(input) ? input : {}; const resolvedOperation = resolveListOperation(editorHost, operationInput, options); const baseOperation = resolvedOperation && typeof resolvedOperation === 'object' ? resolvedOperation : operationInput; const normalizedKind = normalizeListOperationKind(baseOperation.kind || operationInput.kind, options); if (!normalizedKind) { return null; } const targetResolutionMode = getListTargetResolutionMode(options); if ( targetResolutionMode === 'explicit' && !isValidExplicitListOperationTarget(operationInput, normalizedKind) ) { return null; } if ( targetResolutionMode === 'api_uuid' && !isValidApiUuidListOperationTarget(operationInput, normalizedKind) ) { return null; } const mergedOperation = { ...baseOperation, ...operationInput, kind: normalizedKind, }; if (targetResolutionMode === 'api_uuid') { delete mergedOperation.listItem; delete mergedOperation.listElement; return resolveApiUuidListOperationTargets(tracker, mergedOperation); } const explicitListItem = operationInput.listItem instanceof Element && operationInput.listItem.tagName === 'LI' ? operationInput.listItem : null; const currentListItem = explicitListItem || ( typeof editorHost?.getCurrentListItem === 'function' ? editorHost.getCurrentListItem() : null ); const itemPath = String( mergedOperation.path ?? mergedOperation.itemPath ?? mergedOperation.item_path ?? '' ).trim(); if ( !itemPath && normalizedKind !== 'insert_list_item' && normalizedKind !== 'move_list_item' && normalizedKind !== 'toggle_list_type' ) { if (targetResolutionMode === 'explicit') { return null; } const currentItemPath = getListItemPathForElement(tracker, currentListItem); if (!currentItemPath) { return null; } mergedOperation.path = currentItemPath; } if (normalizedKind === 'toggle_list_type') { const targetList = operationInput.listElement || getCurrentListElement(editorHost); const listPath = String( mergedOperation.listPath ?? mergedOperation.list_path ?? '' ).trim(); if (!listPath && targetResolutionMode === 'explicit') { return null; } if (!listPath && targetList) { mergedOperation.listPath = getListPathForElement(tracker, targetList); } } delete mergedOperation.listItem; delete mergedOperation.listElement; return mergedOperation; } /** * Return the explicit current value for one tracked block attribute. * * @param {Object|null} editorHost Active schema editor host. * @param {string} attributePath Canonical block attribute path. * @returns {*} Tracked value when present. */ function getTrackedAttributeValue(editorHost, attributePath) { if (!editorHost || typeof editorHost.getTrackedAttributeValue !== 'function') { return undefined; } return editorHost.getTrackedAttributeValue(attributePath); } /** * Return the current heading level represented by the live editor element. * * @param {Object|null} editorHost Active schema editor host. * @returns {number|null} Current heading level when applicable. */ function getCurrentHeadingLevel(editorHost) { const trackedLevel = getTrackedAttributeValue(editorHost, 'level'); const parsedTrackedLevel = Number.parseInt(trackedLevel, 10); if (Number.isInteger(parsedTrackedLevel) && parsedTrackedLevel >= 1 && parsedTrackedLevel <= 6) { return parsedTrackedLevel; } const tagName = String(editorHost?.element?.tagName || '').trim().toLowerCase(); if (/^h[1-6]$/.test(tagName)) { return Number.parseInt(tagName.slice(1), 10); } return null; } /** * Return the current textAlignment represented by the schema-backed editor state. * * @param {Object|null} editorHost Active schema editor host. * @param {Object|null} operation Schema block-attribute operation metadata. * @returns {string|undefined} Current normalized textAlignment value. */ function getCurrentTextAlignmentValue(editorHost, operation = null) { const textAlignmentCapability = getNormalizedTextAlignmentCapability(editorHost, operation); if (!textAlignmentCapability || !isTextAlignmentOperation(editorHost, operation)) { return undefined; } const isVirtualBindingBackedTextAlignment = isVirtualBindingBackedTextAlignmentCapability(textAlignmentCapability); if ( isVirtualBindingBackedTextAlignment && editorHost && typeof editorHost.getTextAlignmentState === 'function' ) { const domTextAlignment = String(editorHost.getTextAlignmentState() || '').trim().toLowerCase(); if (domTextAlignment) { return domTextAlignment; } return textAlignmentCapability.normalizedUnsetValue; } const trackedTextAlignment = getTrackedAttributeValue(editorHost, textAlignmentCapability.attribute); if (typeof trackedTextAlignment === 'string' && trackedTextAlignment.trim()) { return trackedTextAlignment.trim().toLowerCase(); } if (typeof trackedTextAlignment === 'undefined') { if (editorHost && typeof editorHost.getTextAlignmentState === 'function') { const domTextAlignment = String(editorHost.getTextAlignmentState() || '').trim().toLowerCase(); if (domTextAlignment) { return domTextAlignment; } } return textAlignmentCapability.normalizedUnsetValue; } return textAlignmentCapability.normalizedUnsetValue; } /** * Determines whether the given textAlignment capability relies on a virtual data-binding * (explicitly checking if the attribute is strictly named 'textAlignment'). * * @param {Object|null|undefined} textAlignmentCapability - The textAlignment capability configuration object to evaluate. * @returns {boolean} True if the capability is backed by a virtual binding; otherwise false. */ function isVirtualBindingBackedTextAlignmentCapability(textAlignmentCapability) { return !!( textAlignmentCapability && ( textAlignmentCapability.attribute === 'textAlignment' || textAlignmentCapability.attribute === 'columnAlignment' ) ); } /** * Determine whether one resolved block-attribute operation is the active * schema-backed textAlignment operation for this editor. * * @param {Object|null} editorHost Active schema editor host. * @param {Object|null} operation Schema block-attribute operation metadata. * @returns {boolean} True when this operation targets textAlignment. */ function isTextAlignmentOperation(editorHost, operation = null) { if (!operation || typeof operation !== 'object') { return false; } const textAlignmentCapability = getNormalizedTextAlignmentCapability(editorHost, operation); if (!textAlignmentCapability) { return false; } const operationAttribute = typeof operation.attribute === 'string' ? operation.attribute.trim() : ''; return !!operationAttribute && operationAttribute === textAlignmentCapability.attribute; } /** * Return the normalized schema-backed textAlignment capability for one editor. * * Centralizes the canonical textAlignment attribute lookup so both the executor and * editor mutation helpers read the same normalized shape. * * @param {Object|null} editorHost Active schema editor host. * @returns {Object|null} Normalized textAlignment capability or null. */ function getNormalizedTextAlignmentCapability(editorHost, operation = null) { if (!editorHost || typeof editorHost.getAttributeCapability !== 'function') { return null; } const operationAttribute = typeof operation?.attribute === 'string' ? operation.attribute.trim() : ''; const capabilityKey = operationAttribute === 'columnAlignment' ? 'columnAlignment' : 'textAlignment'; const textAlignmentCapability = editorHost.getAttributeCapability(capabilityKey); if (!textAlignmentCapability || typeof textAlignmentCapability !== 'object') { return null; } const attribute = typeof textAlignmentCapability.attribute === 'string' ? textAlignmentCapability.attribute.trim() : ''; if (!attribute) { return null; } const values = Array.isArray(textAlignmentCapability.values) ? textAlignmentCapability.values .map(value => String(value).trim().toLowerCase()) .filter(Boolean) : []; const hasUnsetValue = Object.prototype.hasOwnProperty.call(textAlignmentCapability, 'unsetValue'); const unsetValue = hasUnsetValue ? textAlignmentCapability.unsetValue : undefined; const normalizedUnsetValue = typeof unsetValue === 'string' ? unsetValue.trim().toLowerCase() : unsetValue; return { ...textAlignmentCapability, attribute, values, unsetValue, normalizedUnsetValue, }; } /** * Normalize one candidate block-attribute value for unset-value comparison. * * String-based schema values such as alignment tokens compare case-insensitively, * while numeric/boolean/null values preserve their original scalar semantics. * * @param {*} value Candidate attribute value. * @returns {*} Normalized comparison value. */ function normalizeBlockAttributeComparisonValue(value) { return typeof value === 'string' ? value.trim().toLowerCase() : value; } /** * Determine whether one requested block-attribute value maps to the schema's * declared unset/default value. * * @param {Object|null} descriptor Operation/capability metadata with optional `unsetValue`. * @param {*} value Candidate attribute value. * @returns {boolean} True when the value represents the unset state. */ function isUnsetBlockAttributeValue(descriptor, value) { if (!descriptor || !Object.prototype.hasOwnProperty.call(descriptor, 'unsetValue')) { return false; } return normalizeBlockAttributeComparisonValue(value) === normalizeBlockAttributeComparisonValue(descriptor.unsetValue); } /** * Normalize one requested block-attribute value for tracked attr storage. * * Schema-declared unset values are converted to `undefined` so the canonical * save path removes the corresponding attribute instead of persisting the * default marker itself. * * @param {Object|null} descriptor Operation/capability metadata with optional `unsetValue`. * @param {*} value Requested attribute value. * @returns {*} Storage-ready tracked value. */ function normalizeBlockAttributeTrackedValue(descriptor, value) { return isUnsetBlockAttributeValue(descriptor, value) ? undefined : value; } /** * Return the current block-attribute value for one schema operation. * * @param {Object|null} editorHost Active schema editor host. * @param {Object|null} operation Schema block-attribute operation metadata. * @returns {*} Current effective attribute value. */ function getCurrentBlockAttributeValue(editorHost, operation = null) { const attributePath = String(operation?.attribute || '').trim(); if (!attributePath) { return undefined; } if (attributePath === 'level') { return getCurrentHeadingLevel(editorHost); } if (operation?.tagChange === true) { return String(editorHost?.element?.tagName || '').trim().toLowerCase() || undefined; } if (attributePath === 'align' && typeof editorHost?.getBlockAlignState === 'function') { return editorHost.getBlockAlignState(); } if (isTextAlignmentOperation(editorHost, operation)) { return getCurrentTextAlignmentValue(editorHost, operation); } const trackedValue = getTrackedAttributeValue(editorHost, attributePath); if (typeof trackedValue !== 'undefined') { return trackedValue; } return Object.prototype.hasOwnProperty.call(operation || {}, 'unsetValue') ? operation.unsetValue : undefined; } /** * Apply one schema-backed block-attribute change to the live editor DOM. * * @param {Object|null} editorHost Active schema editor host. * @param {Object} operation Schema block-attribute operation metadata. * @param {*} value Requested attribute value. * @returns {boolean} True when the mutation was applied. */ function applyBlockAttributeChange(editorHost, operation, value, operationOptions = {}) { if (!editorHost || !operation) { return false; } const attributePath = String(operation.attribute || '').trim(); if (!attributePath) { return false; } if (attributePath === 'level') { const level = Number.parseInt(value, 10); if (!Number.isInteger(level) || level < 1 || level > 6) { return false; } if ( typeof editorHost.changeElementTag !== 'function' || typeof editorHost.setTrackedAttributeValue !== 'function' ) { return false; } // Keep the block attribute as the canonical source of truth, then // project that change into the live DOM tag for immediate feedback. editorHost.setTrackedAttributeValue(attributePath, level); editorHost.changeElementTag(`h${level}`, { validateSelection: false, saveHistory: false, }); return true; } if (operation.tagChange === true) { const tagName = String(value || '').trim().toLowerCase(); const allowedTags = Array.isArray(operation.values) ? operation.values.map(candidate => String(candidate || '').trim().toLowerCase()).filter(Boolean) : []; if (!tagName || !allowedTags.includes(tagName)) { return false; } if ( typeof editorHost.changeElementTag !== 'function' || typeof editorHost.setTrackedAttributeValue !== 'function' ) { return false; } editorHost.setTrackedAttributeValue(attributePath, tagName); editorHost.changeElementTag(tagName, { validateSelection: false, saveHistory: false, }); return true; } if (isTextAlignmentOperation(editorHost, operation)) { if (typeof editorHost.changeTextAlignment !== 'function') { return false; } const targetKey = operation.attribute === 'columnAlignment' ? 'columnAlignment' : 'textAlignment'; const textAlignmentOptions = { operation, targetKey, }; if ( operation.attribute === 'columnAlignment' && Object.prototype.hasOwnProperty.call(operationOptions || {}, 'columns') ) { textAlignmentOptions.columns = operationOptions.columns; } return editorHost.changeTextAlignment( String(value || '').trim().toLowerCase(), textAlignmentOptions ) === true; } if (attributePath === 'align') { if (typeof editorHost.changeBlockAlign !== 'function') { return false; } return editorHost.changeBlockAlign(String(value || '').trim().toLowerCase(), { operation, targetKey: 'align', }) === true; } if (typeof editorHost.setTrackedAttributeValue !== 'function') { return false; } editorHost.setTrackedAttributeValue( attributePath, normalizeBlockAttributeTrackedValue(operation, value) ); return true; } /** * Return whether one public column-alignment target payload matches the * committed runtime contract. * * The public API accepts only a zero-based index array or the `'all'` * shortcut. Scalar values intentionally fail loudly so external callers do * not depend on undocumented coercion. * * @param {*} columns Candidate explicit column target payload. * @returns {boolean} True when the payload matches the committed contract. */ function isValidExplicitColumnTarget(columns) { if (columns === 'all') { return true; } return Array.isArray(columns); } /** * Execute one or more schema-declared block-attribute changes. * * @param {Object} options Executor options. * @returns {Object|null} Applied-operation summary. */ function executeBlockAttributeOperations(options = {}) { const editorHost = options.editorHost || null; const inputOperations = Array.isArray(options.operations) ? options.operations : [ options.operation || options ]; const resolvedOperations = inputOperations .map(inputOperation => { const operationOptions = ( inputOperation && typeof inputOperation === 'object' && !Array.isArray(inputOperation) ) ? inputOperation : {}; const operation = resolveBlockAttributeOperation(editorHost, operationOptions); if (!operation) { return null; } const isExplicitColumnAlignmentOperation = ( String(operation.attribute || '').trim() === 'columnAlignment' ); const isExplicitSetColumnAlign = String(operation.id || '').trim() === 'set_column_align'; const hasColumn = Object.prototype.hasOwnProperty.call(operationOptions, 'column'); const hasColumns = Object.prototype.hasOwnProperty.call(operationOptions, 'columns'); if (isExplicitColumnAlignmentOperation && hasColumn) { return null; } if (isExplicitSetColumnAlign && !hasColumns) { return null; } if ( isExplicitColumnAlignmentOperation && hasColumns && !isValidExplicitColumnTarget(operationOptions.columns) ) { return null; } const operationValue = Object.prototype.hasOwnProperty.call(operationOptions, 'value') ? operationOptions.value : undefined; return operationValue === undefined ? null : { operation, value: operationValue, options: operationOptions, }; }) .filter(Boolean); if (!editorHost || !resolvedOperations.length) { return null; } const savedSelection = captureSelectionSnapshot(editorHost, options); const results = []; resolvedOperations.forEach(({ operation, value, options: operationOptions }) => { const hasExplicitColumnTarget = ( String(operation.attribute || '').trim() === 'columnAlignment' && Object.prototype.hasOwnProperty.call(operationOptions || {}, 'columns') ); const currentValue = hasExplicitColumnTarget ? undefined : getCurrentBlockAttributeValue(editorHost, operation); const normalizedRequestedValue = String(operation.attribute || '').trim() === 'level' ? Number.parseInt(value, 10) : value; const normalizedCurrentValue = String(operation.attribute || '').trim() === 'level' ? Number.parseInt(currentValue, 10) : currentValue; if (normalizedCurrentValue === normalizedRequestedValue) { results.push({ id: String(operation.id || '').trim(), kind: 'block_attribute_change', attribute: String(operation.attribute || '').trim(), value: normalizedRequestedValue, applied: false, }); return; } const didApply = applyBlockAttributeChange( editorHost, operation, normalizedRequestedValue, operationOptions || {} ); if (!didApply) { return; } results.push({ id: String(operation.id || '').trim(), kind: 'block_attribute_change', attribute: String(operation.attribute || '').trim(), value: normalizedRequestedValue, applied: true, }); }); if (!results.length) { return null; } restoreSelectionSnapshot(editorHost, savedSelection); if (typeof options.afterSync === 'function') { options.afterSync({ editorHost, results, operations: resolvedOperations.map(entry => entry.operation), }); } if (typeof editorHost.updateToolbarState === 'function') { editorHost.updateToolbarState(); } const historyApi = getSessionHistoryApiForHost(editorHost); if ( results.some(result => result.applied) && options.saveHistory !== false && typeof historyApi?.saveToHistory === 'function' ) { historyApi.saveToHistory(); } return { results, operationsApplied: results .filter(result => result.applied) .map(result => result.id || result.kind), attributeChanges: editorHost.attributeChanges || {}, }; } /** * Execute one schema-declared block-attribute change. * * @param {Object} options Executor options. * @returns {Object|null} Applied-operation summary. */ function executeBlockAttributeOperation(options = {}) { return executeBlockAttributeOperations({ ...options, operations: undefined, operation: options.operation || options, }); } /** * Resolve the session-owned history API for one active editor host. * * Schema operations should commit through the shared block session directly * instead of depending on MWPEditor's convenience history facade as an * integration boundary. * * @param {Object|null} editorHost Active editor host. * @returns {Object|null} Session history API, or null. */ function getSessionHistoryApiForHost(editorHost) { if (editorHost?.historyApi && typeof editorHost.historyApi === 'object') { return editorHost.historyApi; } if (typeof editorHost?.getSessionHistoryApi === 'function') { return editorHost.getSessionHistoryApi(); } const blockEditSession = editorHost?.blockEditSession || null; if (!blockEditSession || typeof blockEditSession.getHistoryApi !== 'function') { return null; } return blockEditSession.getHistoryApi( String(editorHost?.blockEditSessionScopeId || '').trim() || 'text' ); } /** * Execute one or more list operations through the canonical tracker path. * * This method intentionally disables per-operation history saves and cursor * restores inside the lower-level tracker/editors, then records one history * step after the full batch succeeds. * * @param {Object} options Executor options. * @returns {Object|null} Applied-operation summary. */ function executeListOperations(options = {}) { const listTracker = getListTrackerModule(); const editorHost = options.editorHost || null; const tracker = options.tracker || getTrackerForEditor(editorHost); const inputOperations = normalizeOperations(options); const savedSelection = captureSelectionSnapshot(editorHost, options); if ( !listTracker || typeof listTracker.applyOperation !== 'function' || typeof listTracker.getStructure !== 'function' || !tracker?.listElement || !inputOperations.length ) { return null; } const results = []; const operations = []; const appliedOperationKinds = []; for (let index = 0; index < inputOperations.length; index++) { const rawOperation = inputOperations[index]; const targetResolutionMode = getListTargetResolutionMode(options); const expandedInputs = targetResolutionMode === 'api_uuid' ? expandApiUuidListOperationInputs(rawOperation) : [ rawOperation ]; if (!expandedInputs.length) { return null; } for (let expandedIndex = 0; expandedIndex < expandedInputs.length; expandedIndex++) { const operation = buildExecutableListOperation( editorHost, tracker, expandedInputs[expandedIndex], options ); if (!operation) { return null; } const result = listTracker.applyOperation(tracker, operation, { editorHost, saveHistory: false, restoreCursor: false, }); if (!result) { return null; } operations.push(operation); results.push(result); } const appliedKind = normalizeListOperationKind(rawOperation?.kind, options); if (!appliedKind) { return null; } appliedOperationKinds.push(appliedKind); } syncEditorRoot(editorHost, tracker); restoreSelectionSnapshot(editorHost, savedSelection); if (typeof options.afterSync === 'function') { options.afterSync({ tracker, editorHost, results, operations, }); } if (typeof editorHost?.updateToolbarState === 'function') { editorHost.updateToolbarState(); } const historyApi = getSessionHistoryApiForHost(editorHost); if (options.saveHistory !== false && typeof historyApi?.saveToHistory === 'function') { historyApi.saveToHistory(); } return { results, operationsApplied: appliedOperationKinds, structure: listTracker.getStructure(tracker), }; } /** * Execute one list-type change against the currently selected list. * * @param {Object} options Executor options. * @returns {Object|null} Applied-operation summary. */ function executeCurrentListTypeChange(options = {}) { const editorHost = options.editorHost || null; const tracker = options.tracker || getTrackerForEditor(editorHost); const targetList = options.listElement || getCurrentListElement(editorHost); if (!tracker?.listElement || !targetList) { return null; } return executeListOperations({ tracker, editorHost, saveHistory: options.saveHistory !== false, afterSync: options.afterSync, operations: [ { kind: 'toggle_list_type', listPath: getListPathForElement(tracker, targetList), value: options.value, }, ], }); } SFE.SchemaOperationExecutor = { executeComponentOperation: function executeComponentOperation(options = {}) { return executeComponentOperations({ ...options, operations: undefined, operation: options.operation || options, }); }, executeComponentOperations, executeMediaOperation: function executeMediaOperation(options = {}) { return executeMediaOperations({ ...options, operations: undefined, operation: options.operation || options, }); }, executeMediaOperations, executeBlockAttributeOperation, executeBlockAttributeOperations, executeListOperations, executeCurrentListTypeChange, getTrackerForEditor, getCurrentListElement, getListPathForElement, syncEditorRoot, isUnsetBlockAttributeValue, normalizeBlockAttributeTrackedValue, getTextAlignmentCapability: getNormalizedTextAlignmentCapability, getNormalizedTextAlignmentCapability, isTextAlignmentOperation, }; })(); “Wagering Origins, Risk, Regulation – BTX Clinical Research

The Origins Of The Particular Casino: A Voyage Through History”

These times players can appreciate various games, from more than 1400 slots to the great sportsbook. Similar to numerous other contemporary casinos, this user also functions since a resort, along with a fantastic health spa and multiple restaurants. It continues broadening even today in addition to provides gamblers together with a chance in order to relax after enjoying their favourite games. While people may participate in lotteries, most varieties of gambling were banned. That guided to unlawful gambling houses where nobles would gather to be able to play various online games with significant gambling bets, enjoy dance exhibits, and drink. Due to this, typically the Venetian authorities determined to open their particular own casino within 1638.

  • After all, what is the point associated with placing bets otherwise to win some extra cash?
  • With AI-powered virtual assistants, participants can enjoy immediate assistance and problem resolution.
  • Instead of the common hearts, clubs, spades and diamonds the symbols on these types of cards (shown below) are hunting-themed sides, dog collars, canine leashes, and nooses.
  • You will immediately find full entry to each of our online casino forum/chat plus receive each of our newsletter with reports & exclusive bonus deals every month.

In 1941, the first online casino to legally work, El Rancho Vegas, marked the start of modern online casino culture in Las Vegas. Blackjack can be a fixture in Usa casinos and trente et quarante within the French. Other card games are seldom played out in casino wagering, although most American casinos have table games in the contact form of poker alternatives such as Carribbean stud. They quite often also offer standard poker tables, where patrons play each other while the casino makes it is profit either by simply taking a portion regarding each pot or perhaps by charging an hourly fee.

A Brief Overview Associated With The Origins Associated With Casinos

These Casinos have supplied engaging and immersive gambling experiences available to people inside their own homes. One of the best online Casinos that provides a wonderful experience is Spin and rewrite Palace Casino. They are the commanders in online casinos, due to their huge assortment of high-quality games, and also their focus on customer service mostbet login.

  • The economy involving Las Vegas is almost entirely dependent in the top, luxurious internet casinos who have operated there since the late nineteen forties.
  • Casinos invariably is an iconic contact form of entertainment plus have” “a wealthy and colorful history.
  • In the 17th millennium, the first modern-style casinos began to be able to appear in Italy.
  • It was built in typically the 1760s in the grounds of Navegante House, a looking lodge inherited simply by James Caulfield (1728–99), first earl of Charlemont.

Astragali, small dog bones used in divination, are thought to have been used as an early form of dice. Gambling houses would 1st come in ancient China, as the Romans would certainly be the very first to invent betting chips. After this initial wave involving legitimized, legalized gaming institutions, gambling began making inroads around Europe over the next century. In Edwardian England, a good 1845 ruling legalized skill-based gambling and opened the way for the particular nation’s current gambling establishment scene.

The History Of Internet Casinos: A Gambling Den Using A Colorful Past

With the introduction of new technologies, such as virtual reality, and even advances in unnatural intelligence, casinos are usually going become a lot more interactive and impressive than ever ahead of. However, as betting started to become a lot more popular, the need for the more pleasant and respectable environment regarding players to wager in began in order to emerge. The very first official casino exposed up in Monte-carlo in the late 19th century, and even this was your commence of the modern-day casino industry.

Even today, the gambling establishment is still about in the similar building and maintains the fabulous décor. It includes different exciting games, such as poker, blackjack, and slots. Wiesbaden Kurhaus is undoubtedly a single of the the majority of impressive historic internet casinos. An early type of baccarat existed in the 13th c., and a new prototype of Pai Gow was played out within the 10th chemical.

Join Instantly Along With Your Social Account

At this specific time, they have been painted by hand, luxury, goods; imported into Spain and Italy simply by Islamic Mamluk empire merchants, who managed the trade tracks between Asia plus the West. In a court record, two players had been arrested; nine greeting cards, and the woodblocks used to print them, were impounded. Even the initial cards followed some sort of ‘court’ structure; together with high cards represented by kings, queens, and vizier. Songs” “have been written about only about every playing card you can think of, such as the Joker, the Advisor of Spades, the particular Jack of Minds, the Queen of Diamonds, and a lot more. Cards have tracked down Saddam Hussein’s henchmen, designated the volunteer, and confounded audiences in the palms of a skilled magician. It is usually easy to determine betting as an action where money may be the driving force mostbet app download.

If you are usually in a on line casino anywhere around the particular planet, you may find him appropriate next to an individual, playing blackjack, roulette or texas hold’em. You might ponder why there are usually so many blackjack variants at Gambling establishment. com. The simple answer is that developers” “love to be creative in addition to players want the various options. More importantly, it’s the product of the past online games change to fit the demand involving the people. The game draws the influences from a number of card video games and cultural styles.

The Great Blackjack: From France To The Us In Addition To Beyond

Mobile internet casinos now provide a variety of features that will cater specifically to the mobile knowledge, for instance touchscreen game play and mobile settlement options. The retro mechanical slots inside brick and mortar casinos made the way for your contemporary electronic slots that are now a beloved preferred of players all around the planet. In the sixties the advance of technology allowed electronic slot machines” “to utilize a computer program to determine the outcome of every spin. While the particular ancient Oriental would have gambling residences, we were holding not deemed suitable casinos. The answer to that invented casino operators would appear later inside the beautiful Italian metropolis of Venice.

As these gadgets are more mainstream, many of us will see the introduction of highly realistic on line casino games that may make you are feeling you are right there with the casino. The emergence of AI and robotic technology can result in “Robot dealers” at Casinos which understand every on line casino game and may provide an wonderful experience. It’s not possible to overstate simply how much digital technology designed the online casinos we know today. Around the world, participants now access online casinos from their particular homes, offices, in addition to on” “the particular go. Come together on a trip from the centuries in order to explore the world of casino gaming and its particular evolution. The alteration from the really first slot machine to be able to today’s online survive dealer tables indicates why each and every convert casino players proceed to love typically the thrill of the game.

Casino

Online casinos give players using some sort of fantastic solution to knowledge gambling from the safety involving their houses. Even the social component is replicated to some extent along using live casino games and even their live shows.” “[newline]One of the very first casinos in Quotes was the Wiesbaden Kurhaus, a day spa house that will might soon attain a online casino within the 16th g. In 1771, cards games have been produced legal inside Philippines with the Challenge each other of Nassau-Usingen, forcing” “the creation on this kind of casino.

  • In truth, this is the reason why modern blackjack isn’t regarded as a online casino game with certain rules; they change from casino to casino.
  • Casino, initially, a public hall for music in addition to dancing; by the second half involving the 19th centuries, a collection associated with gaming or wagering rooms.
  • Siegel cut wages in addition to be able to poured the very own diminishing bankroll in to the property, however losses carried in and triggered the ominous rumor.
  • Casinos have got a extended and colorful background, and they possess progressed in recent years.

The exact origin of casinos is challenging to pinpoint, however they have recently been around since ancient times. The original known gambling houses date back to be able to Ancient Rome plus China, where residents would bet in various games involving chance. In historical Rome, gambling was seen as a way to pass the time, because well as to be able to generate revenue regarding the government.

Las Vegas: Birth Of The City

Seeing the significance of gambling to be able to the city’s economic climate, officials expanded internet casinos and other gaming establishments until these people became the major source of duty revenue.” “[newline]It’d be silly never to discuss Las Vegas whilst taking a historic check out casinos, wouldn’t it? This desert metropolis allures hordes of visitors each and every year, enjoying typically the city’s countless destinations and vying to win some cash on the blackjack furniture and roulette tires. Today, the world’s biggest playing card manufacturer is the particular Usa Playing Greeting card Company. The Kentucky-based company was established within 1867 and makes more than 100 mil decks every 12 months. Not surprising, if you consider that casinos change their particular cards every six to eight hours. The discards are ‘cancelled’; possibly deformed with a new drill hole, sides cut, pulped, or perhaps sold as mementos.

  • Reno will not be the world’s leisure capital, and that isn’t everyone’s initial destination.
  • The discards are ‘cancelled’; either deformed with a new drill hole, edges cut, pulped, or even sold as mementos.
  • Apart from forerunners within ancient Rome and Greece, organized endorsed sports betting dates back to the overdue 18th century.

To bet with real cash you must always be physically present inside a state where it’s permitted. We are not accountable for virtually any issues or disruptions users may encounter when accessing the linked gambling sites. A few decades ago, in order to in order to partake in a game title of chance, like poker or black jack, was in a backroom of the saloon or a hidden corner associated with an inn.

The Origin Regarding Blackjack

Typically, customers should deposit accounts together with the operators of this sort of casinos in order to wager (most American credit card issuers reject to validate online gambling transactions). By the beginning of the 21st millennium, about $25 billion annually was getting bet at some 200 Internet casinos. A large number associated with these casinos have been found in off-shore tax havens such because Antigua and Gibraltar, and many are already criticized for their insufficient supervision simply by any regulatory authorities. Whether it will be playing the slot machine games, betting on the horse, or simply taking pleasure in a game associated with cards with close friends, gambling is usually a great approach to the time. Something exciting” “regarding the anticipation of the outcome, the thrill of risk-taking, and even the satisfaction involving winning makes betting a great origin of entertainment. The development of internet gambling platforms has been a game-changer for the gambling business and has opened up up the wagering world to some much wider audience.

Even lesser bettors are offered reduced-fare transportation, hotel areas, free drinks and cigarettes while wagering, as well as other inducements. Once gambling was legalised in 1931 in The state of nevada, the casino would open once again, allowing players to be able to experience a variety of fun games. Now known as the Gold Gate Hotel & Casino, it provides slot machine games and also the sportsbook. These days, you may get a lot of outstanding regional Las Vegas casinos, although it was John F. Miller that invented the first casino in Vegas. Gambling houses have invariably been about to host these types of early, developing game titles but, due to cultural prohibitions against bets, the first elegant casinos didn’t come up until the ending with the Renaissance.

Wiesbaden Kurhaus – The First German Born Casino

Fana is the writer and content material specialist who specializes in the dynamic regarding worldwide publishing. For the past few years, her section of particular” “fascination has been inside the evolution associated with gaming laws due to her legitimate background. You can catch her going or swimming at the nearest beach any time she’s not keeping up with the most latest iGaming developments or even honing her poker abilities.

  • Overall, the future of casinos looks bright and fascinating, having a range involving new technologies and advancements that will assist the particular experience even a lot more enjoyable and immersive.
  • If you want to learn even more about this casino’s fascinating history in addition to present selection of games and services, you can check out out our Gambling establishment De Monte Carlo blog.
  • Woodblock producing to both cloth and even paper was commonplace in the Tang empire, in 7th century China.
  • Players will take advantage involving individualized gaming activities that are crafted to their passions and betting routines with AI-powered position machines, as an example.

As wood block producing techniques improved, turning out to be faster and even more efficient, more greeting cards were produced, little by little and surely finding their way together the inland Western trade routes. Today, you can chance in about any country and even continent, from the particular classic Las Vegas Strip to glamorous Macau. Finally, emerging solutions are expected to have a significant impact about the industry. For illustration, blockchain technology is usually being tested as a solution for the purpose of Smaller and more safe payment transactions.

Ancient Gambling: From Dice Games In Mesopotamia To Be Able To Roman Betting Games

Its extensive resorts boast considerable casinos offering a plethora of video games, including the classic favorites. The city’s vibrant atmosphere, top-tier entertainment, and luxurious casinos make this the go-to betting destination, drawing hundreds of thousands of visitors every year. When gambling had been legalized in 1931, Las Vegas experienced quick growth, attracting visitors desperate to test their particular luck at slot machine game machines and credit card tables.

  • From 2023, both online in addition to land-based casinos will certainly be featuring AJE technology, delivering a good unparalleled personalized and even immersive experience in order to players.
  • The exact origin of casinos is usually challenging to identify, nonetheless they have recently been around since historic times.
  • There, two guidelines were implemented that will massively superior the ball player encounter.
  • By 1993, thirteen states had legalized casinos with some others making exceptions with regard to Indigenous communities and even riverboat gambling outings.

In the 17th millennium, the first modern-style casinos began to be able to appear in Italy. European history is usually riddled with edicts, decrees, and encyclicals banning and condemning gambling, which indirectly testify to it is popularity in almost all strata of society. Organized gambling on a larger size and sanctioned by simply governments and other authorities in purchase to raise funds began in the particular 15th century with lotteries—and centuries before in China with” “keno. By the eighteenth century, casinos got become commonplace in lots of European countries.

Ai And Casino Gaming: The Near Future?

The beginnings of many Casino games come through the games involving chance which have been described by typically the Ancient Chinese and even Rome. Dice games and betting about the outcome associated with fights has been a big portion of many Western cultures. The development associated with online casinos has revolutionized the way people gamble.

  • Few rulings have had this sort of serious effect on the character, history, and wealth of a location than that 1976 Nj-new jersey referendum, on the other hand.
  • The 1st online casino began in 1994 simply by Microgaming, a application company based upon the Isle associated with Man.
  • In Europe nearly every country changed the laws in the latter half of the particular 20th century to be able to permit casinos.
  • In conjunction with its pleasure and social functions, gambling also presents typically the probability of win money.
  • Unfortunately, it absolutely was sealed in 1774 if the specialists passed the change proposal simply by Giorgio Pisani to be able to preserve the city’s holiness.
  • The Ridotto was your most famous within the early days, established in Venice within 1638.

Mobile gaming, cloud-based gaming, as well as the World wide web of Things (IoT) are some involving the technologies of which will drive advancement in the industry. With the raising penetration of cell phones, casinos are adopting mobile apps plus mobile-optimized websites to provide convenience to their particular customers. AI-powered techniques will analyze player data, including their very own playing history and betting patterns, and even provide customized recommendations and promotions in real-time. With AI-powered virtual assistants, gamers can enjoy immediate assistance and issue resolution. Just just like virtually every home experienced a computer, nearly everyone had a new mobile phone in their very own hand.

The Spread Of Casinos Across Europe

For age ranges, gamblers have loved the fantastic games and excellent interpersonal atmosphere of casinos. But many nevertheless wonder who invented the casino plus when did it most begin? Our blog will look with the history of casinos, answer where the first casino was established, and simply by who. We can also observe how internet casinos have changed by means of the ages and in many cases their future. People have long liked playing games involving chance, competing towards their peers, and even winning money. From the era associated with” “typically the ancient Egyptians roughly 5, 000 years ago onwards, there’s some sort of documented history of dice tosses, credit card games, and other types of organized betting.

  • Mobster money helped develop Vegas into the particular casino capital of the world, with the Sands, the New Frontier and the Sahara all getting built soon right after he departed.
  • As the internet casinos continue improving, it’s always good to remember who invented online casino and gave surge to a excellent technique of experiencing gambling games in one place.
  • These days and nights players can appreciate various games, from more than 1400 slots to a new great sportsbook.
  • However, gambling existed long ahead of that, supposedly even throughout the Paleolithic time period.

In the particular 17th millennium, the first modern-style casinos began to appear inside Malta. Blackjack is some sort associated with fixture in United states casinos and trente et quarante within the” “France. As the casinos continue improving, it’s always good to remember who invented on line casino and gave increase to a wonderful means of experiencing betting games in one place.

Las Vegas Gambling Houses To Casinos

Casino Para Monte Carlo was established to aid Monaco’s economic condition by attracting” “vacationers, which is exactly why citizens are restricted from entering. While initially unsuccessful, after a change in management, it grew to be one of the particular most popular internet casinos globally, even staying featured in a James Bond book. Casinos, in the type known today, possess only been around since the 17th c. However, betting existed long prior to that, supposedly actually in the Paleolithic period of time.

Now, nearly every fully acknowledged country in The european union (aside from The Vatican) permits prepared gambling. Eventually, typically the influence of typically the church over Venetian society turned well-known opinion against Arianne Ridotto and it also had been closed down. This initial casino sparked Europe’s love intended for competitive gaming, even so, and soon that they were popping up through the continent.

Las Vegas And Modern-day Casinos

In 1905 the San Pedro, Mis Angeles and Salt Lake railroad came in Las Las vegas, connecting the city with the Pacific and the country’s main rail networks. The future down-town was platted and auctioned by train company backers, plus Vegas was designed in 1911. Playing cards are perhaps the greatest gaming invention ever, simply efficiency in the form of a deck of 52 credit cards. Stay informed with the latest expertly curated and trustworthy stories from typically the casino and wagering industry.

In-depth digital worlds together with realistic graphics, sound clips, and even haptic feedback might always be made available to players in fully-fledged virtuelle realität (VR) internet casinos. The multiple pay lines, bonus game titles, and progressive jackpots were all great news for players. Most importantly, the machines didn’t need much maintenance thus casinos could run them on a greater scale. The up coming pivotal moment inside the history associated with casinos was the particular release of the Liberty Bell, the very first slot machine. Charles Fey created the machine in 1895″ “also it had three fishing reels of symbols, which includes horseshoes, diamonds, spades, hearts, and regarding course the Liberty Bell itself.

The Birth Of The Particular Casino: The Initial Casino In Venice And The Rise Of Gambling Houses In Europe

The tax stamp appeared around the Expert of Spades, showing tax have been paid. In general phrases,” “the object of the video game was going to make the best total which, as you may tell by title, was 21. A lot of the first rules for vingt-et-un were derived by another card game popular in Portugal at the moment, chemin de fer.

  • As casinos advanced over the decades, they became a lot more glamorous and obtainable to all types of gamblers, even people who were not component of the the aristocracy.
  • They extremely often present standard poker tables, where patrons play every single other while typically the casino makes it is profit either by simply taking a percentage regarding each pot or by charging a great hourly fee.
  • We will certainly also observe internet casinos have changed via the ages and even their future.
  • By the late 19th hundred years, gambling had turn out to be a common pastime within many American metropolitan areas, especially in the West.
  • Texas Has Rigid Gambling Laws The primary reason Texas doesn’t have casinos is because casino gambling –” “all kinds of casino gambling – are illegal within the state of The state of texas.

It might rapidly gain excellent reputation among the list of German community, along with many well-known visitors, such since Fyodor Dostoevsky. It is definitely not necessarily surprising that Italians would be the kinds which invented casinos, because their adore intended for gambling extends back to ancient The italian capital. Even the name casino stems through “casa”, meaning property, in addition to is employed for social clubs besides villas. While this hobereau would continue to enjoy games with their homes, this kind of was not the finish for the particular Piccolo, mainly because it would rapidly be reborn while Casino pada Venezia. The 21st-century casino can be a spot where gamblers may risk their money in opposition to a common gambler, called the bank or maybe the house.

Leave a Reply

Your email address will not be published. Required fields are marked *


Fatal error: Uncaught Error: Interface "WP_Rocket\Engine\Optimization\Minify\ProcessorInterface" not found in /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-content/plugins/wp-rocket/inc/Engine/Optimization/Minify/CSS/Minify.php:15 Stack trace: #0 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-content/plugins/elementor/vendor/composer/ClassLoader.php(576): include() #1 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-content/plugins/elementor/vendor/composer/ClassLoader.php(427): Composer\Autoload\{closure}() #2 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-content/plugins/wp-rocket/inc/Engine/Optimization/Minify/CSS/Subscriber.php(48): Composer\Autoload\ClassLoader->loadClass() #3 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/class-wp-hook.php(353): WP_Rocket\Engine\Optimization\Minify\CSS\Subscriber->process() #4 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/plugin.php(206): WP_Hook->apply_filters() #5 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-content/plugins/wp-rocket/inc/Engine/Optimization/Buffer/Optimization.php(87): apply_filters() #6 [internal function]: WP_Rocket\Engine\Optimization\Buffer\Optimization->maybe_process_buffer() #7 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/functions.php(5581): ob_end_flush() #8 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/class-wp-hook.php(353): wp_ob_end_flush_all() #9 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/class-wp-hook.php(377): WP_Hook->apply_filters() #10 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/plugin.php(523): WP_Hook->do_action() #11 /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-includes/load.php(1308): do_action() #12 [internal function]: shutdown_action_hook() #13 {main} thrown in /home/575368.cloudwaysapps.com/hhchdnqmxq/public_html/wp-content/plugins/wp-rocket/inc/Engine/Optimization/Minify/CSS/Minify.php on line 15