@agility/web-studio-sdk
Version:
Standard Development Kit used to enable Web Studio features in Agility CMS
133 lines (121 loc) • 4.02 kB
text/typescript
import { initComponents, initializePreview } from "./util"
import {
dispatchDecoratorMapUpdatedEvent,
dispatchNavigationEvent,
INavigationEventArgs,
} from "./util/frameEvents"
import { generateDecoratorMap } from "./util/generateDecoratorMap"
/**
* Agility Preview SDK
* This works by putting agility-data-* attributes on various elements in your markup.
*/
let isInitialized = false
const setIsInitialized = (state: boolean) => {
isInitialized = state
startObserver()
}
let isDocumentReady = false
const setDocumentReady = (state: boolean) => {
isDocumentReady = state
startObserver()
}
const onLocationChange = () => {
const agilityPageIDElem = document.querySelector("[data-agility-page]")
const agilityDynamicContentElem = document.querySelector(
"[data-agility-dynamic-content]"
)
//initialize pageID and contentID
let pageID: any = -1
let contentID: any = -1
//if agilityPageIDElem exists, get the pageID from its data attribute
if (agilityPageIDElem) {
pageID = parseInt(agilityPageIDElem.getAttribute("data-agility-page") || "")
}
//don't proceed if we don't have a valid pageID
if (isNaN(pageID) || pageID < 1) {
console.warn(
"%cWeb Studio SDK\n - no pageID found on the `data-agility-page` element. \nMake sure you have an element set up like this: data-agility-page='{{agilitypageid}}' .",
"font-weight: bold"
)
return
}
//if agilityDynamicContentElem exists, get the contentID from its data attribute
if (agilityDynamicContentElem) {
contentID = agilityDynamicContentElem.getAttribute(
"data-agility-dynamic-content"
)
}
//prepare the full URL without query parameters
let fullUrl = location.href
if (fullUrl.indexOf("?") > -1) {
fullUrl = fullUrl.substring(0, fullUrl.indexOf("?"))
}
//create the navigation event arguments
const args: INavigationEventArgs = {
url: fullUrl,
pageID,
contentID,
windowScrollableHeight: document.documentElement.scrollHeight,
windowHeight: window.innerHeight,
}
//dispatch the navigation event
dispatchNavigationEvent(args)
//initialize the components that may have reloaded
initComponents()
//generate the decorator map
const decoratorMap = generateDecoratorMap()
//send the decorator map to the parent
if (decoratorMap) {
dispatchDecoratorMapUpdatedEvent({ decoratorMap })
}
}
const startObserver = () => {
if (!isDocumentReady || !isInitialized) return
const observer = new MutationObserver((target, options) => {
if (isInitialized) {
//Debounce the location change; we don't want to send multiple navigation events;
setTimeout(() => {
onLocationChange()
}, 100)
}
})
// start observing the body
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: [
"data-agility-page",
"data-agility-dynamic-content",
"data-agility-guid",
"data-agility-field",
"data-agility-component",
"data-agility-html",
"data-agility-previewbar",
],
})
}
// Check immediately if the document is already in an interactive or complete state
// This ensures that if the script runs after the document is already loaded,
// we don't miss the opportunity to initialize the app.
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
setDocumentReady(true)
}
// Add a listener for changes in the document's ready state.
// This is necessary to handle cases where the document hasn't yet reached the
// interactive or complete state when the script is first executed.
document.onreadystatechange = () => {
// If the document's readyState becomes 'interactive' or 'complete',
// it means the DOM is ready, so we can safely initialize our observer.
if (
document.readyState === "interactive" ||
document.readyState === "complete"
) {
setDocumentReady(true)
}
}
// Needs to always happen
initializePreview({ setIsInitialized })