(() => {
// examples/vanillats/js/build/src/ts/core/html.js
var HtmlUtils = class {
/**
* Parses an HTML string into a Document object.
* @param html - The HTML string to parse.
* @returns A Document object or null if parsing fails.
*/
static getDocument(html) {
html = html.replace(HtmlUtils.styleAttributeRegex, "");
try {
return new DOMParser().parseFromString(html, "text/html");
} catch (ex) {
console.warn(ex);
}
}
/**
* Creates a Document object from HTML string with certain elements removed.
* @param html - The HTML string to parse.
* @returns A cleaned Document object.
*/
static getDocumentCleanText(html) {
let dom = this.getDocument(html);
if (dom === null) {
dom = new Document();
}
const textUnfriendly = dom.querySelectorAll("script, style, svg, noscript, iframe");
for (let i = textUnfriendly.length - 1; i >= 0; i--) {
textUnfriendly[i].parentElement.removeChild(textUnfriendly[i]);
}
return dom;
}
/**
* Creates an XPathResult iterator for text nodes in a cleaned HTML document.
* @param html - The HTML string to parse.
* @returns An XPathResult iterator for text nodes.
*/
static getDocumentCleanTextIterator(html) {
const dom = HtmlUtils.getDocumentCleanText(html);
const xpath = "//text() | //meta[@name='description']/@content | //@alt";
const texts = dom.evaluate(xpath, dom, null, XPathResult.ANY_TYPE, null);
return texts;
}
/**
* Creates an XPathResult iterator for text nodes within a specific element.
* @param dom - The Document object.
* @param element - The HTMLElement to search within.
* @returns An XPathResult iterator for text nodes.
*/
static getElementTextIterator(dom, element) {
const xpath = ".//text()";
const texts = dom.evaluate(xpath, element, null, XPathResult.ANY_TYPE, null);
return texts;
}
/**
* Extracts text content from a specific element.
* @param dom - The Document object.
* @param element - The HTMLElement to extract text from.
* @returns A string containing the element's text content.
*/
static getElementTextOnly(dom, element) {
const xpr = HtmlUtils.getElementTextIterator(dom, element);
const texts = [];
let node = xpr.iterateNext();
while (node) {
texts.push(node.nodeValue.trim());
node = xpr.iterateNext();
}
return texts.join(" ");
}
/**
* Checks if a string is a valid URL.
* @param str - The string to check.
* @returns True if the string is a valid URL, false otherwise.
*/
static isUrl(str) {
return URL.canParse(str);
}
/**
* Encodes HTML special characters in a string.
* @param str - The string to encode.
* @returns An HTML-encoded string.
*/
static htmlEncode(str) {
return new Option(str).innerHTML;
}
};
HtmlUtils.urlsRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_\:]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/g;
HtmlUtils.urlRegex = /^((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_\:]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)$/;
HtmlUtils.styleAttributeRegex = /style\s*=\s*("([^"]*)"|'([^']*)')/gi;
// examples/vanillats/js/build/src/ts/core/touch.js
var TouchProxy = class {
/**
* Creates a new TouchProxy instance and sets up event listeners.
*/
constructor() {
const content = document.body;
content.addEventListener("touchstart", (ev) => {
this.proxyToContainer(ev);
}, { passive: true });
content.addEventListener("touchend", (ev) => {
this.proxyToContainer(ev);
}, { passive: true });
content.addEventListener("touchmove", (ev) => {
this.proxyToContainer(ev);
}, { passive: true });
}
/**
* Proxies touch events to the container iframe.
* @param ev - The TouchEvent to be proxied.
*/
async proxyToContainer(ev) {
var _a;
let primeTouch;
let touches = (_a = ev.touches) !== null && _a !== void 0 ? _a : ev.changedTouches;
if (ev.touches.length === 1) {
primeTouch = ev.touches[0];
} else if (ev.changedTouches.length === 1) {
primeTouch = ev.changedTouches[0];
} else {
return;
}
const touchData = {
identifier: null,
target: null,
clientX: primeTouch.clientX,
clientY: primeTouch.clientY,
pageX: primeTouch.pageX,
pageY: primeTouch.pageY,
screenX: primeTouch.screenX,
screenY: primeTouch.screenY,
radiusX: primeTouch.radiusX,
radiusY: primeTouch.radiusY,
rotationAngle: primeTouch.rotationAngle,
force: primeTouch.force,
eventType: ev.type
};
const msg = {
target: "interrobot",
data: {
reportTouch: touchData
}
};
window.parent.postMessage(msg, "*");
}
async touchEnd(ev) {
}
async touchMove(ev) {
}
};
// examples/vanillats/js/build/src/ts/core/plugin.js
var DarkMode;
(function(DarkMode2) {
DarkMode2[DarkMode2["Light"] = 0] = "Light";
DarkMode2[DarkMode2["Dark"] = 1] = "Dark";
})(DarkMode || (DarkMode = {}));
var PluginConnection = class {
/**
* Creates a new PluginConnection instance.
* @param iframeSrc - The source URL of the iframe.
* @param hostOrigin - The origin of the host (optional).
*/
constructor(iframeSrc, hostOrigin) {
this.iframeSrc = iframeSrc;
if (hostOrigin) {
this.hostOrigin = hostOrigin;
} else {
this.hostOrigin = "";
}
const url = new URL(iframeSrc);
if (iframeSrc === "about:srcdoc") {
this.pluginOrigin = "about:srcdoc";
} else {
this.pluginOrigin = url.origin;
}
}
/**
* Gets the iframe source URL.
* @returns The iframe source URL.
*/
getIframeSrc() {
return this.iframeSrc;
}
/**
* Gets the host origin.
* @returns The host origin.
*/
getHostOrigin() {
return this.hostOrigin;
}
/**
* Gets the plugin origin.
* @returns The plugin origin.
*/
getPluginOrigin() {
return this.pluginOrigin;
}
/**
* Returns a string representation of the connection.
* @returns A string describing the host and plugin origins.
*/
toString() {
return `host = ${this.hostOrigin}; plugin = ${this.pluginOrigin}`;
}
};
var Plugin = class {
/**
* Initializes the plugin class.
* @param classtype - The class type to initialize.
* @returns An instance of the initialized class.
*/
static async initialize(classtype) {
const createAndConfigure = () => {
let instance = new classtype();
Plugin.postMeta(instance.constructor.meta);
window.addEventListener("load", () => Plugin.postContentHeight());
window.addEventListener("resize", () => Plugin.postContentHeight());
return instance;
};
if (document.readyState === "complete" || document.readyState === "interactive") {
return createAndConfigure();
} else {
return new Promise((resolve) => {
document.addEventListener("DOMContentLoaded", () => {
resolve(createAndConfigure());
});
});
}
}
/**
* Posts the current content height to the parent frame.
*/
static postContentHeight(constrainTo = null) {
const mainResults = document.querySelector(".main__results");
let currentScrollHeight = document.body.scrollHeight;
if (mainResults) {
currentScrollHeight = Number(mainResults.getBoundingClientRect().bottom);
}
if (currentScrollHeight !== Plugin.contentScrollHeight) {
const constrainedHeight = constrainTo && constrainTo >= 1 ? Math.min(constrainTo, currentScrollHeight) : currentScrollHeight;
const msg = {
target: "interrobot",
data: {
reportHeight: constrainedHeight
}
};
Plugin.routeMessage(msg);
}
}
/**
* Posts a request to open a resource link.
* @param resource - The resource identifier.
* @param openInBrowser - Whether to open the link in a browser.
*/
static postOpenResourceLink(resource, openInBrowser) {
const msg = {
target: "interrobot",
data: {
reportLink: {
openInBrowser,
resource
}
}
};
Plugin.routeMessage(msg);
}
/**
* Posts plugin metadata to the parent frame.
* @param meta - The metadata object to post.
*/
static postMeta(meta) {
const msg = {
target: "interrobot",
data: {
reportMeta: meta
}
};
Plugin.routeMessage(msg);
}
/**
* Sends an API request to the parent frame.
* @param apiMethod - The API method to call.
* @param apiKwargs - The arguments for the API call.
* @returns A promise that resolves with the API response.
*/
static async postApiRequest(apiMethod, apiKwargs) {
let result = null;
const getPromisedResult = async () => {
return new Promise((resolve) => {
const listener = async (ev) => {
var _a;
if (ev === void 0) {
return;
}
const evData = ev.data;
const evDataData = (_a = evData.data) !== null && _a !== void 0 ? _a : {};
if (evDataData && typeof evDataData === "object" && evDataData.hasOwnProperty("apiResponse")) {
const resultMethod = evDataData.apiResponse["__meta__"]["request"]["method"];
if (apiMethod === resultMethod) {
result = evData.data.apiResponse;
window.removeEventListener("message", listener);
resolve();
} else {
}
}
};
const msg = {
target: "interrobot",
data: {
apiRequest: {
method: apiMethod,
kwargs: apiKwargs
}
}
};
window.addEventListener("message", listener);
Plugin.routeMessage(msg);
});
};
await getPromisedResult();
return result;
}
/**
* Logs timing information to the console.
* @param msg - The message to log.
* @param millis - The time in milliseconds.
*/
static logTiming(msg, millis) {
const seconds = (millis / 1e3).toFixed(3);
console.log(`\u{1F916} [${seconds}s] ${msg}`);
}
/**
* Routes a message to the parent frame.
* @param msg - The message to route.
*/
static routeMessage(msg) {
let parentOrigin = "";
if (Plugin.connection) {
parentOrigin = Plugin.connection.getHostOrigin();
window.parent.postMessage(msg, parentOrigin);
} else {
window.parent.postMessage(msg);
}
}
/**
* Creates a new Plugin instance.
*/
constructor() {
this.projectId = -1;
this.mode = DarkMode.Light;
let paramProject;
let paramMode;
let paramOrigin;
if (this.parentIsOrigin()) {
const ifx = window.parent.document.getElementById("report");
paramProject = parseInt(ifx.dataset.project, 10);
paramMode = parseInt(ifx.dataset.mode, 10);
paramOrigin = ifx.dataset.origin;
} else {
const urlSearchParams = new URLSearchParams(window.location.search);
paramProject = parseInt(urlSearchParams.get("project"), 10);
paramMode = parseInt(urlSearchParams.get("mode"), 10);
paramOrigin = urlSearchParams.get("origin");
}
Plugin.connection = new PluginConnection(document.location.href, paramOrigin);
if (isNaN(paramProject)) {
const errorMessage = `missing project url argument`;
throw new Error(errorMessage);
}
this.data = null;
this.projectId = paramProject;
this.mode = isNaN(paramMode) || paramMode !== 1 ? DarkMode.Light : DarkMode.Dark;
Plugin.contentScrollHeight = 0;
const modeClass = DarkMode[this.mode].toLowerCase();
document.body.classList.remove("light", "dark");
document.body.classList.add(modeClass);
const tp = new TouchProxy();
}
/**
* Introduces a delay in the execution.
* @param ms - The number of milliseconds to delay.
* @returns A promise that resolves after the specified delay.
*/
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Gets the current mode.
* @returns The mode (DarkMode.Light, DarkMode.Dark).
*/
getMode() {
return this.mode;
}
/**
* Gets the current project ID.
* @returns The project ID.
*/
getProjectId() {
return this.projectId;
}
/**
* Gets the instance meta, the subclassed override data
* @returns the class meta.
*/
getInstanceMeta() {
return this.constructor["meta"];
}
/**
* Initializes the plugin data.
* @param defaultData - The default data for the plugin.
* @param autoform - An array of HTML elements for the autoform.
*/
async initData(defaultData, autoform) {
this.data = new PluginData(this.getProjectId(), this.getInstanceMeta(), defaultData, autoform);
await this.data.loadData();
}
/**
* Initializes and returns the plugin data.
* @param defaultData - The default data for the plugin.
* @param autoform - An array of HTML elements for the autoform.
* @returns A promise that resolves with the initialized PluginData.
*/
async initAndGetData(defaultData, autoform) {
await this.initData(defaultData, autoform);
return this.data;
}
/**
* Gets the current project.
* @returns A promise that resolves with the current Project.
*/
async getProject() {
if (this.project === void 0) {
const project = await Project.getApiProject(this.projectId);
if (project === null) {
const errorMessage = `project id=${this.projectId} not found`;
throw new Error(errorMessage);
}
this.project = project;
}
return this.project;
}
/**
* Renders HTML content in the document body.
* @param html - The HTML content to render.
*/
render(html) {
document.body.innerHTML = html;
}
/**
* Initializes the plugin index page.
*/
async index() {
const project = await Project.getApiProject(this.getProjectId());
const encodedTitle = HtmlUtils.htmlEncode(project.getDisplayTitle());
const encodedMetaTitle = HtmlUtils.htmlEncode(Plugin.meta["title"]);
this.render(`
${encodedMetaTitle}
${encodedTitle}
Welcome, from the index() of the Plugin base-class. This page exists as a placeholder,
but in your hands it could be so much more. The Example Report form below will count and
present page title terms used across the website, by count.
It's an example to help get you started.
If you have any questions, please reach out to the dev via the in-app contact form.
`);
const button = document.getElementsByTagName("button")[0];
button.addEventListener("click", async (ev) => {
await this.process();
});
}
/**
* Processes the plugin data.
*/
async process() {
const titleWords = /* @__PURE__ */ new Map();
let resultsMap;
const exampleResultHandler = async (result, titleWordsMap) => {
const terms = result.name.trim().split(/[\s\-—]+/g);
for (let term of terms) {
if (!titleWordsMap.has(term)) {
titleWordsMap.set(term, 1);
} else {
const currentCount = titleWordsMap.get(term);
titleWordsMap.set(term, currentCount + 1);
}
}
};
const projectId = this.getProjectId();
const freeQueryString = "headers: text/html";
const fields = "name";
const internalHtmlPagesQuery = new SearchQuery({
project: projectId,
query: freeQueryString,
fields,
type: SearchQueryType.Any,
includeExternal: false,
includeNoRobots: false
});
await Search.execute(internalHtmlPagesQuery, resultsMap, async (result) => {
await exampleResultHandler(result, titleWords);
}, true, false, "Processing\u2026");
await this.report(titleWords);
}
/**
* Generates and displays a report based on the processed data.
* @param titleWords - A map of title words and their counts.
*/
async report(titleWords) {
const titleWordsRemap = new Map([...titleWords.entries()].sort((a, b) => {
const aVal = a[1];
const bVal = b[1];
if (aVal === bVal) {
return a[0].toLowerCase().localeCompare(b[0].toLowerCase());
} else {
return bVal - aVal;
}
}));
const tableRows = [];
for (let term of titleWordsRemap.keys()) {
const count = titleWordsRemap.get(term);
const truncatedTerm = term.length > 24 ? term.substring(24) + "\u2026" : term;
tableRows.push(`
`;
}
/**
* Wraps form HTML in a standard container.
* @param formHtml - The HTML string of the form to be wrapped.
* @returns A string containing the wrapped form HTML.
*/
static standardForm(formHtml) {
return `
${formHtml}
`;
}
/**
* Generates a standard results container.
* @returns A string containing the HTML for the standard results container.
*/
static standardResults() {
return ``;
}
/**
* Generates HTML for a standard checkbox input.
* @param name - The name attribute for the checkbox.
* @param value - The value attribute for the checkbox.
* @param label - The label text for the checkbox.
* @param synopsis - Optional synopsis text for the checkbox.
* @returns A string containing the HTML for the checkbox.
*/
static standardCheckbox(name, value, label, synopsis) {
return `
`;
}
/**
* Generates HTML for a standard radio input.
* @param name - The name attribute for the radio button.
* @param value - The value attribute for the radio button.
* @param label - The label text for the radio button.
* @param synopsis - Optional synopsis text for the radio button.
* @returns A string containing the HTML for the radio button.
*/
static standardRadio(name, value, label, synopsis) {
return `
`;
}
/**
* Renders a cell with "same as last" functionality.
* @param cellValue - The value of the current cell.
* @param rowData - An object containing the data for the entire row.
* @param i - The index of the current row.
* @returns An object containing classes and content for the cell.
*/
static cellRendererSameAsLast(cellValue, rowData, i) {
var _a;
if (!("ID" in rowData)) {
throw "ID must be present to use cellHandlerSameAsLast";
}
const keys = Object.keys(rowData);
const values = Object.values(rowData);
const valueIndex = values.indexOf(cellValue);
const cellHeading = keys[valueIndex];
const currentId = rowData["ID"].toString();
const lastId = (_a = Templates.cellHandlerSameAsLastMemo[cellHeading]) !== null && _a !== void 0 ? _a : "";
const classes = [];
if (i > 0 && lastId === currentId) {
classes.push("sameaslast");
}
Templates.cellHandlerSameAsLastMemo[cellHeading] = currentId;
return { "classes": classes, "content": `${HtmlUtils.htmlEncode(cellValue)}` };
}
/**
* Renders a cell with a link and "same as last" functionality.
* @param cellValue - The value of the current cell (used as the link URL).
* @param rowData - An object containing the data for the entire row.
* @param i - The index of the current row.
* @returns An object containing classes and content for the cell.
*/
static cellRendererSameAsLastLink(cellValue, rowData, i) {
const result = Templates.cellRendererSameAsLast(cellValue, rowData, i);
result["content"] = `${HtmlUtils.htmlEncode(cellValue)}`;
return result;
}
/**
* Renders a cell with a linked ID.
* @param cellValue - The value of the current cell (used as the ID).
* @param rowData - An object containing the data for the entire row.
* @param i - The index of the current row.
* @returns An object containing classes and content for the cell.
*/
static cellRendererLinkedId(cellValue, rowData, i) {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
const origin = params.origin;
const projectId = params.project;
if (!origin || !projectId) {
throw "missing required url arguments";
}
const interrobotPageDetail = `${origin}/search/${projectId}/resource/${cellValue}/`;
const result = {
"classes": [],
"content": `${HtmlUtils.htmlEncode(cellValue)}`
};
return result;
}
/**
* Renders a cell with wrapped content.
* @param cellValue - The value of the current cell.
* @param rowData - An object containing the data for the entire row.
* @param i - The index of the current row.
* @returns An object containing classes and content for the cell.
*/
static cellRendererWrappedContent(cellValue, rowData, i) {
return {
"classes": ["wrap"],
"content": `${HtmlUtils.htmlEncode(cellValue)}`
};
}
};
Templates.cellHandlerSameAsLastMemo = {};
// examples/vanillats/js/build/examples/vanillats/ts/interrobot-plugin.js
var Core;
(function(Core2) {
Core2.Project = Project;
Core2.SearchQueryType = SearchQueryType;
Core2.SearchQuery = SearchQuery;
Core2.Search = Search;
Core2.SearchResult = SearchResult;
Core2.PluginData = PluginData;
Core2.HtmlUtils = HtmlUtils;
Core2.Plugin = Plugin;
})(Core || (Core = {}));
var Ui;
(function(Ui2) {
Ui2.HtmlProcessingWidget = HtmlProcessingWidget;
Ui2.HtmlResultsTable = HtmlResultsTable;
Ui2.Templates = Templates;
})(Ui || (Ui = {}));
window.InterroBot = {
Core,
Ui
};
})();