Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 128 additions & 7 deletions API/dotpipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -5356,12 +5356,38 @@ function escapeHtml(html) {
}

/**
*
* @param {JSON Object} value
* @param {string} tempTag
* @param {} root
* @param {*} id
* @returns HTML Object
* Render a JSON-defined UI fragment into a DOM container.
*
* Given a JSON "template" object, creates DOM nodes described by the object's keys
* and appends them into the provided container (tempTag). Supports nested objects,
* common presentation keys and helper directives used by the framework:
* - tagname / tagName: element tag to create (defaults to "div")
* - header: object rendered via modalaHead (also injects a CSP meta tag)
* - buttons: array of button definitions (text/value/attributes)
* - sources: semicolon-separated sources for cards/carousels (img/audio/video/html/php)
* - css / js: external asset URLs to inject as <link> / <script>
* - html / php: URL to fetch and insert as HTML (fetched asynchronously)
* - select / options: create <select> and populate <option> elements
* - br: numeric count of <br> elements to insert
* - style: CSS text for the element
* - textcontent / innerhtml / innertext: content setters (newlines converted to <br>)
* - boxes / id / index and arbitrary attributes are applied where appropriate
*
* This function mutates the DOM: it appends created elements to tempTag (or the
* element referenced by tempTag if an id string is passed), may append a CSP meta
* tag when header is present, and triggers domContentLoad() after insertion.
*
* @param {Object} value - JSON template describing the element and its children.
* Keys and supported directives are documented above.
* @param {HTMLElement|string} tempTag - Container element or the id of a container
* to append generated content into.
* @param {HTMLElement} [root] - Optional root element used for header processing;
* defaults to tempTag when omitted.
* @param {*} [id] - Optional identifier passed through to nested processing (unused
* by most templates).
* @returns {HTMLElement|undefined} The container element (tempTag as an HTMLElement)
* after appending the generated content, or
* undefined if required inputs are missing.
*/
function modala(value, tempTag, root, id) {
if (typeof (tempTag) == "string") {
Expand Down Expand Up @@ -5798,6 +5824,21 @@ function fileOrder(elem) {
}


/**
* Parse an HTML string and convert the first top-level element into a JSON-like tree.
*
* The returned object represents the element structure with these fields:
* - tagName: lower-cased tag name (e.g., "div")
* - attributes: an object mapping attribute names to values
* - children: an array of child nodes where element children are nested objects (same shape)
* and text nodes are represented as { type: 'text', content: '...' }.
*
* Only the first child of the parsed document body is converted (i.e., the top-level element
* produced by parsing `htmlString`). Empty text nodes are skipped.
*
* @param {string} htmlString - HTML markup to parse (must contain at least one top-level element).
* @returns {{tagName: string, attributes: Object<string,string>, children: Array<Object>|Array>} A JSON-like representation of the first top-level element.
*/
function htmlToJson(htmlString) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/html');
Expand Down Expand Up @@ -5833,9 +5874,31 @@ function htmlToJson(htmlString) {
}
// Helpers to track which elements have pipe listeners
const pipeListenersSet = new WeakSet();
/**
* Check whether an element is already registered in the internal set of pipe listeners.
* @param {Element} elem - DOM element to test.
* @returns {boolean} True if the element is present in the pipe listeners set.
*/
function hasPipeListener(elem) { return pipeListenersSet.has(elem); }
/**
* Mark a DOM element as having a pipe listener attached so it won't be re-registered.
* @param {Element} elem - The element to mark (typically a pipe-related element).
*/
function markPipeListener(elem) { pipeListenersSet.add(elem); }

/**
* Attaches global pipe listeners and processes CSV-for-each elements under a root node.
*
* Adds capturing listeners for 'click' and custom 'inline' events on the provided root element. When an event
* targets an element that is eligible (has the 'mouse' class or an id) and hasn't already been wired, the element
* is marked as having a pipe listener, passed through the `pipes` processor, and—if it has an `inline` attribute—
* registered in `dotPipe.matrix` and executed via `dotPipe.runInline`.
*
* Additionally, scans for `<csv-foreach>` elements within the root and invokes `processCsvForeach` for any
* unprocessed instances, marking them with the `processed` class to avoid duplicate work.
*
* @param {Document|Element} [rootElem=document] - Root node to attach listeners to and to scan for `<csv-foreach>` elements.
*/
function addPipe(rootElem = document) {
// Global listeners for clicks or custom 'inline' events
['click', 'inline'].forEach(eventType => {
Expand Down Expand Up @@ -5898,7 +5961,15 @@ function addPipe(rootElem = document) {
// }
// });

// }
/**
* Ensure an element has a click handler that invokes the pipe dispatcher and then triggers a DOM re-scan.
*
* Attaches a click listener to the provided element that calls pipes(elem). Attempts to remove a previous click listener first
* (note: removal relies on identical function reference). After wiring the listener, calls domContentLoad(true) to reprocess
* dynamic/custom tags within the document.
*
* @param {Element} elem - The DOM element to bind the click handler to; nothing is done if the element has no id.
*/

function flashClickListener(elem) {
if (elem.id) {
Expand Down Expand Up @@ -5952,6 +6023,22 @@ function sortNodesByName(selector) {
}


/**
* Execute a set of declarative UI actions described by an element's attributes and classes.
*
* Inspects the provided DOM element for dotPipe-specific attributes and class markers (e.g., `ajax`, `modal`, `inline`, `insert`, `query`, `headers`, `download`, `clear-node`, `turn`, `x-toggle`, `set`, `get`, `remove`, carousel controls, redirect, etc.) and performs the corresponding side-effectful operations:
* - Triggers registered callbacks and inline macros.
* - Performs navigation/AJAX loads via `navigate` for `ajax` attributes (supports multiple `file:target:limit` parts).
* - Opens links or modals, starts/stops carousels, rotates lists, toggles classes, sets/removes attributes, clears or removes nodes, and initiates downloads.
* - Parses `query` and `headers` attributes into request data used by AJAX navigation.
* - Honors `disabled`, `clear-node`, and `redirect` semantics and returns early when appropriate.
*
* Most actions mutate the DOM, initiate network requests, or trigger other global behaviors; this function does not return a value.
*
* @param {Element} elem - The element whose attributes/classes define the actions to perform.
* @param {boolean} [stop=false] - Reserved/unused in current implementation; callers may pass true to indicate higher-level flow control (no effect here).
* @returns {void}
*/
function pipes(elem, stop = false) {

var query = "";
Expand Down Expand Up @@ -6237,6 +6324,40 @@ function displayColoredJson(elementId, jsonObj) {
document.getElementById(elementId).innerHTML = `<pre>${prettyHtml}</pre>`;
}

/**
* Send an AJAX request using an element's `ajax` attribute and insert or render the response
* according to the element's CSS classes (JSON, HTML, plain text, tree view, modala, etc.).
*
* The function builds a query string by combining `query` with any form-encoded data returned
* by `formAJAX(elem, classname)` when `classname` matches elements on the page, encodes it,
* applies request options via `setAJAXOpts`, then performs an XMLHttpRequest to
* `elem.getAttribute("ajax") + "?" + encodedQuery`. Response handling branches by the
* element's classes:
* - `strict-json` — validates response is JSON and replaces document.body with it.
* - `json` — parses JSON, calls `displayColoredJson(insertId, parsed)`, and writes pretty JSON
* to the element referenced by `insert` (if present).
* - `text-html` — writes response as innerHTML into the element referenced by `insert`.
* - `plain-text` — writes response as textContent into the element referenced by `insert`.
* - `tree-view` — parses JSON and calls `renderTree(parsed, targetElement)`.
* - `modala` — parses JSON and renders via `modala()` into the `insert` target; supports
* multi-box behavior controlled by `boxes` and `modala-multi-first` / `modala-multi-last`.
* - default — writes response HTML into the element referenced by `insert` if present.
*
* After inserting or rendering content the function invokes `domContentLoad()` and
* `flashClickListener(elem)` to initialize processed content and UI behavior.
*
* Note: this function mutates the DOM and logs warnings when required `insert` targets are missing.
*
* @param {Element} elem - The source element; must have an `ajax` attribute with the request URL,
* and may include classes (e.g., `json`, `text-html`, `modala`) and an
* `insert` attribute naming the target element id for inserted content.
* @param {Map|Object|null} [opts=null] - Optional request option container; will be normalized via
* `setAJAXOpts`. (Internal option handling; callers rarely need to set this.)
* @param {string} [query=""] - Query string fragment to append to the request URL; additional form
* data may be appended from elements matching `classname`.
* @param {string} [classname=""] - If non-empty and matching elements exist, `formAJAX(elem, classname)`
* is used to collect and append form-encoded data to `query`.
*/
function navigate(elem, opts = null, query = "", classname = "") {
//formAJAX at the end of this line
// console.log(elem);
Expand Down
118 changes: 111 additions & 7 deletions activeMenu/dotpipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -5356,12 +5356,31 @@ function escapeHtml(html) {
}

/**
*
* @param {JSON Object} value
* @param {string} tempTag
* @param {} root
* @param {*} id
* @returns HTML Object
* Render a JSON-described UI node into a target DOM container.
*
* Processes a JSON "modal" descriptor (hierarchical object with keys like
* tagname, header, css, js, html, php, buttons, sources, select/options,
* textcontent/innerhtml/innertext, style, and arbitrary attributes), creates
* corresponding DOM nodes, appends them to the provided target, and triggers
* post-processing for any newly inserted dotPipe elements.
*
* This function has side effects: it appends created elements (and any fetched
* HTML/CSS/JS resources) into the DOM, may inject a CSP <meta> when a
* header object is present, performs fetches for external html/php sources,
* and calls domContentLoad() after insertion.
*
* @param {Object} value - JSON descriptor for the element to create. Expected
* properties include `tagname` (or `tagName`), `header`, `buttons`, `sources`,
* `css`, `js`, `html`, `php`, `select`/`options`, `textcontent`/`innerhtml`/`innertext`,
* `style`, and arbitrary attributes. Nested objects are rendered recursively.
* @param {string|HTMLElement} tempTag - Target container element or the id of
* the target element to which the rendered node will be appended.
* @param {HTMLElement} [root] - Optional root element used for header processing;
* if omitted it defaults to the resolved tempTag.
* @param {*} [id] - Optional identifier passed through to nested processing (not
* required for typical use).
* @returns {HTMLElement|undefined} The target container (tempTag) after appending
* the rendered content, or undefined if required inputs are missing or invalid.
*/
function modala(value, tempTag, root, id) {
if (typeof (tempTag) == "string") {
Expand Down Expand Up @@ -5798,6 +5817,19 @@ function fileOrder(elem) {
}


/**
* Parse an HTML string and return a JSON representation of its first top-level element.
*
* The returned object describes the element tree with these keys:
* - tagName: lowercase tag name
* - attributes: map of attribute names to values
* - children: array of child nodes; element children are similar objects, text nodes are
* represented as { type: 'text', content: '...' } (whitespace-only text nodes are omitted)
*
* @param {string} htmlString - HTML fragment or document to parse.
* @return {object|null} JSON representation of the first element found in the parsed HTML body,
* or null if no element is present.
*/
function htmlToJson(htmlString) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/html');
Expand Down Expand Up @@ -5833,9 +5865,34 @@ function htmlToJson(htmlString) {
}
// Helpers to track which elements have pipe listeners
const pipeListenersSet = new WeakSet();
/**
* Check whether a DOM element is registered as a pipe listener.
*
* @param {Element} elem - The DOM element to check.
* @return {boolean} True if the element is registered in the internal pipeListenersSet, otherwise false.
*/
function hasPipeListener(elem) { return pipeListenersSet.has(elem); }
/**
* Mark an element as processed for pipe listeners so it won't be registered again.
* @param {Element} elem - The DOM element to mark as having pipe listeners attached.
*/
function markPipeListener(elem) { pipeListenersSet.add(elem); }

/**
* Install event listeners and initialize per-root dynamic handlers for dotPipe elements.
*
* Attaches capture-phase listeners for "click" and custom "inline" events on the provided root (default: document).
* When an event originates from an unprocessed element that either has class "mouse" or an id, this will:
* - mark the element as having a pipe listener,
* - invoke pipes(target) to perform standard pipe processing,
* - if the element has an `inline` attribute, ensure a dotPipe.matrix entry for it and run dotPipe.runInline for that entry.
*
* Additionally scans the root for <csv-foreach> elements and runs processCsvForeach on any not already processed, marking them with the "processed" class.
*
* Note: This function mutates global state (adds listeners, updates dotPipe.matrix, and may fetch/insert content via pipes/runInline).
*
* @param {Element|Document} [rootElem=document] - Root element under which listeners are attached and child custom tags are initialized.
*/
function addPipe(rootElem = document) {
// Global listeners for clicks or custom 'inline' events
['click', 'inline'].forEach(eventType => {
Expand Down Expand Up @@ -5898,7 +5955,14 @@ function addPipe(rootElem = document) {
// }
// });

// }
/**
* Ensure an element runs the pipe processing on click and refreshes dotPipe processing.
*
* If the provided element has an `id`, a click listener is attached that calls `pipes(elem)` when clicked.
* After (re)binding, `domContentLoad(true)` is invoked to (re)process dotPipe-supported elements.
*
* @param {HTMLElement} elem - The target element; must be a DOM element (typically with an `id`) to receive the click binding.
*/

function flashClickListener(elem) {
if (elem.id) {
Expand Down Expand Up @@ -5952,6 +6016,24 @@ function sortNodesByName(selector) {
}


/**
* Process a custom dotPipe element and perform its configured actions (AJAX loads, navigation, modal rendering, DOM mutations, downloads, carousel controls, toggles, attribute ops, etc.).
*
* This inspects attributes and classes on the provided element and executes the corresponding behavior:
* - Triggers callback functions named by the `callback` attribute.
* - Performs navigation/AJAX loads when `ajax` is present (may call `navigate` or create cloned loaders).
* - Opens links for `<lnk>` elements, triggers downloads for elements with class `download`.
* - Handles modal lists via `modal` and carousel controls / stepping via carousel-related classes.
* - Applies simple DOM operations driven by attributes: `display`, `turn`, `x-toggle`, `set`, `get`, `delete`, `remove`, `headers`, `query`, `form-class`, `file-order`, and `clear-node`.
* - Invokes dotPipe inline macro registration/run when encountering inline content during `turn` processing.
* - Early-returns for disabled elements or after actions that replace responsibility (e.g., carousel start, download, redirect).
*
* Note: The function mutates the DOM, may call global helpers (dotPipe, navigate, modalList, shiftFilesLeft/Right, carousel, fileOrder, etc.), and may change window.location. It does not return a value.
*
* @param {HTMLElement} elem - The element to process; expected to be a custom dotPipe tag or decorated element.
* @param {boolean} [stop=false] - Optional flag (currently unused) reserved for future control of processing flow.
* @returns {void}
*/
function pipes(elem, stop = false) {

var query = "";
Expand Down Expand Up @@ -6237,6 +6319,28 @@ function displayColoredJson(elementId, jsonObj) {
document.getElementById(elementId).innerHTML = `<pre>${prettyHtml}</pre>`;
}

/**
* Send an AJAX request built from an element's `ajax` attribute and injects the response into the DOM.
*
* Builds a query string (optionally augmented by formAJAX when `classname` matches), encodes it,
* applies AJAX options via setAJAXOpts, issues an XMLHttpRequest to the URL from the element's
* `ajax` attribute, and handles the response according to classes on the element:
* - `strict-json`: replaces document.body with the raw JSON response if it's valid JSON.
* - `json`: pretty-prints JSON into the target `insert` element and calls domContentLoad.
* - `text-html`: inserts response as HTML into the `insert` element.
* - `plain-text`: inserts response as text into the `insert` element.
* - `tree-view`: parses JSON and renders it via renderTree into the element with elem.id.
* - `modala`: parses JSON and renders modala content into the element named by `insert`, supporting multi-box behavior.
* - default: injects raw response into the `insert` element when no special class or callback is present.
*
* Side effects: performs network request, mutates DOM (inserts/replaces content), calls domContentLoad()
* and flashClickListener(), and may log warnings for missing `insert` targets.
*
* @param {Element} elem - The source element containing attributes that control navigation (must have an `ajax` attribute; optional `insert`, and behavior-driving CSS classes).
* @param {Map|Object|null} [opts=null] - AJAX option container passed to setAJAXOpts (method, headers, mode, etc.). If null, defaults are applied.
* @param {string} [query=""] - Additional query string to append to the request URL (will be URI-encoded).
* @param {string} [classname=""] - If non-empty, formAJAX(elem, classname) will be called and its output appended to the query string.
*/
function navigate(elem, opts = null, query = "", classname = "") {
//formAJAX at the end of this line
// console.log(elem);
Expand Down
Loading