Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| {"version":3,"file":"Element.js","sources":["../../src/core/Element.js"],"sourcesContent":["import getAttributesDiff from '../utils/getAttributesDiff.js';\n\nconst SYNC_PROPS = ['value', 'checked', 'selected'];\n\n/**\n * Element reference for managing DOM element attributes.\n * @param {Object} options The options object.\n * @param {Function} options.getSelector Function that returns the CSS selector for the element.\n * @param {Function} options.getAttributes Function that returns the attributes object for the element.\n * @private\n */\nclass Element {\n constructor(options) {\n this.getSelector = options.getSelector;\n this.getAttributes = options.getAttributes;\n this.previousAttributes = {};\n }\n\n /**\n * Attach the element reference to a DOM element.\n * @param {Node} parent The parent node to search in.\n */\n hydrate(parent) {\n this.ref = parent.querySelector(this.getSelector());\n }\n\n /**\n * Update the element's attributes based on the difference with previous attributes.\n */\n update() {\n // Attributes diff.\n const attributes = this.getAttributes();\n const { remove, add } = getAttributesDiff(attributes, this.previousAttributes);\n // Store previous attributes.\n this.previousAttributes = attributes;\n // Remove attributes first so later `setAttribute` overrides if needed.\n remove.forEach(attr => {\n this.ref.removeAttribute(attr);\n if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) {\n // Reset property to default.\n this.ref[attr] = attr === 'value' ? '' : false;\n }\n });\n // Add / update attributes.\n Object.keys(add).forEach(attr => {\n const value = add[attr];\n this.ref.setAttribute(attr, value);\n if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) {\n this.ref[attr] = attr === 'value' ? value : value !== false && value !== 'false';\n }\n });\n }\n}\n\nexport default Element;\n"],"names":[],"mappings":";;AAEA,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC;;AAEnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,OAAO,CAAC;AACd,IAAI,WAAW,CAAC,OAAO,EAAE;AACzB,QAAQ,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW;AAC9C,QAAQ,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa;AAClD,QAAQ,IAAI,CAAC,kBAAkB,GAAG,EAAE;AACpC,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,OAAO,CAAC,MAAM,EAAE;AACpB,QAAQ,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;AAC3D,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,MAAM,GAAG;AACb;AACA,QAAQ,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE;AAC/C,QAAQ,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,iBAAiB,CAAC,UAAU,EAAE,IAAI,CAAC,kBAAkB,CAAC;AACtF;AACA,QAAQ,IAAI,CAAC,kBAAkB,GAAG,UAAU;AAC5C;AACA,QAAQ,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;AAC/B,YAAY,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;AAC1C,YAAY,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE;AACrE;AACA,gBAAgB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,OAAO,GAAG,EAAE,GAAG,KAAK;AAC9D,YAAY;AACZ,QAAQ,CAAC,CAAC;AACV;AACA,QAAQ,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI;AACzC,YAAY,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC;AACnC,YAAY,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;AAC9C,YAAY,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE;AACrE,gBAAgB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,OAAO,GAAG,KAAK,GAAG,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,OAAO;AAChG,YAAY;AACZ,QAAQ,CAAC,CAAC;AACV,IAAI;AACJ;;;;"} |
| {"version":3,"file":"EventsManager.js","sources":["../../src/core/EventsManager.js"],"sourcesContent":["/**\n * Manager for delegated events.\n * @private\n */\nclass EventsManager {\n constructor() {\n this.listeners = [];\n this.types = new Set();\n this.previousSize = 0;\n }\n\n /**\n * Add a listener to the events manager.\n * @param {Function} listener The listener to add.\n * @param {string} type The type of event.\n * @return {number} The index of the listener.\n */\n addListener(listener, type) {\n this.types.add(type);\n this.listeners.push(listener);\n return this.listeners.length - 1;\n }\n\n /**\n * Reset the events manager.\n */\n reset() {\n this.listeners = [];\n this.previousSize = this.types.size;\n }\n\n /**\n * Check if there are pending types.\n * @return {boolean} True if there are pending types, false otherwise.\n */\n hasPendingTypes() {\n return this.types.size > this.previousSize;\n }\n}\n\nexport default EventsManager;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA,MAAM,aAAa,CAAC;AACpB,IAAI,WAAW,GAAG;AAClB,QAAQ,IAAI,CAAC,SAAS,GAAG,EAAE;AAC3B,QAAQ,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE;AAC9B,QAAQ,IAAI,CAAC,YAAY,GAAG,CAAC;AAC7B,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE;AAChC,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;AACrC,QAAQ,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;AACxC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,SAAS,GAAG,EAAE;AAC3B,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI;AAC3C,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,eAAe,GAAG;AACtB,QAAQ,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,YAAY;AAClD,IAAI;AACJ;;;;"} |
| {"version":3,"file":"Interpolation.js","sources":["../../src/core/Interpolation.js"],"sourcesContent":["import PathManager from './PathManager.js';\nimport syncNode from '../utils/syncNode.js';\nimport findComment from '../utils/findComment.js';\n\n/**\n * Interpolation reference for managing dynamic content between comment markers.\n * Handles the lifecycle of content that can change between renders, including\n * component recycling and DOM synchronization.\n * @param {Object} options The options object.\n * @param {Function} options.getStart Function that returns the start comment marker text.\n * @param {Function} options.getEnd Function that returns the end comment marker text.\n * @param {any} options.expression The expression to be evaluated for the interpolation.\n * @param {Function} options.isComponent Function that checks if an element is a component root element.\n * @param {Function} options.containsComponent Function that checks if an element contains a component.\n * @private\n */\nclass Interpolation {\n constructor(options) {\n this.getStart = options.getStart;\n this.getEnd = options.getEnd;\n this.expression = options.expression;\n this.shouldSkipFind = options.shouldSkipFind;\n this.shouldSkipSync = options.shouldSkipSync;\n this.tracker = new PathManager();\n }\n\n /**\n * Attach the interpolation reference to comment markers in the DOM.\n * Searches for start and end comment markers, skipping component subtrees.\n * @param {Node} parent The parent node to search in.\n */\n hydrate(parent) {\n const startMark = findComment(parent, this.getStart(), this.shouldSkipFind);\n const endMark = findComment(parent, this.getEnd(), this.shouldSkipFind, startMark);\n\n this.ref = [\n startMark,\n endMark\n ];\n }\n\n /**\n * Update the interpolation content with a new fragment.\n * Optimizes updates by syncing single non-component elements or replacing content entirely.\n * @param {DocumentFragment} fragment The new content fragment to insert.\n * @param {Element} targetElement The target element to replace with the new fragment.\n */\n update(fragment, handleComponents) {\n let divider;\n // Reference to marker elements.\n const [startMark, endMark] = this.ref;\n\n const currentFirstElement = startMark.nextSibling;\n // Check if the interpolation is empty.\n const currentEmpty = currentFirstElement === endMark;\n // Check if the interpolation has a single child element.\n const currentSingleChildElement = !currentEmpty && currentFirstElement.nextSibling === endMark;\n\n if (currentEmpty) {\n // The interpolation is empty. Insert the fragment.\n endMark.parentNode.insertBefore(fragment, endMark);\n } else if (\n currentSingleChildElement &&\n fragment.children.length === 1 &&\n !this.shouldSkipSync(currentFirstElement) &&\n !this.shouldSkipSync(fragment.firstChild)\n ) {\n // The interpolation has a single child element and the new fragment has a single child element.\n // The elements doesn't contain a component. Sync node attributes and content.\n syncNode(currentFirstElement, fragment.firstChild);\n } else {\n // The interpolation has multiple child elements or the new fragment has multiple child elements.\n // Insert a divider comment before the end marker and insert the new fragment after the divider.\n divider = document.createComment('');\n endMark.parentNode.insertBefore(divider, endMark);\n endMark.parentNode.insertBefore(fragment, endMark);\n }\n // Handle the components in the fragment. Execute the handler function.\n handleComponents();\n\n if (divider) {\n // Remove old content and divider.\n const startMark = this.ref[0];\n if (startMark.nextSibling === divider) {\n divider.parentNode.removeChild(divider);\n } else {\n const range = document.createRange();\n range.setStartAfter(this.ref[0]);\n range.setEndAfter(divider);\n range.deleteContents();\n }\n }\n }\n\n /**\n * Update the interpolation content with a new fragment. This is used for container components.\n * @param {Element} element The element to replace with the new fragment.\n * @param {DocumentFragment} fragment The new content fragment to insert.\n * @param {Function} handleComponents Function to handle the components in the fragment before removing the old content.\n */\n updateElement(element, fragment, handleComponents) {\n const divider = document.createComment('');\n element.parentNode.insertBefore(divider, element.nextSibling);\n divider.parentNode.insertBefore(fragment.firstChild, divider.nextSibling);\n handleComponents();\n if (element.nextSibling === divider) element.parentNode.removeChild(element);\n divider.parentNode.removeChild(divider);\n }\n}\n\nexport default Interpolation;\n"],"names":[],"mappings":"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"} |
| {"version":3,"file":"Partial.js","sources":["../../src/core/Partial.js"],"sourcesContent":["/**\n * Wrapper class for partial templates that preserves structure.\n * @param {Array} items The items in the partial.\n * @private\n */\nclass Partial {\n constructor(items) {\n this.items = items;\n }\n}\n\nexport default Partial;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA,MAAM,OAAO,CAAC;AACd,IAAI,WAAW,CAAC,KAAK,EAAE;AACvB,QAAQ,IAAI,CAAC,KAAK,GAAG,KAAK;AAC1B,IAAI;AACJ;;;;"} |
| {"version":3,"file":"PathManager.js","sources":["../../src/core/PathManager.js"],"sourcesContent":["/**\n * Manager for component position tracking and recycling.\n * @private\n */\nclass PathManager {\n constructor() {}\n\n /**\n * Reset before render.\n */\n reset() {\n this.paused = 0;\n this.previous = this.tracked || new Map();\n this.tracked = new Map();\n this.positionStack = [0];\n }\n\n /**\n * Push position to stack.\n */\n push() {\n this.positionStack.push(0);\n }\n\n /**\n * Pop position from stack.\n */\n pop() {\n this.positionStack.pop();\n }\n\n /**\n * Increment position.\n */\n increment() {\n this.positionStack[this.positionStack.length - 1]++;\n }\n\n /**\n * Pause tracking.\n */\n pause() {\n this.paused++;\n }\n\n /**\n * Resume tracking.\n */\n resume() {\n this.paused--;\n }\n\n /**\n * Get current path as array.\n * @return {string} Current position path.\n */\n getPath() {\n return this.positionStack.join('-');\n }\n\n /**\n * Track component at current path.\n * @param {Component} component The component to track.\n * @return {Component} The component.\n */\n track(component) {\n if (this.paused === 0) {\n this.tracked.set(\n this.getPath(),\n component\n );\n }\n\n return component;\n }\n\n /**\n * Tell if there was only one component rendered in the previous and current tracked maps\n * and that component instance is the same (was recycled).\n * Used by Component to detect simple single component cases and skip DOM moves.\n * @return {boolean} Returns true when there is exactly one component at the first level\n * and it was successfully recycled.\n */\n hasSingleComponent() {\n if (this.tracked.size !== 1 || this.previous.size !== 1) return false;\n const [currentPath, currentComponent] = this.tracked.entries().next().value;\n const [previousPath, previousComponent] = this.previous.entries().next().value;\n // Ensure the component is at the root level (path '0') so it is not part of a deeper partial/array.\n if (currentPath !== '0' || previousPath !== '0') return false;\n return currentComponent === previousComponent;\n }\n\n /**\n * Find a recyclable component for the current path based on constructor (type)\n * when the component is un-keyed.\n * @param {Component} candidate The candidate component instance.\n * @return {Component|null} The recyclable component or null if none found.\n */\n findRecyclable(candidate) {\n const previous = this.previous.get(this.getPath());\n return previous && !previous.key && previous.constructor === candidate.constructor ? previous : null;\n }\n}\n\nexport default PathManager;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA,MAAM,WAAW,CAAC;AAClB,IAAI,WAAW,GAAG,CAAC;;AAEnB;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,MAAM,GAAG,CAAC;AACvB,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,GAAG,EAAE;AACjD,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE;AAChC,QAAQ,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;AAChC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,IAAI,GAAG;AACX,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AAClC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,GAAG,GAAG;AACV,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE;AAChC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE;AAC3D,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,MAAM,EAAE;AACrB,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,MAAM,GAAG;AACb,QAAQ,IAAI,CAAC,MAAM,EAAE;AACrB,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,OAAO,GAAG;AACd,QAAQ,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;AAC3C,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA,IAAI,KAAK,CAAC,SAAS,EAAE;AACrB,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;AAC/B,YAAY,IAAI,CAAC,OAAO,CAAC,GAAG;AAC5B,gBAAgB,IAAI,CAAC,OAAO,EAAE;AAC9B,gBAAgB;AAChB,aAAa;AACb,QAAQ;;AAER,QAAQ,OAAO,SAAS;AACxB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,kBAAkB,GAAG;AACzB,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,OAAO,KAAK;AAC7E,QAAQ,MAAM,CAAC,WAAW,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK;AACnF,QAAQ,MAAM,CAAC,YAAY,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK;AACtF;AACA,QAAQ,IAAI,WAAW,KAAK,GAAG,IAAI,YAAY,KAAK,GAAG,EAAE,OAAO,KAAK;AACrE,QAAQ,OAAO,gBAAgB,KAAK,iBAAiB;AACrD,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,cAAc,CAAC,SAAS,EAAE;AAC9B,QAAQ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AAC1D,QAAQ,OAAO,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,CAAC,WAAW,GAAG,QAAQ,GAAG,IAAI;AAC5G,IAAI;AACJ;;;;"} |
| {"version":3,"file":"SafeHTML.js","sources":["../../src/core/SafeHTML.js"],"sourcesContent":["/**\n * Wrapper class for HTML strings marked as safe.\n * @param {string} value The HTML string to be marked as safe.\n * @property {string} value The HTML string.\n * @private\n */\nclass SafeHTML {\n constructor(value) {\n this.value = value;\n }\n\n toString() {\n return this.value;\n }\n}\n\nexport default SafeHTML;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,QAAQ,CAAC;AACf,IAAI,WAAW,CAAC,KAAK,EAAE;AACvB,QAAQ,IAAI,CAAC,KAAK,GAAG,KAAK;AAC1B,IAAI;;AAEJ,IAAI,QAAQ,GAAG;AACf,QAAQ,OAAO,IAAI,CAAC,KAAK;AACzB,IAAI;AACJ;;;;"} |
| {"version":3,"file":"Emitter.js","sources":["../src/Emitter.js"],"sourcesContent":["import validateListener from './utils/validateListener.js';\n\n/**\n * `Emitter` is a class that provides an easy way to implement the observer pattern \n * in your applications. \n * It can be extended to create new classes that have the ability to emit and bind custom named events. \n * Emitter is used by `Model` and `View` classes, which inherit from it to implement \n * event-driven functionality.\n * \n * ## Inverse of Control Pattern\n * \n * The Emitter class includes \"inverse of control\" methods (`listenTo`, `listenToOnce`, `stopListening`) \n * that allow an object to manage its own listening relationships. Instead of:\n * \n * ```javascript\n * // Traditional approach - harder to clean up\n * otherObject.on('change', this.myHandler);\n * otherObject.on('destroy', this.cleanup);\n * // Later you need to remember to clean up each listener\n * otherObject.off('change', this.myHandler);\n * otherObject.off('destroy', this.cleanup);\n * ```\n * \n * You can use:\n * \n * ```javascript\n * // Inverse of control - easier cleanup\n * this.listenTo(otherObject, 'change', this.myHandler);\n * this.listenTo(otherObject, 'destroy', this.cleanup);\n * // Later, clean up ALL listeners at once\n * this.stopListening(); // Removes all listening relationships\n * ```\n * \n * This pattern is particularly useful for preventing memory leaks and simplifying cleanup\n * in component lifecycle management.\n *\n * @module\n * @example\n * import { Emitter } from 'rasti';\n * // Custom cart\n * class ShoppingCart extends Emitter {\n * constructor() {\n * super();\n * this.items = [];\n * }\n *\n * addItem(item) {\n * this.items.push(item);\n * // Emit a custom event called `itemAdded`.\n * // Pass the added item as an argument to the event listener.\n * this.emit('itemAdded', item);\n * }\n * }\n * // Create an instance of ShoppingCart and Logger\n * const cart = new ShoppingCart();\n * // Listen to the `itemAdded` event and log the added item using the logger.\n * cart.on('itemAdded', (item) => {\n * console.log(`Item added to cart: ${item.name} - Price: $${item.price}`);\n * });\n * // Simulate adding items to the cart\n * const item1 = { name : 'Smartphone', price : 1000 };\n * const item2 = { name : 'Headphones', price : 150 };\n *\n * cart.addItem(item1); // Output: \"Item added to cart: Smartphone - Price: $1000\"\n * cart.addItem(item2); // Output: \"Item added to cart: Headphones - Price: $150\"\n */\nexport default class Emitter {\n /**\n * Adds event listener.\n * @param {string} type Type of the event (e.g. `change`).\n * @param {Function} listener Callback function to be called when the event is emitted.\n * @return {Function} A function to remove the listener.\n * @example\n * // Re render when model changes.\n * this.model.on('change', this.render.bind(this));\n */\n on(type, listener) {\n // Validate listener.\n validateListener(listener);\n // Create listeners object if it doesn't exist.\n if (!this.listeners) this.listeners = {};\n // Every type must have an array of listeners.\n if (!this.listeners[type]) this.listeners[type] = [];\n // Add listener to the array of listeners.\n this.listeners[type].push(listener);\n // Return a function to remove the listener.\n return () => this.off(type, listener);\n }\n\n /**\n * Adds event listener that executes once.\n * @param {string} type Type of the event (e.g. `change`).\n * @param {Function} listener Callback function to be called when the event is emitted.\n * @return {Function} A function to remove the listener.\n * @example\n * // Log a message once when model changes.\n * this.model.once('change', () => console.log('This will happen once'));\n */\n once(type, listener) {\n // Validate listener.\n validateListener(listener);\n // Wrap listener to remove it after it is called.\n const wrapper = (...args) => {\n listener(...args);\n this.off(type, wrapper);\n };\n // Add listener.\n return this.on(type, wrapper);\n }\n\n /**\n * Removes event listeners with flexible parameter combinations.\n * @param {string} [type] Type of the event (e.g. `change`). If not provided, removes ALL listeners from this emitter.\n * @param {Function} [listener] Specific callback function to remove. If not provided, removes all listeners for the specified type.\n * \n * **Behavior based on parameters:**\n * - `off()` - Removes ALL listeners from this emitter\n * - `off(type)` - Removes all listeners for the specified event type\n * - `off(type, listener)` - Removes the specific listener for the specified event type\n * \n * @example\n * // Remove all listeners from this emitter\n * this.model.off();\n * \n * @example\n * // Remove all 'change' event listeners\n * this.model.off('change');\n * \n * @example\n * // Remove specific listener for 'change' events\n * const myListener = () => console.log('changed');\n * this.model.on('change', myListener);\n * this.model.off('change', myListener);\n */\n off(type, listener) {\n // No listeners.\n if (!this.listeners) return;\n // No type provided, remove all listeners.\n if (!type) {\n delete this.listeners;\n return;\n }\n // No listeners for specified type.\n if (!this.listeners[type]) return;\n // No listener provided, remove all listeners for specified type.\n if (!listener) {\n delete this.listeners[type];\n } else {\n // Remove specific listener.\n this.listeners[type] = this.listeners[type].filter(fn => fn !== listener);\n if (!this.listeners[type].length) delete this.listeners[type];\n }\n // Remove listeners object if it's empty.\n if (!Object.keys(this.listeners).length) delete this.listeners;\n }\n\n /**\n * Emits event of specified type. Listeners will receive specified arguments.\n * @param {string} type Type of the event (e.g. `change`).\n * @param {...any} [args] Optional arguments to be passed to listeners.\n * @example\n * // Emit validation error event with no arguments\n * this.emit('invalid');\n * \n * @example\n * // Emit change event with data\n * this.emit('change', { field : 'name', value : 'John' });\n */\n emit(type, ...args) {\n // No listeners.\n if (!this.listeners || !this.listeners[type]) return;\n // Call listeners. Use `slice` to make a copy and prevent errors when \n // removing listeners inside a listener.\n this.listeners[type].slice().forEach(fn => fn(...args));\n }\n\n /**\n * Listen to an event of another emitter (Inverse of Control pattern).\n * \n * This method allows this object to manage its own listening relationships,\n * making cleanup easier and preventing memory leaks. Instead of calling\n * `otherEmitter.on()`, you call `this.listenTo(otherEmitter, ...)` which\n * allows this object to track and clean up all its listeners at once.\n * \n * @param {Emitter} emitter The emitter to listen to.\n * @param {string} type The type of the event to listen to.\n * @param {Function} listener The listener to call when the event is emitted.\n * @return {Function} A function to stop listening to the event.\n * \n * @example\n * // Instead of: otherModel.on('change', this.render.bind(this));\n * // Use: this.listenTo(otherModel, 'change', this.render.bind(this));\n * // This way you can later call this.stopListening() to clean up all listeners\n */\n listenTo(emitter, type, listener) {\n // Add listener to the emitter.\n emitter.on(type, listener);\n // Create listeningTo array if it doesn't exist.\n if (!this.listeningTo) this.listeningTo = [];\n // Add listener to the array of listeners.\n this.listeningTo.push({ emitter, type, listener });\n // Return a function to stop listening to the event.\n return () => this.stopListening(emitter, type, listener);\n }\n\n /**\n * Listen to an event of another emitter and remove the listener after it is called (Inverse of Control pattern).\n * \n * Similar to `listenTo()` but automatically removes the listener after the first execution,\n * like `once()` but with the inverse of control benefits for cleanup management.\n * \n * @param {Emitter} emitter The emitter to listen to.\n * @param {string} type The type of the event to listen to.\n * @param {Function} listener The listener to call when the event is emitted.\n * @return {Function} A function to stop listening to the event.\n * \n * @example\n * // Listen once to another emitter's initialization event\n * this.listenToOnce(otherModel, 'initialized', () => {\n * console.log('Other model initialized');\n * });\n */\n listenToOnce(emitter, type, listener) {\n validateListener(listener);\n // Wrap listener to remove it after it is called.\n const wrapper = (...args) => {\n listener(...args);\n this.stopListening(emitter, type, wrapper);\n };\n // Add listener.\n return this.listenTo(emitter, type, wrapper);\n }\n\n /**\n * Stop listening to events from other emitters (Inverse of Control pattern).\n * \n * This method provides flexible cleanup of listening relationships established with `listenTo()`.\n * All parameters are optional, allowing different levels of cleanup granularity.\n * \n * @param {Emitter} [emitter] The emitter to stop listening to. If not provided, stops listening to ALL emitters.\n * @param {string} [type] The type of event to stop listening to. If not provided, stops listening to all event types from the specified emitter.\n * @param {Function} [listener] The specific listener to remove. If not provided, removes all listeners for the specified event type from the specified emitter.\n * \n * **Behavior based on parameters:**\n * - `stopListening()` - Stops listening to ALL events from ALL emitters\n * - `stopListening(emitter)` - Stops listening to all events from the specified emitter\n * - `stopListening(emitter, type)` - Stops listening to the specified event type from the specified emitter\n * - `stopListening(emitter, type, listener)` - Stops listening to the specific listener for the specific event from the specific emitter\n * \n * @example\n * // Stop listening to all events from all emitters (complete cleanup)\n * this.stopListening();\n * \n * @example\n * // Stop listening to all events from a specific emitter\n * this.stopListening(otherModel);\n * \n * @example\n * // Stop listening to 'change' events from a specific emitter\n * this.stopListening(otherModel, 'change');\n * \n * @example\n * // Stop listening to a specific listener\n * const myListener = () => console.log('changed');\n * this.listenTo(otherModel, 'change', myListener);\n * this.stopListening(otherModel, 'change', myListener);\n */\n stopListening(emitter, type, listener) {\n // No listeningTo object.\n if (!this.listeningTo) return;\n // Remove listener from the array of listeners.\n this.listeningTo = this.listeningTo.filter(item => {\n if (\n !emitter ||\n (emitter === item.emitter && !type) ||\n (emitter === item.emitter && type === item.type && !listener) ||\n (emitter === item.emitter && type === item.type && listener === item.listener)\n ) {\n item.emitter.off(item.type, item.listener);\n return false;\n }\n return true;\n });\n // Remove listeningTo object if it's empty.\n if (!this.listeningTo.length) delete this.listeningTo;\n }\n}\n"],"names":[],"mappings":"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"} |
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;"} |
| {"version":3,"file":"Model.js","sources":["../src/Model.js"],"sourcesContent":["import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\n\n/**\n * - Orchestrates data and business logic.\n * - Emits events when data changes.\n * \n * A `Model` manages an internal table of data attributes and triggers change events when any of its data is modified. \n * Models may handle syncing data with a persistence layer. To design your models, create atomic, reusable objects \n * that contain all the necessary functions for manipulating their specific data. \n * Models should be easily passed throughout your app and used anywhere the corresponding data is needed.\n * \n * ## Construction Flow\n * 1. `preinitialize()` is called with all constructor arguments\n * 2. `this.defaults` are resolved (if function, it's called and bound to the model)\n * 3. `parse()` is called with all constructor arguments to process the data\n * 4. `this.attributes` is built by merging defaults and parsed data\n * 5. Getters/setters are generated for each attribute to emit change events\n * \n * @module\n * @extends Emitter\n * @param {object} [attributes={}] Primary data object containing model attributes\n * @param {...*} [args] Additional arguments passed to `preinitialize` and `parse` methods\n * @property {object|Function} defaults Default attributes for the model. If a function, it's called bound to the model instance to get defaults.\n * @property {object} previous Object containing previous attributes when a change occurs.\n * @property {string} attributePrefix Static property that defines a prefix for generated getters/setters. Defaults to empty string.\n * @example\n * import { Model } from 'rasti';\n * \n * // User model\n * class User extends Model {\n * preinitialize() {\n * this.defaults = { name : '', email : '', role : 'user' };\n * }\n * }\n * // Order model with nested User and custom methods\n * class Order extends Model {\n * preinitialize(attributes, options = {}) {\n * this.defaults = {\n * id : null,\n * total : 0,\n * status : 'pending',\n * user : null\n * };\n * \n * this.apiUrl = options.apiUrl || '/api/orders';\n * }\n *\n * parse(data, options = {}) {\n * const parsed = { ...data };\n * \n * // Convert user object to User model instance\n * if (data.user && !(data.user instanceof User)) {\n * parsed.user = new User(data.user);\n * }\n * \n * return parsed;\n * }\n * \n * toJSON() {\n * const result = {};\n * for (const [key, value] of Object.entries(this.attributes)) {\n * if (value instanceof Model) {\n * result[key] = value.toJSON();\n * } else {\n * result[key] = value;\n * }\n * }\n * return result;\n * }\n * \n * async fetch() {\n * try {\n * const response = await fetch(`${this.apiUrl}/${this.id}`);\n * const data = await response.json();\n * \n * // Parse the fetched data and update model\n * const parsed = this.parse(data);\n * this.set(parsed, { source : 'fetch' });\n * \n * return this;\n * } catch (error) {\n * console.error('Failed to fetch order:', error);\n * throw error;\n * }\n * }\n * }\n * \n * // Create order with nested user data\n * const order = new Order({\n * id : 123,\n * total : 99.99,\n * user : { name : 'Alice', email : 'alice@example.com' }\n * });\n * \n * console.log(order.user instanceof User); // true\n * // Serialize with nested models\n * const json = order.toJSON();\n * console.log(json); // { id: 123, total: 99.99, status: 'pending', user: { name: 'Alice', email: 'alice@example.com', role: 'user' } }\n * \n * // Listen to fetch updates\n * order.on('change', (model, changed, options) => {\n * if (options?.source === 'fetch') {\n * console.log('Order updated from server:', changed);\n * }\n * });\n * \n * // Fetch latest data from server\n * await order.fetch();\n */\nexport default class Model extends Emitter {\n constructor() {\n super();\n // Call preinitialize.\n this.preinitialize.apply(this, arguments);\n // Set attributes object with defaults and passed attributes.\n this.attributes = Object.assign({}, getResult(this.defaults, this), this.parse.apply(this, arguments));\n // Object to store previous attributes when a change occurs.\n this.previous = {};\n // Generate getters/setters for every attribute.\n Object.keys(this.attributes).forEach(this.defineAttribute.bind(this));\n }\n\n /**\n * Called before any instantiation logic runs for the Model.\n * Receives all constructor arguments, allowing for flexible initialization patterns.\n * Use this to set up `defaults`, configure the model, or handle custom constructor arguments.\n * @param {object} [attributes={}] Primary data object containing model attributes\n * @param {...*} [args] Additional arguments passed from the constructor\n * @example\n * class User extends Model {\n * preinitialize(attributes, options = {}) {\n * this.defaults = { name : '', role : options.defaultRole || 'user' };\n * this.apiEndpoint = options.apiEndpoint || '/users';\n * }\n * }\n * const user = new User({ name : 'Alice' }, { defaultRole : 'admin', apiEndpoint : '/api/users' });\n */\n preinitialize() {}\n\n /**\n * Generate getter/setter for the given attribute key to emit `change` events.\n * The property name uses `attributePrefix` + key (e.g., with prefix 'attr_', key 'name' becomes 'attr_name').\n * Called internally by the constructor for each key in `this.attributes`.\n * Override with an empty method if you don't want automatic getters/setters.\n * \n * @param {string} key Attribute key from `this.attributes`\n * @example\n * // Custom prefix for all attributes\n * class PrefixedModel extends Model {\n * static attributePrefix = 'attr_';\n * }\n * const model = new PrefixedModel({ name: 'Alice' });\n * console.log(model.attr_name); // 'Alice'\n * \n * // Disable automatic getters/setters\n * class ManualModel extends Model {\n * defineAttribute() {\n * // Empty - no getters/setters generated\n * }\n * \n * getName() {\n * return this.get('name'); // Manual getter\n * }\n * }\n */\n defineAttribute(key) {\n Object.defineProperty(\n this,\n `${this.constructor.attributePrefix}${key}`,\n {\n get : () => this.get(key),\n set : (value) => { this.set(key, value); }\n }\n );\n }\n\n /**\n * Get an attribute from `this.attributes`.\n * This method is called internally by generated getters.\n * @param {string} key Attribute key.\n * @return {any} The attribute value.\n */\n get(key) {\n return this.attributes[key];\n }\n\n /**\n * Set one or more attributes into `this.attributes` and emit change events.\n * Supports two call signatures: `set(key, value, ...args)` or `set(object, ...args)`.\n * Additional arguments are passed to change event listeners, enabling custom behavior.\n * \n * @param {string|object} key Attribute key (string) or object containing key-value pairs\n * @param {*} [value] Attribute value (when key is string)\n * @param {...*} [args] Additional arguments passed to event listeners\n * @return {Model} This model instance for chaining\n * @emits change Emitted when any attribute changes. Listeners receive `(model, changedAttributes, ...args)`\n * @emits change:attribute Emitted for each changed attribute. Listeners receive `(model, newValue, ...args)`\n * @example\n * // Basic usage\n * model.set('name', 'Alice');\n * model.set({ name : 'Alice', age : 30 });\n * \n * // With options for listeners\n * model.set('name', 'Bob', { silent : false, validate : true });\n * model.on('change:name', (model, value, options) => {\n * if (options?.validate) {\n * // Custom validation logic\n * }\n * });\n */\n set(key, value, ...rest) {\n let attrs, args;\n // Handle both `\"key\", value` and `{key: value}` style arguments.\n if (typeof key === 'object') {\n attrs = key;\n args = [value, ...rest];\n } else {\n attrs = { [key] : value };\n args = rest;\n }\n // Are we in a nested `set` call?\n // Calling a `set` inside a `change:attribute` or `change` event listener\n const changing = this._changing;\n this._changing = true;\n // Store changed attributes.\n const changed = {};\n // Store previous attributes.\n if (!changing) {\n this.previous = Object.assign({}, this.attributes);\n }\n // Set attributes.\n Object.keys(attrs).forEach(key => {\n // Use equality to determine if value changed.\n if (attrs[key] !== this.attributes[key]) {\n changed[key] = attrs[key];\n this.attributes[key] = attrs[key];\n }\n });\n\n const changedKeys = Object.keys(changed);\n // Pending `change` event arguments.\n if (changedKeys.length) this._pending = ['change', this, changed, ...args];\n // Emit `change:attribute` events.\n changedKeys.forEach(key => {\n this.emit(`change:${key}`, this, attrs[key], ...args);\n });\n // Don't emit `change` event until the end of the nested \n // `set` calls inside `change:attribute` event listeners.\n if (changing) return this;\n // Emit `change` events, that might be nested.\n while (this._pending) {\n const pendingChange = this._pending;\n this._pending = null;\n this.emit.apply(this, pendingChange);\n }\n // Reset flags.\n this._pending = null;\n this._changing = false;\n\n return this;\n }\n\n /**\n * Transforms and validates data before it becomes model attributes.\n * Called during construction with all constructor arguments, allowing flexible data processing.\n * Override this method to transform incoming data, create nested models, or handle different data formats.\n * \n * @param {object} [data={}] Primary data object to be parsed into attributes\n * @param {...*} [args] Additional arguments from constructor, useful for parsing options\n * @return {object} Processed data that will become the model's attributes\n * @example\n * // Transform nested objects into models\n * class User extends Model {}\n * class Order extends Model {\n * parse(data, options = {}) {\n * // Skip parsing if requested\n * if (options.raw) return data;\n * // Transform user data into User model\n * const parsed = { ...data };\n * if (data.user && !(data.user instanceof User)) {\n * parsed.user = new User(data.user);\n * }\n * return parsed;\n * }\n * }\n * \n * // Usage with parsing options\n * const order1 = new Order({ id : 1, user : { name : 'Alice' } }); // user becomes User model\n * const order2 = new Order({ id : 2, user : { name : 'Bob' } }, { raw : true }); // user stays plain object\n */\n parse(data) {\n return data;\n }\n\n /**\n * Return object representation of the model to be used for JSON serialization.\n * By default returns a copy of `this.attributes`.\n * You can override this method to customize serialization behavior, such as calling `toJSON` recursively on nested Model instances.\n * @return {object} Object representation of the model to be used for JSON serialization.\n * @example\n * // Basic usage - returns a copy of model attributes:\n * const user = new Model({ name : 'Alice', age : 30 });\n * const json = user.toJSON();\n * console.log(json); // { name : 'Alice', age : 30 }\n * \n * // Override toJSON for recursive serialization of nested models:\n * class User extends Model {}\n * class Order extends Model {\n * parse(data) {\n * // Ensure user is always a User model\n * return { ...data, user : data.user instanceof User ? data.user : new User(data.user) };\n * }\n * \n * toJSON() {\n * const result = {};\n * for (const [key, value] of Object.entries(this.attributes)) {\n * if (value instanceof Model) {\n * result[key] = value.toJSON();\n * } else {\n * result[key] = value;\n * }\n * }\n * return result;\n * }\n * }\n * const order = new Order({ id : 1, user : { name : 'Alice' } });\n * const json = order.toJSON();\n * console.log(json); // { id : 1, user : { name : 'Alice' } }\n */\n toJSON() {\n return Object.assign({}, this.attributes);\n }\n}\n\n/**\n * Static property that defines a prefix for generated getters/setters.\n * When set, all attribute properties will be prefixed (e.g., 'attr_name' instead of 'name').\n * Useful for avoiding naming conflicts or creating a consistent property naming convention.\n * @type {string}\n * @default ''\n * @example\n * // Set prefix for all models of this class\n * class ApiModel extends Model {\n * static attributePrefix = 'attr_';\n * }\n * \n * const user = new ApiModel({ name : 'Alice', email : 'alice@example.com' });\n * console.log(user.attr_name); // 'Alice'\n * console.log(user.attr_email); // 'alice@example.com'\n * \n * // Still access via get/set methods without prefix\n * console.log(user.get('name')); // 'Alice'\n * user.set('name', 'Bob');\n * console.log(user.attr_name); // 'Bob'\n */\nModel.attributePrefix = '';\n"],"names":[],"mappings":";;;;;;;;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,MAAM,KAAK,SAAS,OAAO,CAAC;AAC3C,IAAI,WAAW,GAAG;AAClB,QAAQ,KAAK,EAAE;AACf;AACA,QAAQ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;AACjD;AACA,QAAQ,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC9G;AACA,QAAQ,IAAI,CAAC,QAAQ,GAAG,EAAE;AAC1B;AACA,QAAQ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7E,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,aAAa,GAAG,CAAC;;AAErB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AA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"} |
| import repeat from './repeat.js'; | ||
| import padEnd from './padEnd.js'; | ||
| /** | ||
| * The ASCII art cat. | ||
| * @type {string[]} | ||
| * @private | ||
| */ | ||
| const cat = [ | ||
| ' \\| ', | ||
| ' \\ ', | ||
| ' /\\___/\\ ', | ||
| ' ( o . o ) ', | ||
| ' > ︵ < ', | ||
| ' /| |\\ ', | ||
| ' (_| |_) ' | ||
| ]; | ||
| /** | ||
| * Creates a formatted error message with ASCII art (development version). | ||
| * @param {string} message The error message. Supports \n for multiple lines. | ||
| * @return {string} Formatted error message with ASCII art. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function createDevelopmentErrorMessage(message) { | ||
| const title = 'Something went wrong!'; | ||
| const minWidth = 40; | ||
| const lines = message.split('\n'); | ||
| const contentWidth = Math.max(minWidth, title.length, ...lines.map(line => line.length)); | ||
| const catPadding = Math.floor((contentWidth - cat[0].length) / 2); | ||
| const border = '+' + padEnd('-', contentWidth + 2, '-') + '+'; | ||
| return [ | ||
| '', | ||
| ` ${border}`, | ||
| ` | ${padEnd(title, contentWidth)} |`, | ||
| ` | ${padEnd('', contentWidth)} |`, | ||
| ].concat(lines.map(line => ` | ${padEnd(line, contentWidth)} |`)) | ||
| .concat([` ${border}`]) | ||
| .concat(cat.map(line => ` ${repeat(' ', catPadding)} ${line}`)) | ||
| .concat(['']) | ||
| .join('\n'); | ||
| } | ||
| export { createDevelopmentErrorMessage as default }; | ||
| //# sourceMappingURL=createDevelopmentErrorMessage.js.map |
| {"version":3,"file":"createDevelopmentErrorMessage.js","sources":["../../src/utils/createDevelopmentErrorMessage.js"],"sourcesContent":["import repeat from './repeat.js';\nimport padEnd from './padEnd.js';\n\n/**\n * The ASCII art cat.\n * @type {string[]}\n * @private\n */\nconst cat = [\n ' \\\\| ',\n ' \\\\ ',\n ' /\\\\___/\\\\ ',\n ' ( o . o ) ',\n ' > ︵ < ',\n ' /| |\\\\ ',\n ' (_| |_) '\n];\n\n/**\n * Creates a formatted error message with ASCII art (development version).\n * @param {string} message The error message. Supports \\n for multiple lines.\n * @return {string} Formatted error message with ASCII art.\n * @module\n * @private\n */\nexport default function createDevelopmentErrorMessage(message) {\n const title = 'Something went wrong!';\n const minWidth = 40;\n const lines = message.split('\\n');\n const contentWidth = Math.max(minWidth, title.length, ...lines.map(line => line.length));\n const catPadding = Math.floor((contentWidth - cat[0].length) / 2);\n const border = '+' + padEnd('-', contentWidth + 2, '-') + '+';\n return [\n '',\n ` ${border}`,\n ` | ${padEnd(title, contentWidth)} |`,\n ` | ${padEnd('', contentWidth)} |`,\n ].concat(lines.map(line => ` | ${padEnd(line, contentWidth)} |`))\n .concat([` ${border}`])\n .concat(cat.map(line => ` ${repeat(' ', catPadding)} ${line}`))\n .concat([''])\n .join('\\n');\n}\n"],"names":[],"mappings":";;;AAGA;AACA;AACA;AACA;AACA;AACA,MAAM,GAAG,GAAG;AACZ,IAAI,sBAAsB;AAC1B,IAAI,sBAAsB;AAC1B,IAAI,uBAAuB;AAC3B,IAAI,qBAAqB;AACzB,IAAI,qBAAqB;AACzB,IAAI,sBAAsB;AAC1B,IAAI;AACJ,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,6BAA6B,CAAC,OAAO,EAAE;AAC/D,IAAI,MAAM,KAAK,GAAG,uBAAuB;AACzC,IAAI,MAAM,QAAQ,GAAG,EAAE;AACvB,IAAI,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;AACrC,IAAI,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5F,IAAI,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;AACrE,IAAI,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG;AACjE,IAAI,OAAO;AACX,QAAQ,EAAE;AACV,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;AACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;AAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;AAC3C,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;AACrE,SAAS,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;AAC/B,SAAS,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACvE,SAAS,MAAM,CAAC,CAAC,EAAE,CAAC;AACpB,SAAS,IAAI,CAAC,IAAI,CAAC;AACnB;;;;"} |
| /** | ||
| * Production version: returns the message as-is without formatting. | ||
| * @param {string} message The error message. | ||
| * @return {string} Plain error message. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function createProductionErrorMessage(message) { | ||
| return message; | ||
| } | ||
| export { createProductionErrorMessage as default }; | ||
| //# sourceMappingURL=createProductionErrorMessage.js.map |
| {"version":3,"file":"createProductionErrorMessage.js","sources":["../../src/utils/createProductionErrorMessage.js"],"sourcesContent":["/**\n * Production version: returns the message as-is without formatting.\n * @param {string} message The error message.\n * @return {string} Plain error message.\n * @module\n * @private\n */\nexport default function createProductionErrorMessage(message) {\n return message;\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,4BAA4B,CAAC,OAAO,EAAE;AAC9D,IAAI,OAAO,OAAO;AAClB;;;;"} |
| {"version":3,"file":"deepFlat.js","sources":["../../src/utils/deepFlat.js"],"sourcesContent":["/**\n * Flatten an array recursively.\n * @param {Array} arr Array to flat recursively\n * @return {Array} Flat array\n * @module\n * @private\n */\nconst deepFlat = (arr) => arr.reduce((acc, val) => {\n if (Array.isArray(val)) acc.push(...deepFlat(val));\n else acc.push(val);\n return acc;\n}, []);\n\nexport default deepFlat;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACK,MAAC,QAAQ,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK;AACnD,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;AACtD,SAAS,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AACtB,IAAI,OAAO,GAAG;AACd,CAAC,EAAE,EAAE;;;;"} |
| /** | ||
| * Development mode flag. | ||
| * This will be replaced during build: | ||
| * - ESM/CJS: replaced with process.env.NODE_ENV !== 'production' | ||
| * - UMD dev: replaced with true | ||
| * - UMD prod: replaced with false | ||
| * @type {boolean} | ||
| * @private | ||
| */ | ||
| const __DEV__ = process.env.NODE_ENV !== 'production'; | ||
| export { __DEV__ as default }; | ||
| //# sourceMappingURL=dev.js.map |
| {"version":3,"file":"dev.js","sources":["../../src/utils/dev.js"],"sourcesContent":["/**\n * Development mode flag.\n * This will be replaced during build:\n * - ESM/CJS: replaced with process.env.NODE_ENV !== 'production'\n * - UMD dev: replaced with true\n * - UMD prod: replaced with false\n * @type {boolean}\n * @private\n */\nconst __DEV__ = true;\n\nexport default __DEV__;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAA,OAAA,GAAA,OAAA,CAAA,GAAA,CAAA,QAAA,KAAA;;;;"} |
| {"version":3,"file":"findComment.js","sources":["../../src/utils/findComment.js"],"sourcesContent":["/**\n * Finds the first comment node whose text matches exactly the given `text`.\n * Uses manual DOM traversal. Skips entire subtrees if `shouldSkip(element)` returns true.\n * Starts searching from `startNode` if provided, otherwise from `root.firstChild`.\n * @param {Node} root - Root node or fragment that limits the search scope.\n * @param {string} text - Exact comment text to match.\n * @param {Function} [shouldSkip] - Function that receives an element and returns true if its subtree should be skipped.\n * @param {Node} [startNode] - Node to start searching from (defaults to root.firstChild).\n * @return {Comment|null} The first matching comment node, or null if not found.\n * @module\n * @private\n */\nexport default function findComment(\n root,\n text,\n shouldSkip = () => false,\n startNode\n) {\n let node = startNode || root.firstChild;\n\n while (node) {\n // Check if current node is a comment with matching text.\n if (node.nodeType === Node.COMMENT_NODE && node.data.trim() === text) {\n return node;\n }\n // Descend into children if allowed and present.\n if (node.nodeType === Node.ELEMENT_NODE && !shouldSkip(node) && node.firstChild) {\n node = node.firstChild;\n continue;\n }\n // Move to next sibling, or climb up until a sibling is found.\n while (node && !node.nextSibling) {\n node = node.parentNode;\n if (!node || node === root) return null;\n }\n if (node) node = node.nextSibling;\n }\n\n return null;\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,WAAW;AACnC,IAAI,IAAI;AACR,IAAI,IAAI;AACR,IAAI,UAAU,GAAG,MAAM,KAAK;AAC5B,IAAI;AACJ,EAAE;AACF,IAAI,IAAI,IAAI,GAAG,SAAS,IAAI,IAAI,CAAC,UAAU;;AAE3C,IAAI,OAAO,IAAI,EAAE;AACjB;AACA,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;AAC9E,YAAY,OAAO,IAAI;AACvB,QAAQ;AACR;AACA,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE;AACzF,YAAY,IAAI,GAAG,IAAI,CAAC,UAAU;AAClC,YAAY;AACZ,QAAQ;AACR;AACA,QAAQ,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;AAC1C,YAAY,IAAI,GAAG,IAAI,CAAC,UAAU;AAClC,YAAY,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,OAAO,IAAI;AACnD,QAAQ;AACR,QAAQ,IAAI,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,WAAW;AACzC,IAAI;;AAEJ,IAAI,OAAO,IAAI;AACf;;;;"} |
| import repeat from './repeat.js'; | ||
| import padStart from './padStart.js'; | ||
| /** | ||
| * Converts an expression to its string representation for display. | ||
| * @param {any} expr The expression to convert. | ||
| * @return {string} String representation of the expression. | ||
| * @private | ||
| */ | ||
| function expressionToString(expr) { | ||
| if (typeof expr.toString === 'function') { | ||
| const source = expr.toString(); | ||
| // Keep single line or show first line for multi-line. | ||
| return '${' + (source.includes('\n') ? source.split('\n')[0] + '...' : source) + '}'; | ||
| } | ||
| return '${' + String(expr) + '}'; | ||
| } | ||
| /** | ||
| * Formats the template source with line numbers and highlights the specific error expression. | ||
| * @param {Object} source The original template source object with strings and expressions. | ||
| * @param {any} errorExpression The expression that caused the error. | ||
| * @return {string} Formatted template source. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function formatTemplateSource(source, errorExpression) { | ||
| if (!source || !source.strings || !source.expressions || !errorExpression) return ''; | ||
| const { strings, expressions } = source; | ||
| // Find the index of the error expression in the expressions array. | ||
| const expressionIndex = expressions.indexOf(errorExpression); | ||
| if (expressionIndex === -1) return ''; | ||
| // Build the template source and calculate the error position directly. | ||
| let templateSource = ''; | ||
| let errorPosition = -1; | ||
| for (let i = 0; i < strings.length; i++) { | ||
| templateSource += strings[i]; | ||
| if (i < expressions.length) { | ||
| // Mark the position before adding the error expression. | ||
| if (i === expressionIndex) { | ||
| errorPosition = templateSource.length; | ||
| } | ||
| templateSource += expressionToString(expressions[i]); | ||
| } | ||
| } | ||
| if (errorPosition === -1) return ''; | ||
| // Find the line and column of the error position. | ||
| const lines = templateSource.split('\n'); | ||
| const formattedLines = []; | ||
| const maxLineNumWidth = String(lines.length).length; | ||
| let errorLineNum = -1; | ||
| let errorStartCol = -1; | ||
| let charCount = 0; | ||
| for (let i = 0; i < lines.length; i++) { | ||
| const lineLength = lines[i].length + 1; // +1 for newline character. | ||
| if (charCount + lineLength > errorPosition) { | ||
| errorLineNum = i; | ||
| errorStartCol = errorPosition - charCount; | ||
| break; | ||
| } | ||
| charCount += lineLength; | ||
| } | ||
| if (errorLineNum === -1) return ''; | ||
| // Get the expression string to determine marker length. | ||
| const expressionStr = expressionToString(errorExpression); | ||
| // Format output with context. | ||
| const contextStart = Math.max(0, errorLineNum - 2); | ||
| const contextEnd = Math.min(lines.length - 1, errorLineNum + 2); | ||
| for (let i = contextStart; i <= contextEnd; i++) { | ||
| const lineNum = i + 1; | ||
| const lineNumStr = padStart(String(lineNum), maxLineNumWidth, ' '); | ||
| const isErrorLine = i === errorLineNum; | ||
| const linePrefix = isErrorLine ? ` ${lineNumStr} > ` : ` ${lineNumStr} | `; | ||
| formattedLines.push(linePrefix + lines[i]); | ||
| // Add pointer on error line. | ||
| if (isErrorLine) { | ||
| const markerPos = linePrefix.length + errorStartCol; | ||
| const markerLen = expressionStr.length; | ||
| const pointerLine = repeat(' ', markerPos) + repeat('^', markerLen) + ' <-- Error here!'; | ||
| formattedLines.push(pointerLine); | ||
| } | ||
| } | ||
| // If error expression is multi-line, show full details. | ||
| if (typeof errorExpression === 'function') { | ||
| const fullSource = errorExpression.toString(); | ||
| if (fullSource.includes('\n')) { | ||
| formattedLines.push(''); | ||
| formattedLines.push(' | Expression details:'); | ||
| fullSource.split('\n').forEach(line => { | ||
| formattedLines.push(' | ' + line); | ||
| }); | ||
| } | ||
| } | ||
| return formattedLines.join('\n'); | ||
| } | ||
| export { formatTemplateSource as default }; | ||
| //# sourceMappingURL=formatTemplateSource.js.map |
| {"version":3,"file":"formatTemplateSource.js","sources":["../../src/utils/formatTemplateSource.js"],"sourcesContent":["import repeat from './repeat.js';\nimport padStart from './padStart.js';\n\n/**\n * Converts an expression to its string representation for display.\n * @param {any} expr The expression to convert.\n * @return {string} String representation of the expression.\n * @private\n */\nfunction expressionToString(expr) {\n if (typeof expr.toString === 'function') {\n const source = expr.toString();\n // Keep single line or show first line for multi-line.\n return '${' + (source.includes('\\n') ? source.split('\\n')[0] + '...' : source) + '}';\n }\n return '${' + String(expr) + '}';\n}\n\n/**\n * Formats the template source with line numbers and highlights the specific error expression.\n * @param {Object} source The original template source object with strings and expressions.\n * @param {any} errorExpression The expression that caused the error.\n * @return {string} Formatted template source.\n * @module\n * @private\n */\nexport default function formatTemplateSource(source, errorExpression) {\n if (!source || !source.strings || !source.expressions || !errorExpression) return '';\n\n const { strings, expressions } = source;\n\n // Find the index of the error expression in the expressions array.\n const expressionIndex = expressions.indexOf(errorExpression);\n if (expressionIndex === -1) return '';\n\n // Build the template source and calculate the error position directly.\n let templateSource = '';\n let errorPosition = -1;\n\n for (let i = 0; i < strings.length; i++) {\n templateSource += strings[i];\n\n if (i < expressions.length) {\n // Mark the position before adding the error expression.\n if (i === expressionIndex) {\n errorPosition = templateSource.length;\n }\n templateSource += expressionToString(expressions[i]);\n }\n }\n\n if (errorPosition === -1) return '';\n\n // Find the line and column of the error position.\n const lines = templateSource.split('\\n');\n const formattedLines = [];\n const maxLineNumWidth = String(lines.length).length;\n\n let errorLineNum = -1;\n let errorStartCol = -1;\n let charCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const lineLength = lines[i].length + 1; // +1 for newline character.\n if (charCount + lineLength > errorPosition) {\n errorLineNum = i;\n errorStartCol = errorPosition - charCount;\n break;\n }\n charCount += lineLength;\n }\n\n if (errorLineNum === -1) return '';\n\n // Get the expression string to determine marker length.\n const expressionStr = expressionToString(errorExpression);\n\n // Format output with context.\n const contextStart = Math.max(0, errorLineNum - 2);\n const contextEnd = Math.min(lines.length - 1, errorLineNum + 2);\n\n for (let i = contextStart; i <= contextEnd; i++) {\n const lineNum = i + 1;\n const lineNumStr = padStart(String(lineNum), maxLineNumWidth, ' ');\n const isErrorLine = i === errorLineNum;\n const linePrefix = isErrorLine ? ` ${lineNumStr} > ` : ` ${lineNumStr} | `;\n\n formattedLines.push(linePrefix + lines[i]);\n\n // Add pointer on error line.\n if (isErrorLine) {\n const markerPos = linePrefix.length + errorStartCol;\n const markerLen = expressionStr.length;\n const pointerLine = repeat(' ', markerPos) + repeat('^', markerLen) + ' <-- Error here!';\n formattedLines.push(pointerLine);\n }\n }\n\n // If error expression is multi-line, show full details.\n if (typeof errorExpression === 'function') {\n const fullSource = errorExpression.toString();\n if (fullSource.includes('\\n')) {\n formattedLines.push('');\n formattedLines.push(' | Expression details:');\n fullSource.split('\\n').forEach(line => {\n formattedLines.push(' | ' + line);\n });\n }\n }\n\n return formattedLines.join('\\n');\n}\n"],"names":[],"mappings":"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"} |
| {"version":3,"file":"getAttributesDiff.js","sources":["../../src/utils/getAttributesDiff.js"],"sourcesContent":["/**\n * Get difference between current and previous attributes.\n * @param {Object} attributes Current attributes object.\n * @param {Object} previous Previous attributes object.\n * @return {Object} Object with add and remove properties.\n * @module\n * @private\n */\nexport default function getAttributesDiff(attributes, previous = {}) {\n const add = {};\n const remove = [];\n // Find attributes to add/update.\n Object.keys(attributes).forEach(key => {\n let value = attributes[key];\n // Only add if the value is different from previous.\n if (value !== previous[key]) {\n if (value === true) {\n add[key] = '';\n } else if (value !== false) {\n if (value === null || typeof value === 'undefined') value = '';\n add[key] = value;\n }\n }\n });\n // Find attributes to remove.\n Object.keys(previous).forEach(key => {\n if (!(key in attributes) || ((previous[key] !== attributes[key]) && attributes[key] === false)) {\n remove.push(key);\n }\n });\n\n return { add, remove };\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,EAAE,EAAE;AACrE,IAAI,MAAM,GAAG,GAAG,EAAE;AAClB,IAAI,MAAM,MAAM,GAAG,EAAE;AACrB;AACA,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AAC3C,QAAQ,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC;AACnC;AACA,QAAQ,IAAI,KAAK,KAAK,QAAQ,CAAC,GAAG,CAAC,EAAE;AACrC,YAAY,IAAI,KAAK,KAAK,IAAI,EAAE;AAChC,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE;AAC7B,YAAY,CAAC,MAAM,IAAI,KAAK,KAAK,KAAK,EAAE;AACxC,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE;AAC9E,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK;AAChC,YAAY;AACZ,QAAQ;AACR,IAAI,CAAC,CAAC;AACN;AACA,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AACzC,QAAQ,IAAI,EAAE,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,EAAE;AACxG,YAAY,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;AAC5B,QAAQ;AACR,IAAI,CAAC,CAAC;;AAEN,IAAI,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE;AAC1B;;;;"} |
| {"version":3,"file":"getAttributesHTML.js","sources":["../../src/utils/getAttributesHTML.js"],"sourcesContent":["/**\n * Generate HTML string from attributes object.\n * @param {Object} attributes Object containing attribute names and values.\n * @return {string} HTML string of attributes.\n * @module\n * @private\n */\nexport default function getAttributesHTML(attributes) {\n const html = [];\n\n Object.keys(attributes).forEach(key => {\n let value = attributes[key];\n\n if (value === true) {\n html.push(key);\n } else if (value !== false) {\n if (value === null || typeof value === 'undefined') value = '';\n html.push(`${key}=\"${value}\"`);\n }\n });\n\n return html.join(' ');\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,iBAAiB,CAAC,UAAU,EAAE;AACtD,IAAI,MAAM,IAAI,GAAG,EAAE;;AAEnB,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AAC3C,QAAQ,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC;;AAEnC,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE;AAC5B,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC1B,QAAQ,CAAC,MAAM,IAAI,KAAK,KAAK,KAAK,EAAE;AACpC,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE;AAC1E,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1C,QAAQ;AACR,IAAI,CAAC,CAAC;;AAEN,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACzB;;;;"} |
| {"version":3,"file":"getResult.js","sources":["../../src/utils/getResult.js"],"sourcesContent":["/**\n * Evaluate the expression. If it's a function, call it with the provided context and return the result.\n * Otherwise, return the expression as is.\n * @param {any} expression Expression to be evaluated.\n * @param {any} context Context to call the expression.\n * @param {...any} args Arguments to pass to the expression.\n * @return {any} The result of the expression.\n * @module\n * @private\n */\nconst getResult = (expression, context, ...args) =>\n typeof expression !== 'function' ? expression :\n expression.apply(context, args);\n\nexport default getResult;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACK,MAAC,SAAS,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI;AAC/C,IAAI,OAAO,UAAU,KAAK,UAAU,GAAG,UAAU;AACjD,QAAQ,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI;;;;"} |
| import repeat from './repeat.js'; | ||
| /** | ||
| * Pads a string to a given length with a given character. | ||
| * @param {string} string The string to pad. | ||
| * @param {number} length The length to pad the string to. | ||
| * @param {string} char The character to pad the string with. | ||
| * @return {string} The padded string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function padEnd(string, length, char = ' ') { | ||
| return string + repeat(char, length - string.length); | ||
| } | ||
| export { padEnd as default }; | ||
| //# sourceMappingURL=padEnd.js.map |
| {"version":3,"file":"padEnd.js","sources":["../../src/utils/padEnd.js"],"sourcesContent":["import repeat from './repeat.js';\n\n/**\n * Pads a string to a given length with a given character.\n * @param {string} string The string to pad.\n * @param {number} length The length to pad the string to.\n * @param {string} char The character to pad the string with.\n * @return {string} The padded string.\n * @module\n * @private\n */\nexport default function padEnd(string, length, char = ' ') {\n return string + repeat(char, length - string.length);\n}\n"],"names":[],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,GAAG,EAAE;AAC3D,IAAI,OAAO,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACxD;;;;"} |
| import repeat from './repeat.js'; | ||
| /** | ||
| * Pads a string to a given length with a given character at the start. | ||
| * @param {string} string The string to pad. | ||
| * @param {number} length The length to pad the string to. | ||
| * @param {string} char The character to pad the string with. | ||
| * @return {string} The padded string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function padStart(string, length, char = ' ') { | ||
| return repeat(char, length - string.length) + string; | ||
| } | ||
| export { padStart as default }; | ||
| //# sourceMappingURL=padStart.js.map |
| {"version":3,"file":"padStart.js","sources":["../../src/utils/padStart.js"],"sourcesContent":["import repeat from './repeat.js';\n\n/**\n * Pads a string to a given length with a given character at the start.\n * @param {string} string The string to pad.\n * @param {number} length The length to pad the string to.\n * @param {string} char The character to pad the string with.\n * @return {string} The padded string.\n * @module\n * @private\n */\nexport default function padStart(string, length, char = ' ') {\n return repeat(char, length - string.length) + string;\n}\n"],"names":[],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,GAAG,EAAE;AAC7D,IAAI,OAAO,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM;AACxD;;;;"} |
| {"version":3,"file":"parseHTML.js","sources":["../../src/utils/parseHTML.js"],"sourcesContent":["/**\n * Parse HTML string to a DocumentFragment.\n * @param {string} html The HTML string to parse.\n * @return {DocumentFragment} The parsed DocumentFragment.\n * @module\n * @private\n */\nexport default function parseHTML(html) {\n const fragment = document.createElement('template');\n fragment.innerHTML = `${html}`.trim();\n return fragment.content;\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,SAAS,CAAC,IAAI,EAAE;AACxC,IAAI,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;AACvD,IAAI,QAAQ,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;AACzC,IAAI,OAAO,QAAQ,CAAC,OAAO;AAC3B;;;;"} |
| /** | ||
| * Repeats a string a given number of times. | ||
| * @param {string} string The string to repeat. | ||
| * @param {number} length The number of times to repeat the string. | ||
| * @param {string} acc Accumulator for recursion. | ||
| * @return {string} The repeated string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function repeat(string, length, acc = '') { | ||
| return length > 0 ? repeat(string, length - 1, acc + string) : acc; | ||
| } | ||
| export { repeat as default }; | ||
| //# sourceMappingURL=repeat.js.map |
| {"version":3,"file":"repeat.js","sources":["../../src/utils/repeat.js"],"sourcesContent":["/**\n * Repeats a string a given number of times.\n * @param {string} string The string to repeat.\n * @param {number} length The number of times to repeat the string.\n * @param {string} acc Accumulator for recursion.\n * @return {string} The repeated string.\n * @module\n * @private\n */\nexport default function repeat(string, length, acc = '') {\n return length > 0 ? repeat(string, length - 1, acc + string) : acc;\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,EAAE;AACzD,IAAI,OAAO,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,GAAG;AACtE;;;;"} |
| {"version":3,"file":"replaceNode.js","sources":["../../src/utils/replaceNode.js"],"sourcesContent":["/**\n * Replaces an existing DOM node with a new node, preserving internal DOM state.\n * Uses moveBefore if available, otherwise falls back to before.\n * \n * @param {Node} oldNode The existing DOM node to replace.\n * @param {Node} newNode The new DOM node to replace the old node with.\n * @module\n * @private\n */\nexport default function replaceNode(oldNode, newNode) {\n if (Element.prototype.moveBefore) {\n oldNode.parentNode.moveBefore(newNode, oldNode);\n oldNode.parentNode.removeChild(oldNode);\n } else {\n const activeElement = document.activeElement;\n oldNode.parentNode.insertBefore(newNode, oldNode);\n oldNode.parentNode.removeChild(oldNode);\n if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) {\n activeElement.focus();\n }\n }\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE;AACtD,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,UAAU,EAAE;AACtC,QAAQ,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC;AACvD,QAAQ,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC;AAC/C,IAAI,CAAC,MAAM;AACX,QAAQ,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa;AACpD,QAAQ,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC;AACzD,QAAQ,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC;AAC/C,QAAQ,IAAI,aAAa,IAAI,aAAa,KAAK,QAAQ,CAAC,aAAa,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE;AAC1G,YAAY,aAAa,CAAC,KAAK,EAAE;AACjC,QAAQ;AACR,IAAI;AACJ;;;;"} |
| {"version":3,"file":"syncNode.js","sources":["../../src/utils/syncNode.js"],"sourcesContent":["/**\n * Properties that should be mirrored from source to target.\n * @type {string[]}\n * @private\n */\nconst SYNC_PROPS = ['value', 'checked', 'selected'];\n\n/**\n * Compares two elements to see if their child nodes are structurally equal.\n * @param {Element} a First element to compare.\n * @param {Element} b Second element to compare.\n * @return {boolean} True if all child nodes are deeply equal.\n * @private\n */\nconst areChildNodesEqual = (a, b) => {\n const aChildren = a.childNodes;\n const bChildren = b.childNodes;\n const aLength = aChildren.length;\n\n if (aLength !== bChildren.length) return false;\n\n for (let i = 0; i < aLength; i++) {\n if (!aChildren[i].isEqualNode(bChildren[i])) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Synchronizes attributes and key DOM properties (value, checked, selected).\n * Assumes tagName is already the same.\n * @param {Element} targetEl Target DOM element to update.\n * @param {Element} sourceEl Source DOM element to copy attributes from.\n * @private\n */\nconst syncAttributes = (targetEl, sourceEl) => {\n // Add, update and remove attributes.\n const srcAttrs = sourceEl.attributes;\n const tgtAttrs = targetEl.attributes;\n const srcAttrNames = new Set();\n\n for (let i = 0, l = srcAttrs.length; i < l; i++) {\n const { name, value } = srcAttrs[i];\n srcAttrNames.add(name);\n if (targetEl.getAttribute(name) !== value) targetEl.setAttribute(name, value);\n }\n\n for (let i = tgtAttrs.length - 1; i >= 0; i--) {\n const { name } = tgtAttrs[i];\n if (!srcAttrNames.has(name)) targetEl.removeAttribute(name);\n }\n // Sync key DOM properties.\n for (let i = 0, l = SYNC_PROPS.length; i < l; i++) {\n const prop = SYNC_PROPS[i];\n if (prop in targetEl && targetEl[prop] !== sourceEl[prop]) {\n targetEl[prop] = sourceEl[prop];\n }\n }\n};\n\n/**\n * Replaces all child nodes of targetEl with those from sourceEl.\n * Moves the nodes without cloning.\n * @param {Element} targetEl The element whose children will be replaced.\n * @param {Element} sourceEl The element providing new children.\n * @module\n * @private\n */\nconst syncNodeContent = (targetEl, sourceEl) => {\n const children = Array.from(sourceEl.childNodes);\n targetEl.replaceChildren(...children);\n};\n\n/**\n * Synchronizes the contents of one DOM node to match another.\n * It performs a shallow sync: tag name, attributes, properties, and child nodes.\n * If tag names or node types differ, it replaces the node entirely.\n * @param {Node} targetNode The node currently in the DOM to be updated.\n * @param {Node} sourceNode The reference node to sync from (not in the DOM).\n * @private\n */\nexport default function syncNode(targetNode, sourceNode) {\n // Replace if node types are different (e.g. element vs text).\n if (targetNode.nodeType !== sourceNode.nodeType) {\n targetNode.replaceWith(sourceNode);\n return;\n }\n // Text node: sync value.\n if (targetNode.nodeType === Node.TEXT_NODE) {\n if (targetNode.nodeValue !== sourceNode.nodeValue) {\n targetNode.nodeValue = sourceNode.nodeValue;\n }\n return;\n }\n // Element node: check tag.\n if (targetNode.tagName !== sourceNode.tagName) {\n targetNode.replaceWith(sourceNode);\n return;\n }\n // Sync attributes and DOM properties.\n syncAttributes(targetNode, sourceNode);\n // Sync child nodes if necessary.\n if (!areChildNodesEqual(targetNode, sourceNode)) {\n syncNodeContent(targetNode, sourceNode);\n }\n}\n"],"names":[],"mappings":"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"} |
| {"version":3,"file":"validateListener.js","sources":["../../src/utils/validateListener.js"],"sourcesContent":["import createDevelopmentErrorMessage from './createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './createProductionErrorMessage.js';\nimport __DEV__ from './dev.js';\n\n/**\n * Validates that the listener is a function.\n * @param {Function} listener The listener to validate.\n * @throws {TypeError} If the listener is not a function.\n * @module\n * @private\n */\nexport default function validateListener(listener) {\n if (typeof listener !== 'function') {\n const message = 'Event listener must be a function';\n throw new TypeError(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message));\n }\n}\n"],"names":[],"mappings":";;;;;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,gBAAgB,CAAC,QAAQ,EAAE;AACnD,IAAI,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;AACxC,QAAQ,MAAM,OAAO,GAAG,mCAAmC;AAC3D,QAAQ,MAAM,IAAI,SAAS,CAAC,OAAO,GAAG,6BAA6B,CAAC,OAAO,CAAC,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC;AACrH,IAAI;AACJ;;;;"} |
| {"version":3,"file":"View.js","sources":["../src/View.js"],"sourcesContent":["import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\nimport validateListener from './utils/validateListener.js';\n\n/*\n * These option keys will be extended on the view instance.\n */\nconst viewOptions = ['el', 'tag', 'attributes', 'events', 'model', 'template', 'onDestroy'];\n\n/**\n * - Listens for changes and renders the UI.\n * - Handles user input and interactivity.\n * - Sends captured input to the model.\n *\n * A `View` is an atomic unit of the user interface that can render data from a specific model or multiple models.\n * However, views can also be independent and have no associated data. \n * Models must be unaware of views. Views, on the other hand, may render model data and listen to the change events \n * emitted by the models to re-render themselves based on changes. \n * Each `View` has a root element, `this.el`, which is used for event delegation. \n * All element lookups are scoped to this element, and any rendering or DOM manipulations should be done inside it. \n * If `this.el` is not present, an element will be created using `this.tag` (defaulting to `div`) and `this.attributes`.\n * @module\n * @extends Emitter\n * @param {object} options Object containing options. The following keys will be merged into the view instance: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.\n * @property {node|Function} el Every view has a root DOM element stored at `this.el`. If not present, it will be created. If `this.el` is a function, it will be called to get the element at `this.ensureElement`, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {string|Function} tag If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. Default is `div`. If it is a function, it will be called to get the tag, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} attributes If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. If it is a function, it will be called to get the attributes object, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.\n * @property {object} model A model or any object containing data and business logic.\n * @property {Function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}. \n * @property {number} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.\n * @example\n * import { View, Model } from 'rasti';\n * \n * class Timer extends View {\n * constructor(options) {\n * super(options);\n * // Create model to store internal state. Set `seconds` attribute to 0.\n * this.model = new Model({ seconds : 0 });\n * // Listen to changes in model `seconds` attribute and re-render.\n * this.model.on('change:seconds', this.render.bind(this));\n * // Increment model `seconds` attribute every 1000 milliseconds.\n * this.interval = setInterval(() => this.model.seconds++, 1000);\n * }\n *\n * template(model) {\n * return `Seconds: <span>${model.seconds}</span>`;\n * }\n * }\n * // Render view and append view's element into the body.\n * document.body.appendChild(new Timer().render().el);\n */\nexport default class View extends Emitter {\n constructor(options = {}) {\n super();\n // Call preinitialize.\n this.preinitialize.apply(this, arguments);\n // Store delegated event listeners,\n // so they can be unbound later.\n this.delegatedEventListeners = [];\n // Store child views,\n // so they can be destroyed.\n this.children = [];\n // Mutable array to store handlers to be called on destroy.\n this.destroyQueue = [];\n this.viewOptions = [];\n // Extend \"this\" with options.\n viewOptions.forEach(key => {\n if (key in options) {\n this[key] = options[key];\n this.viewOptions.push(key);\n }\n });\n // Ensure that the view has a unique id at `this.uid`.\n this.ensureUid();\n // Ensure that the view has a root element at `this.el`.\n this.ensureElement();\n }\n\n /**\n * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.\n * @param {object} options The view options.\n */\n preinitialize() {}\n\n /**\n * Returns the first element that matches the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node} Element matching selector within the view's root element (`this.el`).\n */\n $(selector) {\n return this.el.querySelector(selector);\n }\n\n /**\n * Returns a list of elements that match the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node[]} List of elements matching selector within the view's root element (`this.el`).\n */\n $$(selector) {\n return this.el.querySelectorAll(selector);\n }\n\n /**\n * Destroy the view.\n * Destroy children views if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.\n * @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.\n * @return {Rasti.View} Return `this` for chaining.\n */\n destroy() {\n // Call destroy on children.\n this.destroyChildren();\n // Undelegate `this.el` event listeners\n this.undelegateEvents();\n // Stop listening to events.\n this.stopListening();\n // Unbind `this` events.\n this.off();\n // Call destroy queue.\n this.destroyQueue.forEach(fn => fn());\n this.destroyQueue = [];\n // Call onDestroy lifecycle method\n this.onDestroy.apply(this, arguments);\n // Set destroyed flag.\n this.destroyed = true;\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * `onDestroy` lifecycle method is called after the view is destroyed.\n * Override with your code. Useful to stop listening to model's events.\n * @param {object} options Options object or any arguments passed to `destroy` method.\n */\n onDestroy() {}\n\n /**\n * Add a view as a child.\n * Children views are stored at `this.children`, and destroyed when the parent is destroyed.\n * Returns the child for chaining.\n * @param {Rasti.View} child\n * @return {Rasti.View}\n */\n addChild(child) {\n this.children.push(child);\n return child;\n }\n\n /**\n * Call destroy method on children views.\n */\n destroyChildren() {\n this.children.forEach(child => child.destroy());\n this.children = [];\n }\n\n /**\n * Ensure that the view has a unique id at `this.uid`.\n */\n ensureUid() {\n if (!this.uid) this.uid = `r${++View.uid}`;\n }\n\n /**\n * Ensure that the view has a root element at `this.el`.\n * You shouldn't call this method directly. It's called from the constructor.\n * You may override it if you want to use a different logic or to \n * postpone element creation.\n */\n ensureElement() {\n // Element is already present.\n if (this.el) {\n // If \"this.el\" is a function, call it to get the element.\n this.el = getResult(this.el, this);\n } else {\n // If \"this.el\" is not present,\n // create a new element according \"this.tag\"\n // and \"this.attributes\".\n const tag = getResult(this.tag, this);\n const attrs = getResult(this.attributes, this);\n this.el = this.createElement(tag, attrs);\n }\n // Delegate events on element.\n this.delegateEvents();\n }\n\n /**\n * Create an element.\n * Called from the constructor if `this.el` is undefined, to ensure\n * the view has a root element.\n * @param {string} tag Tag for the element. Default to `div`\n * @param {object} attributes Attributes for the element.\n * @return {node} The created element.\n */\n createElement(tag = 'div', attributes = {}) {\n // Create DOM element.\n let el = document.createElement(tag);\n // Add element attributes.\n Object.keys(attributes)\n .forEach(key => el.setAttribute(key, attributes[key]));\n\n return el;\n }\n\n /**\n * Remove `this.el` from the DOM.\n * @return {Rasti.View} Return `this` for chaining.\n */\n removeElement() {\n this.el.parentNode.removeChild(this.el);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Provide declarative listeners for DOM events within a view. If an events object is not provided, \n * it defaults to using `this.events`. If `this.events` is a function, it will be called to get the events object.\n * \n * The events object should follow the format `{'event selector': 'listener'}`:\n * - `event`: The type of event (e.g., 'click').\n * - `selector`: A CSS selector to match the event target. If omitted, the event is bound to the root element.\n * - `listener`: A function or a string representing a method name on the view. The method will be called with `this` bound to the view instance.\n * \n * By default, `delegateEvents` is called within the View's constructor. If you have a simple events object, \n * all of your DOM events will be connected automatically, and you will not need to call this function manually.\n * \n * All attached listeners are bound to the view, ensuring that `this` refers to the view object when the listeners are invoked.\n * When `delegateEvents` is called again, possibly with a different events object, all previous listeners are removed and delegated afresh.\n * \n * **Listener signature:** `(event, view, matched)`\n * - `event`: The native DOM event object.\n * - `view`: The current view instance (`this`).\n * - `matched`: The element that satisfies the selector. If no selector is provided, it will be the view's root element (`this.el`).\n *\n * If more than one ancestor between `event.target` and the view's root element matches the selector, the listener will be\n * invoked **once for each matched element** (from inner to outer).\n *\n * @param {object} [events] Object in the format `{'event selector' : 'listener'}`. Used to bind delegated event listeners to the root element.\n * @return {Rasti.View} Returns `this` for chaining.\n * @example\n * // Using prototype (recommended for static events)\n * class Modal extends View {\n * onClickOk(event, view, matched) {\n * // matched === the button.ok element that was clicked\n * this.close();\n * }\n * \n * onClickCancel() {\n * this.destroy();\n * }\n * }\n * Modal.prototype.events = {\n * 'click button.ok': 'onClickOk',\n * 'click button.cancel': 'onClickCancel',\n * 'submit form': 'onSubmit'\n * };\n * \n * // Using a function for dynamic events\n * class DynamicView extends View {\n * events() {\n * return {\n * [`click .${this.model.buttonClass}`]: 'onButtonClick',\n * 'click': 'onRootClick'\n * };\n * }\n * }\n */\n delegateEvents(events) {\n if (!events) events = getResult(this.events, this);\n if (!events) return this;\n\n if (this.delegatedEventListeners.length) this.undelegateEvents();\n\n // Store events by type i.e.: \"click\", \"submit\", etc.\n let eventTypes = {};\n\n Object.keys(events).forEach(key => {\n const keyParts = key.split(' ');\n const type = keyParts.shift();\n const selector = keyParts.join(' ');\n\n let listener = events[key];\n // Listener may be a string representing a method name on the view, or a function.\n if (typeof listener === 'string') listener = this[listener];\n // Validate listener is a function.\n validateListener(listener);\n\n if (!eventTypes[type]) eventTypes[type] = [];\n\n eventTypes[type].push({ selector, listener });\n });\n\n Object.keys(eventTypes).forEach(type => {\n // Listener for the type of event.\n const typeListener = (event) => {\n // Iterate and run every individual listener if the selector matches.\n eventTypes[type].forEach(({ selector, listener }) => {\n // No selector provided: invoke listener once with root element.\n if (!selector) {\n listener.call(this, event, this, this.el);\n return;\n }\n\n let node = event.target;\n // Traverse ancestors until reaching the view root (`this.el`).\n while (node && node !== this.el) {\n if (node.matches && node.matches(selector)) {\n listener.call(this, event, this, node);\n }\n node = node.parentElement;\n }\n });\n };\n\n this.delegatedEventListeners.push({ type, listener : typeListener });\n this.el.addEventListener(type, typeListener);\n });\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Removes all of the view's delegated events. \n * Useful if you want to disable or remove a view from the DOM temporarily. \n * Called automatically when the view is destroyed and when `delegateEvents` is called again.\n * @return {Rasti.View} Return `this` for chaining.\n */\n undelegateEvents() {\n this.delegatedEventListeners.forEach(({ type, listener }) => {\n this.el.removeEventListener(type, listener);\n });\n\n this.delegatedEventListeners = [];\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Renders the view. \n * This method should be overridden with custom logic.\n * The only convention is to manipulate the DOM within the scope of `this.el`,\n * and to return `this` for chaining. \n * If you add any child views, you should call `this.destroyChildren` before re-rendering. \n * The default implementation updates `this.el`'s innerHTML with the result\n * of calling `this.template`, passing `this.model` as the argument.\n * <br><br> ⚠ **Security Notice:** The default implementation utilizes `innerHTML`, which may introduce Cross-Site Scripting (XSS) risks. \n * Ensure that any user-generated content is properly sanitized before inserting it into the DOM. \n * You can use the {@link #module_view_sanitize View.sanitize} static method to escape HTML entities in a string. \n * For best practices on secure data handling, refer to the \n * [OWASP's XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).<br><br>\n * @return {Rasti.View} Returns `this` for chaining.\n */\n render() {\n if (this.template) this.el.innerHTML = this.template(this.model);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Escape HTML entities in a string.\n * Use this method to sanitize user-generated content before inserting it into the DOM.\n * Override this method to provide a custom escape function.\n * This method is inherited by {@link #module_component Component} and used to escape template interpolations.\n * @static\n * @param {string} value String to escape.\n * @return {string} Escaped string.\n */\n static sanitize(value) {\n return `${value}`.replace(/[&<>\"']/g, match => ({\n '&' : '&',\n '<' : '<',\n '>' : '>',\n '\"' : '"',\n '\\'' : '''\n }[match]));\n }\n}\n\n/**\n * Counter for generating unique IDs for view instances. \n * This is primarily used to assign unique identifiers to each view instance (`this.uid`), which can be helpful for tasks like \n * generating element IDs. \n * {@link #module_component Component}s use `this.uid` to generate data attributes for their elements, to be looked up on hydration. \n * For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated \n * unique IDs match those on the client, enabling seamless hydration of components. \n * @static\n * @type {number}\n * @default 0\n */\nView.uid = 0;\n"],"names":[],"mappings":"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"} |
Sorry, the diff of this file is too big to display
| {"version":3,"file":"Element.cjs","sources":["../../src/core/Element.js"],"sourcesContent":["import getAttributesDiff from '../utils/getAttributesDiff.js';\n\nconst SYNC_PROPS = ['value', 'checked', 'selected'];\n\n/**\n * Element reference for managing DOM element attributes.\n * @param {Object} options The options object.\n * @param {Function} options.getSelector Function that returns the CSS selector for the element.\n * @param {Function} options.getAttributes Function that returns the attributes object for the element.\n * @private\n */\nclass Element {\n constructor(options) {\n this.getSelector = options.getSelector;\n this.getAttributes = options.getAttributes;\n this.previousAttributes = {};\n }\n\n /**\n * Attach the element reference to a DOM element.\n * @param {Node} parent The parent node to search in.\n */\n hydrate(parent) {\n this.ref = parent.querySelector(this.getSelector());\n }\n\n /**\n * Update the element's attributes based on the difference with previous attributes.\n */\n update() {\n // Attributes diff.\n const attributes = this.getAttributes();\n const { remove, add } = getAttributesDiff(attributes, this.previousAttributes);\n // Store previous attributes.\n this.previousAttributes = attributes;\n // Remove attributes first so later `setAttribute` overrides if needed.\n remove.forEach(attr => {\n this.ref.removeAttribute(attr);\n if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) {\n // Reset property to default.\n this.ref[attr] = attr === 'value' ? '' : false;\n }\n });\n // Add / update attributes.\n Object.keys(add).forEach(attr => {\n const value = add[attr];\n this.ref.setAttribute(attr, value);\n if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) {\n this.ref[attr] = attr === 'value' ? value : value !== false && value !== 'false';\n }\n });\n }\n}\n\nexport default Element;\n"],"names":["getAttributesDiff"],"mappings":";;;;AAEA,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC;;AAEnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,OAAO,CAAC;AACd,IAAI,WAAW,CAAC,OAAO,EAAE;AACzB,QAAQ,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW;AAC9C,QAAQ,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa;AAClD,QAAQ,IAAI,CAAC,kBAAkB,GAAG,EAAE;AACpC,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,OAAO,CAAC,MAAM,EAAE;AACpB,QAAQ,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;AAC3D,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,MAAM,GAAG;AACb;AACA,QAAQ,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE;AAC/C,QAAQ,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAGA,uBAAiB,CAAC,UAAU,EAAE,IAAI,CAAC,kBAAkB,CAAC;AACtF;AACA,QAAQ,IAAI,CAAC,kBAAkB,GAAG,UAAU;AAC5C;AACA,QAAQ,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;AAC/B,YAAY,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;AAC1C,YAAY,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE;AACrE;AACA,gBAAgB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,OAAO,GAAG,EAAE,GAAG,KAAK;AAC9D,YAAY;AACZ,QAAQ,CAAC,CAAC;AACV;AACA,QAAQ,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI;AACzC,YAAY,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC;AACnC,YAAY,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;AAC9C,YAAY,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE;AACrE,gBAAgB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,OAAO,GAAG,KAAK,GAAG,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,OAAO;AAChG,YAAY;AACZ,QAAQ,CAAC,CAAC;AACV,IAAI;AACJ;;;;"} |
| {"version":3,"file":"EventsManager.cjs","sources":["../../src/core/EventsManager.js"],"sourcesContent":["/**\n * Manager for delegated events.\n * @private\n */\nclass EventsManager {\n constructor() {\n this.listeners = [];\n this.types = new Set();\n this.previousSize = 0;\n }\n\n /**\n * Add a listener to the events manager.\n * @param {Function} listener The listener to add.\n * @param {string} type The type of event.\n * @return {number} The index of the listener.\n */\n addListener(listener, type) {\n this.types.add(type);\n this.listeners.push(listener);\n return this.listeners.length - 1;\n }\n\n /**\n * Reset the events manager.\n */\n reset() {\n this.listeners = [];\n this.previousSize = this.types.size;\n }\n\n /**\n * Check if there are pending types.\n * @return {boolean} True if there are pending types, false otherwise.\n */\n hasPendingTypes() {\n return this.types.size > this.previousSize;\n }\n}\n\nexport default EventsManager;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA,MAAM,aAAa,CAAC;AACpB,IAAI,WAAW,GAAG;AAClB,QAAQ,IAAI,CAAC,SAAS,GAAG,EAAE;AAC3B,QAAQ,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE;AAC9B,QAAQ,IAAI,CAAC,YAAY,GAAG,CAAC;AAC7B,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE;AAChC,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;AACrC,QAAQ,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;AACxC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,SAAS,GAAG,EAAE;AAC3B,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI;AAC3C,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,eAAe,GAAG;AACtB,QAAQ,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,YAAY;AAClD,IAAI;AACJ;;;;"} |
| {"version":3,"file":"Interpolation.cjs","sources":["../../src/core/Interpolation.js"],"sourcesContent":["import PathManager from './PathManager.js';\nimport syncNode from '../utils/syncNode.js';\nimport findComment from '../utils/findComment.js';\n\n/**\n * Interpolation reference for managing dynamic content between comment markers.\n * Handles the lifecycle of content that can change between renders, including\n * component recycling and DOM synchronization.\n * @param {Object} options The options object.\n * @param {Function} options.getStart Function that returns the start comment marker text.\n * @param {Function} options.getEnd Function that returns the end comment marker text.\n * @param {any} options.expression The expression to be evaluated for the interpolation.\n * @param {Function} options.isComponent Function that checks if an element is a component root element.\n * @param {Function} options.containsComponent Function that checks if an element contains a component.\n * @private\n */\nclass Interpolation {\n constructor(options) {\n this.getStart = options.getStart;\n this.getEnd = options.getEnd;\n this.expression = options.expression;\n this.shouldSkipFind = options.shouldSkipFind;\n this.shouldSkipSync = options.shouldSkipSync;\n this.tracker = new PathManager();\n }\n\n /**\n * Attach the interpolation reference to comment markers in the DOM.\n * Searches for start and end comment markers, skipping component subtrees.\n * @param {Node} parent The parent node to search in.\n */\n hydrate(parent) {\n const startMark = findComment(parent, this.getStart(), this.shouldSkipFind);\n const endMark = findComment(parent, this.getEnd(), this.shouldSkipFind, startMark);\n\n this.ref = [\n startMark,\n endMark\n ];\n }\n\n /**\n * Update the interpolation content with a new fragment.\n * Optimizes updates by syncing single non-component elements or replacing content entirely.\n * @param {DocumentFragment} fragment The new content fragment to insert.\n * @param {Element} targetElement The target element to replace with the new fragment.\n */\n update(fragment, handleComponents) {\n let divider;\n // Reference to marker elements.\n const [startMark, endMark] = this.ref;\n\n const currentFirstElement = startMark.nextSibling;\n // Check if the interpolation is empty.\n const currentEmpty = currentFirstElement === endMark;\n // Check if the interpolation has a single child element.\n const currentSingleChildElement = !currentEmpty && currentFirstElement.nextSibling === endMark;\n\n if (currentEmpty) {\n // The interpolation is empty. Insert the fragment.\n endMark.parentNode.insertBefore(fragment, endMark);\n } else if (\n currentSingleChildElement &&\n fragment.children.length === 1 &&\n !this.shouldSkipSync(currentFirstElement) &&\n !this.shouldSkipSync(fragment.firstChild)\n ) {\n // The interpolation has a single child element and the new fragment has a single child element.\n // The elements doesn't contain a component. Sync node attributes and content.\n syncNode(currentFirstElement, fragment.firstChild);\n } else {\n // The interpolation has multiple child elements or the new fragment has multiple child elements.\n // Insert a divider comment before the end marker and insert the new fragment after the divider.\n divider = document.createComment('');\n endMark.parentNode.insertBefore(divider, endMark);\n endMark.parentNode.insertBefore(fragment, endMark);\n }\n // Handle the components in the fragment. Execute the handler function.\n handleComponents();\n\n if (divider) {\n // Remove old content and divider.\n const startMark = this.ref[0];\n if (startMark.nextSibling === divider) {\n divider.parentNode.removeChild(divider);\n } else {\n const range = document.createRange();\n range.setStartAfter(this.ref[0]);\n range.setEndAfter(divider);\n range.deleteContents();\n }\n }\n }\n\n /**\n * Update the interpolation content with a new fragment. This is used for container components.\n * @param {Element} element The element to replace with the new fragment.\n * @param {DocumentFragment} fragment The new content fragment to insert.\n * @param {Function} handleComponents Function to handle the components in the fragment before removing the old content.\n */\n updateElement(element, fragment, handleComponents) {\n const divider = document.createComment('');\n element.parentNode.insertBefore(divider, element.nextSibling);\n divider.parentNode.insertBefore(fragment.firstChild, divider.nextSibling);\n handleComponents();\n if (element.nextSibling === divider) element.parentNode.removeChild(element);\n divider.parentNode.removeChild(divider);\n }\n}\n\nexport default Interpolation;\n"],"names":["PathManager","findComment","syncNode"],"mappings":"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"} |
| {"version":3,"file":"Partial.cjs","sources":["../../src/core/Partial.js"],"sourcesContent":["/**\n * Wrapper class for partial templates that preserves structure.\n * @param {Array} items The items in the partial.\n * @private\n */\nclass Partial {\n constructor(items) {\n this.items = items;\n }\n}\n\nexport default Partial;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA,MAAM,OAAO,CAAC;AACd,IAAI,WAAW,CAAC,KAAK,EAAE;AACvB,QAAQ,IAAI,CAAC,KAAK,GAAG,KAAK;AAC1B,IAAI;AACJ;;;;"} |
| {"version":3,"file":"PathManager.cjs","sources":["../../src/core/PathManager.js"],"sourcesContent":["/**\n * Manager for component position tracking and recycling.\n * @private\n */\nclass PathManager {\n constructor() {}\n\n /**\n * Reset before render.\n */\n reset() {\n this.paused = 0;\n this.previous = this.tracked || new Map();\n this.tracked = new Map();\n this.positionStack = [0];\n }\n\n /**\n * Push position to stack.\n */\n push() {\n this.positionStack.push(0);\n }\n\n /**\n * Pop position from stack.\n */\n pop() {\n this.positionStack.pop();\n }\n\n /**\n * Increment position.\n */\n increment() {\n this.positionStack[this.positionStack.length - 1]++;\n }\n\n /**\n * Pause tracking.\n */\n pause() {\n this.paused++;\n }\n\n /**\n * Resume tracking.\n */\n resume() {\n this.paused--;\n }\n\n /**\n * Get current path as array.\n * @return {string} Current position path.\n */\n getPath() {\n return this.positionStack.join('-');\n }\n\n /**\n * Track component at current path.\n * @param {Component} component The component to track.\n * @return {Component} The component.\n */\n track(component) {\n if (this.paused === 0) {\n this.tracked.set(\n this.getPath(),\n component\n );\n }\n\n return component;\n }\n\n /**\n * Tell if there was only one component rendered in the previous and current tracked maps\n * and that component instance is the same (was recycled).\n * Used by Component to detect simple single component cases and skip DOM moves.\n * @return {boolean} Returns true when there is exactly one component at the first level\n * and it was successfully recycled.\n */\n hasSingleComponent() {\n if (this.tracked.size !== 1 || this.previous.size !== 1) return false;\n const [currentPath, currentComponent] = this.tracked.entries().next().value;\n const [previousPath, previousComponent] = this.previous.entries().next().value;\n // Ensure the component is at the root level (path '0') so it is not part of a deeper partial/array.\n if (currentPath !== '0' || previousPath !== '0') return false;\n return currentComponent === previousComponent;\n }\n\n /**\n * Find a recyclable component for the current path based on constructor (type)\n * when the component is un-keyed.\n * @param {Component} candidate The candidate component instance.\n * @return {Component|null} The recyclable component or null if none found.\n */\n findRecyclable(candidate) {\n const previous = this.previous.get(this.getPath());\n return previous && !previous.key && previous.constructor === candidate.constructor ? previous : null;\n }\n}\n\nexport default PathManager;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA,MAAM,WAAW,CAAC;AAClB,IAAI,WAAW,GAAG,CAAC;;AAEnB;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,MAAM,GAAG,CAAC;AACvB,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,GAAG,EAAE;AACjD,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE;AAChC,QAAQ,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;AAChC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,IAAI,GAAG;AACX,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AAClC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,GAAG,GAAG;AACV,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE;AAChC,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE;AAC3D,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,MAAM,EAAE;AACrB,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,MAAM,GAAG;AACb,QAAQ,IAAI,CAAC,MAAM,EAAE;AACrB,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,OAAO,GAAG;AACd,QAAQ,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;AAC3C,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA,IAAI,KAAK,CAAC,SAAS,EAAE;AACrB,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;AAC/B,YAAY,IAAI,CAAC,OAAO,CAAC,GAAG;AAC5B,gBAAgB,IAAI,CAAC,OAAO,EAAE;AAC9B,gBAAgB;AAChB,aAAa;AACb,QAAQ;;AAER,QAAQ,OAAO,SAAS;AACxB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,kBAAkB,GAAG;AACzB,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,OAAO,KAAK;AAC7E,QAAQ,MAAM,CAAC,WAAW,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK;AACnF,QAAQ,MAAM,CAAC,YAAY,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK;AACtF;AACA,QAAQ,IAAI,WAAW,KAAK,GAAG,IAAI,YAAY,KAAK,GAAG,EAAE,OAAO,KAAK;AACrE,QAAQ,OAAO,gBAAgB,KAAK,iBAAiB;AACrD,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,cAAc,CAAC,SAAS,EAAE;AAC9B,QAAQ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AAC1D,QAAQ,OAAO,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,CAAC,WAAW,GAAG,QAAQ,GAAG,IAAI;AAC5G,IAAI;AACJ;;;;"} |
| {"version":3,"file":"SafeHTML.cjs","sources":["../../src/core/SafeHTML.js"],"sourcesContent":["/**\n * Wrapper class for HTML strings marked as safe.\n * @param {string} value The HTML string to be marked as safe.\n * @property {string} value The HTML string.\n * @private\n */\nclass SafeHTML {\n constructor(value) {\n this.value = value;\n }\n\n toString() {\n return this.value;\n }\n}\n\nexport default SafeHTML;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,QAAQ,CAAC;AACf,IAAI,WAAW,CAAC,KAAK,EAAE;AACvB,QAAQ,IAAI,CAAC,KAAK,GAAG,KAAK;AAC1B,IAAI;;AAEJ,IAAI,QAAQ,GAAG;AACf,QAAQ,OAAO,IAAI,CAAC,KAAK;AACzB,IAAI;AACJ;;;;"} |
| {"version":3,"file":"Emitter.cjs","sources":["../src/Emitter.js"],"sourcesContent":["import validateListener from './utils/validateListener.js';\n\n/**\n * `Emitter` is a class that provides an easy way to implement the observer pattern \n * in your applications. \n * It can be extended to create new classes that have the ability to emit and bind custom named events. \n * Emitter is used by `Model` and `View` classes, which inherit from it to implement \n * event-driven functionality.\n * \n * ## Inverse of Control Pattern\n * \n * The Emitter class includes \"inverse of control\" methods (`listenTo`, `listenToOnce`, `stopListening`) \n * that allow an object to manage its own listening relationships. Instead of:\n * \n * ```javascript\n * // Traditional approach - harder to clean up\n * otherObject.on('change', this.myHandler);\n * otherObject.on('destroy', this.cleanup);\n * // Later you need to remember to clean up each listener\n * otherObject.off('change', this.myHandler);\n * otherObject.off('destroy', this.cleanup);\n * ```\n * \n * You can use:\n * \n * ```javascript\n * // Inverse of control - easier cleanup\n * this.listenTo(otherObject, 'change', this.myHandler);\n * this.listenTo(otherObject, 'destroy', this.cleanup);\n * // Later, clean up ALL listeners at once\n * this.stopListening(); // Removes all listening relationships\n * ```\n * \n * This pattern is particularly useful for preventing memory leaks and simplifying cleanup\n * in component lifecycle management.\n *\n * @module\n * @example\n * import { Emitter } from 'rasti';\n * // Custom cart\n * class ShoppingCart extends Emitter {\n * constructor() {\n * super();\n * this.items = [];\n * }\n *\n * addItem(item) {\n * this.items.push(item);\n * // Emit a custom event called `itemAdded`.\n * // Pass the added item as an argument to the event listener.\n * this.emit('itemAdded', item);\n * }\n * }\n * // Create an instance of ShoppingCart and Logger\n * const cart = new ShoppingCart();\n * // Listen to the `itemAdded` event and log the added item using the logger.\n * cart.on('itemAdded', (item) => {\n * console.log(`Item added to cart: ${item.name} - Price: $${item.price}`);\n * });\n * // Simulate adding items to the cart\n * const item1 = { name : 'Smartphone', price : 1000 };\n * const item2 = { name : 'Headphones', price : 150 };\n *\n * cart.addItem(item1); // Output: \"Item added to cart: Smartphone - Price: $1000\"\n * cart.addItem(item2); // Output: \"Item added to cart: Headphones - Price: $150\"\n */\nexport default class Emitter {\n /**\n * Adds event listener.\n * @param {string} type Type of the event (e.g. `change`).\n * @param {Function} listener Callback function to be called when the event is emitted.\n * @return {Function} A function to remove the listener.\n * @example\n * // Re render when model changes.\n * this.model.on('change', this.render.bind(this));\n */\n on(type, listener) {\n // Validate listener.\n validateListener(listener);\n // Create listeners object if it doesn't exist.\n if (!this.listeners) this.listeners = {};\n // Every type must have an array of listeners.\n if (!this.listeners[type]) this.listeners[type] = [];\n // Add listener to the array of listeners.\n this.listeners[type].push(listener);\n // Return a function to remove the listener.\n return () => this.off(type, listener);\n }\n\n /**\n * Adds event listener that executes once.\n * @param {string} type Type of the event (e.g. `change`).\n * @param {Function} listener Callback function to be called when the event is emitted.\n * @return {Function} A function to remove the listener.\n * @example\n * // Log a message once when model changes.\n * this.model.once('change', () => console.log('This will happen once'));\n */\n once(type, listener) {\n // Validate listener.\n validateListener(listener);\n // Wrap listener to remove it after it is called.\n const wrapper = (...args) => {\n listener(...args);\n this.off(type, wrapper);\n };\n // Add listener.\n return this.on(type, wrapper);\n }\n\n /**\n * Removes event listeners with flexible parameter combinations.\n * @param {string} [type] Type of the event (e.g. `change`). If not provided, removes ALL listeners from this emitter.\n * @param {Function} [listener] Specific callback function to remove. If not provided, removes all listeners for the specified type.\n * \n * **Behavior based on parameters:**\n * - `off()` - Removes ALL listeners from this emitter\n * - `off(type)` - Removes all listeners for the specified event type\n * - `off(type, listener)` - Removes the specific listener for the specified event type\n * \n * @example\n * // Remove all listeners from this emitter\n * this.model.off();\n * \n * @example\n * // Remove all 'change' event listeners\n * this.model.off('change');\n * \n * @example\n * // Remove specific listener for 'change' events\n * const myListener = () => console.log('changed');\n * this.model.on('change', myListener);\n * this.model.off('change', myListener);\n */\n off(type, listener) {\n // No listeners.\n if (!this.listeners) return;\n // No type provided, remove all listeners.\n if (!type) {\n delete this.listeners;\n return;\n }\n // No listeners for specified type.\n if (!this.listeners[type]) return;\n // No listener provided, remove all listeners for specified type.\n if (!listener) {\n delete this.listeners[type];\n } else {\n // Remove specific listener.\n this.listeners[type] = this.listeners[type].filter(fn => fn !== listener);\n if (!this.listeners[type].length) delete this.listeners[type];\n }\n // Remove listeners object if it's empty.\n if (!Object.keys(this.listeners).length) delete this.listeners;\n }\n\n /**\n * Emits event of specified type. Listeners will receive specified arguments.\n * @param {string} type Type of the event (e.g. `change`).\n * @param {...any} [args] Optional arguments to be passed to listeners.\n * @example\n * // Emit validation error event with no arguments\n * this.emit('invalid');\n * \n * @example\n * // Emit change event with data\n * this.emit('change', { field : 'name', value : 'John' });\n */\n emit(type, ...args) {\n // No listeners.\n if (!this.listeners || !this.listeners[type]) return;\n // Call listeners. Use `slice` to make a copy and prevent errors when \n // removing listeners inside a listener.\n this.listeners[type].slice().forEach(fn => fn(...args));\n }\n\n /**\n * Listen to an event of another emitter (Inverse of Control pattern).\n * \n * This method allows this object to manage its own listening relationships,\n * making cleanup easier and preventing memory leaks. Instead of calling\n * `otherEmitter.on()`, you call `this.listenTo(otherEmitter, ...)` which\n * allows this object to track and clean up all its listeners at once.\n * \n * @param {Emitter} emitter The emitter to listen to.\n * @param {string} type The type of the event to listen to.\n * @param {Function} listener The listener to call when the event is emitted.\n * @return {Function} A function to stop listening to the event.\n * \n * @example\n * // Instead of: otherModel.on('change', this.render.bind(this));\n * // Use: this.listenTo(otherModel, 'change', this.render.bind(this));\n * // This way you can later call this.stopListening() to clean up all listeners\n */\n listenTo(emitter, type, listener) {\n // Add listener to the emitter.\n emitter.on(type, listener);\n // Create listeningTo array if it doesn't exist.\n if (!this.listeningTo) this.listeningTo = [];\n // Add listener to the array of listeners.\n this.listeningTo.push({ emitter, type, listener });\n // Return a function to stop listening to the event.\n return () => this.stopListening(emitter, type, listener);\n }\n\n /**\n * Listen to an event of another emitter and remove the listener after it is called (Inverse of Control pattern).\n * \n * Similar to `listenTo()` but automatically removes the listener after the first execution,\n * like `once()` but with the inverse of control benefits for cleanup management.\n * \n * @param {Emitter} emitter The emitter to listen to.\n * @param {string} type The type of the event to listen to.\n * @param {Function} listener The listener to call when the event is emitted.\n * @return {Function} A function to stop listening to the event.\n * \n * @example\n * // Listen once to another emitter's initialization event\n * this.listenToOnce(otherModel, 'initialized', () => {\n * console.log('Other model initialized');\n * });\n */\n listenToOnce(emitter, type, listener) {\n validateListener(listener);\n // Wrap listener to remove it after it is called.\n const wrapper = (...args) => {\n listener(...args);\n this.stopListening(emitter, type, wrapper);\n };\n // Add listener.\n return this.listenTo(emitter, type, wrapper);\n }\n\n /**\n * Stop listening to events from other emitters (Inverse of Control pattern).\n * \n * This method provides flexible cleanup of listening relationships established with `listenTo()`.\n * All parameters are optional, allowing different levels of cleanup granularity.\n * \n * @param {Emitter} [emitter] The emitter to stop listening to. If not provided, stops listening to ALL emitters.\n * @param {string} [type] The type of event to stop listening to. If not provided, stops listening to all event types from the specified emitter.\n * @param {Function} [listener] The specific listener to remove. If not provided, removes all listeners for the specified event type from the specified emitter.\n * \n * **Behavior based on parameters:**\n * - `stopListening()` - Stops listening to ALL events from ALL emitters\n * - `stopListening(emitter)` - Stops listening to all events from the specified emitter\n * - `stopListening(emitter, type)` - Stops listening to the specified event type from the specified emitter\n * - `stopListening(emitter, type, listener)` - Stops listening to the specific listener for the specific event from the specific emitter\n * \n * @example\n * // Stop listening to all events from all emitters (complete cleanup)\n * this.stopListening();\n * \n * @example\n * // Stop listening to all events from a specific emitter\n * this.stopListening(otherModel);\n * \n * @example\n * // Stop listening to 'change' events from a specific emitter\n * this.stopListening(otherModel, 'change');\n * \n * @example\n * // Stop listening to a specific listener\n * const myListener = () => console.log('changed');\n * this.listenTo(otherModel, 'change', myListener);\n * this.stopListening(otherModel, 'change', myListener);\n */\n stopListening(emitter, type, listener) {\n // No listeningTo object.\n if (!this.listeningTo) return;\n // Remove listener from the array of listeners.\n this.listeningTo = this.listeningTo.filter(item => {\n if (\n !emitter ||\n (emitter === item.emitter && !type) ||\n (emitter === item.emitter && type === item.type && !listener) ||\n (emitter === item.emitter && type === item.type && listener === item.listener)\n ) {\n item.emitter.off(item.type, item.listener);\n return false;\n }\n return true;\n });\n // Remove listeningTo object if it's empty.\n if (!this.listeningTo.length) delete this.listeningTo;\n }\n}\n"],"names":["validateListener"],"mappings":"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"} |
| {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} |
| {"version":3,"file":"Model.cjs","sources":["../src/Model.js"],"sourcesContent":["import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\n\n/**\n * - Orchestrates data and business logic.\n * - Emits events when data changes.\n * \n * A `Model` manages an internal table of data attributes and triggers change events when any of its data is modified. \n * Models may handle syncing data with a persistence layer. To design your models, create atomic, reusable objects \n * that contain all the necessary functions for manipulating their specific data. \n * Models should be easily passed throughout your app and used anywhere the corresponding data is needed.\n * \n * ## Construction Flow\n * 1. `preinitialize()` is called with all constructor arguments\n * 2. `this.defaults` are resolved (if function, it's called and bound to the model)\n * 3. `parse()` is called with all constructor arguments to process the data\n * 4. `this.attributes` is built by merging defaults and parsed data\n * 5. Getters/setters are generated for each attribute to emit change events\n * \n * @module\n * @extends Emitter\n * @param {object} [attributes={}] Primary data object containing model attributes\n * @param {...*} [args] Additional arguments passed to `preinitialize` and `parse` methods\n * @property {object|Function} defaults Default attributes for the model. If a function, it's called bound to the model instance to get defaults.\n * @property {object} previous Object containing previous attributes when a change occurs.\n * @property {string} attributePrefix Static property that defines a prefix for generated getters/setters. Defaults to empty string.\n * @example\n * import { Model } from 'rasti';\n * \n * // User model\n * class User extends Model {\n * preinitialize() {\n * this.defaults = { name : '', email : '', role : 'user' };\n * }\n * }\n * // Order model with nested User and custom methods\n * class Order extends Model {\n * preinitialize(attributes, options = {}) {\n * this.defaults = {\n * id : null,\n * total : 0,\n * status : 'pending',\n * user : null\n * };\n * \n * this.apiUrl = options.apiUrl || '/api/orders';\n * }\n *\n * parse(data, options = {}) {\n * const parsed = { ...data };\n * \n * // Convert user object to User model instance\n * if (data.user && !(data.user instanceof User)) {\n * parsed.user = new User(data.user);\n * }\n * \n * return parsed;\n * }\n * \n * toJSON() {\n * const result = {};\n * for (const [key, value] of Object.entries(this.attributes)) {\n * if (value instanceof Model) {\n * result[key] = value.toJSON();\n * } else {\n * result[key] = value;\n * }\n * }\n * return result;\n * }\n * \n * async fetch() {\n * try {\n * const response = await fetch(`${this.apiUrl}/${this.id}`);\n * const data = await response.json();\n * \n * // Parse the fetched data and update model\n * const parsed = this.parse(data);\n * this.set(parsed, { source : 'fetch' });\n * \n * return this;\n * } catch (error) {\n * console.error('Failed to fetch order:', error);\n * throw error;\n * }\n * }\n * }\n * \n * // Create order with nested user data\n * const order = new Order({\n * id : 123,\n * total : 99.99,\n * user : { name : 'Alice', email : 'alice@example.com' }\n * });\n * \n * console.log(order.user instanceof User); // true\n * // Serialize with nested models\n * const json = order.toJSON();\n * console.log(json); // { id: 123, total: 99.99, status: 'pending', user: { name: 'Alice', email: 'alice@example.com', role: 'user' } }\n * \n * // Listen to fetch updates\n * order.on('change', (model, changed, options) => {\n * if (options?.source === 'fetch') {\n * console.log('Order updated from server:', changed);\n * }\n * });\n * \n * // Fetch latest data from server\n * await order.fetch();\n */\nexport default class Model extends Emitter {\n constructor() {\n super();\n // Call preinitialize.\n this.preinitialize.apply(this, arguments);\n // Set attributes object with defaults and passed attributes.\n this.attributes = Object.assign({}, getResult(this.defaults, this), this.parse.apply(this, arguments));\n // Object to store previous attributes when a change occurs.\n this.previous = {};\n // Generate getters/setters for every attribute.\n Object.keys(this.attributes).forEach(this.defineAttribute.bind(this));\n }\n\n /**\n * Called before any instantiation logic runs for the Model.\n * Receives all constructor arguments, allowing for flexible initialization patterns.\n * Use this to set up `defaults`, configure the model, or handle custom constructor arguments.\n * @param {object} [attributes={}] Primary data object containing model attributes\n * @param {...*} [args] Additional arguments passed from the constructor\n * @example\n * class User extends Model {\n * preinitialize(attributes, options = {}) {\n * this.defaults = { name : '', role : options.defaultRole || 'user' };\n * this.apiEndpoint = options.apiEndpoint || '/users';\n * }\n * }\n * const user = new User({ name : 'Alice' }, { defaultRole : 'admin', apiEndpoint : '/api/users' });\n */\n preinitialize() {}\n\n /**\n * Generate getter/setter for the given attribute key to emit `change` events.\n * The property name uses `attributePrefix` + key (e.g., with prefix 'attr_', key 'name' becomes 'attr_name').\n * Called internally by the constructor for each key in `this.attributes`.\n * Override with an empty method if you don't want automatic getters/setters.\n * \n * @param {string} key Attribute key from `this.attributes`\n * @example\n * // Custom prefix for all attributes\n * class PrefixedModel extends Model {\n * static attributePrefix = 'attr_';\n * }\n * const model = new PrefixedModel({ name: 'Alice' });\n * console.log(model.attr_name); // 'Alice'\n * \n * // Disable automatic getters/setters\n * class ManualModel extends Model {\n * defineAttribute() {\n * // Empty - no getters/setters generated\n * }\n * \n * getName() {\n * return this.get('name'); // Manual getter\n * }\n * }\n */\n defineAttribute(key) {\n Object.defineProperty(\n this,\n `${this.constructor.attributePrefix}${key}`,\n {\n get : () => this.get(key),\n set : (value) => { this.set(key, value); }\n }\n );\n }\n\n /**\n * Get an attribute from `this.attributes`.\n * This method is called internally by generated getters.\n * @param {string} key Attribute key.\n * @return {any} The attribute value.\n */\n get(key) {\n return this.attributes[key];\n }\n\n /**\n * Set one or more attributes into `this.attributes` and emit change events.\n * Supports two call signatures: `set(key, value, ...args)` or `set(object, ...args)`.\n * Additional arguments are passed to change event listeners, enabling custom behavior.\n * \n * @param {string|object} key Attribute key (string) or object containing key-value pairs\n * @param {*} [value] Attribute value (when key is string)\n * @param {...*} [args] Additional arguments passed to event listeners\n * @return {Model} This model instance for chaining\n * @emits change Emitted when any attribute changes. Listeners receive `(model, changedAttributes, ...args)`\n * @emits change:attribute Emitted for each changed attribute. Listeners receive `(model, newValue, ...args)`\n * @example\n * // Basic usage\n * model.set('name', 'Alice');\n * model.set({ name : 'Alice', age : 30 });\n * \n * // With options for listeners\n * model.set('name', 'Bob', { silent : false, validate : true });\n * model.on('change:name', (model, value, options) => {\n * if (options?.validate) {\n * // Custom validation logic\n * }\n * });\n */\n set(key, value, ...rest) {\n let attrs, args;\n // Handle both `\"key\", value` and `{key: value}` style arguments.\n if (typeof key === 'object') {\n attrs = key;\n args = [value, ...rest];\n } else {\n attrs = { [key] : value };\n args = rest;\n }\n // Are we in a nested `set` call?\n // Calling a `set` inside a `change:attribute` or `change` event listener\n const changing = this._changing;\n this._changing = true;\n // Store changed attributes.\n const changed = {};\n // Store previous attributes.\n if (!changing) {\n this.previous = Object.assign({}, this.attributes);\n }\n // Set attributes.\n Object.keys(attrs).forEach(key => {\n // Use equality to determine if value changed.\n if (attrs[key] !== this.attributes[key]) {\n changed[key] = attrs[key];\n this.attributes[key] = attrs[key];\n }\n });\n\n const changedKeys = Object.keys(changed);\n // Pending `change` event arguments.\n if (changedKeys.length) this._pending = ['change', this, changed, ...args];\n // Emit `change:attribute` events.\n changedKeys.forEach(key => {\n this.emit(`change:${key}`, this, attrs[key], ...args);\n });\n // Don't emit `change` event until the end of the nested \n // `set` calls inside `change:attribute` event listeners.\n if (changing) return this;\n // Emit `change` events, that might be nested.\n while (this._pending) {\n const pendingChange = this._pending;\n this._pending = null;\n this.emit.apply(this, pendingChange);\n }\n // Reset flags.\n this._pending = null;\n this._changing = false;\n\n return this;\n }\n\n /**\n * Transforms and validates data before it becomes model attributes.\n * Called during construction with all constructor arguments, allowing flexible data processing.\n * Override this method to transform incoming data, create nested models, or handle different data formats.\n * \n * @param {object} [data={}] Primary data object to be parsed into attributes\n * @param {...*} [args] Additional arguments from constructor, useful for parsing options\n * @return {object} Processed data that will become the model's attributes\n * @example\n * // Transform nested objects into models\n * class User extends Model {}\n * class Order extends Model {\n * parse(data, options = {}) {\n * // Skip parsing if requested\n * if (options.raw) return data;\n * // Transform user data into User model\n * const parsed = { ...data };\n * if (data.user && !(data.user instanceof User)) {\n * parsed.user = new User(data.user);\n * }\n * return parsed;\n * }\n * }\n * \n * // Usage with parsing options\n * const order1 = new Order({ id : 1, user : { name : 'Alice' } }); // user becomes User model\n * const order2 = new Order({ id : 2, user : { name : 'Bob' } }, { raw : true }); // user stays plain object\n */\n parse(data) {\n return data;\n }\n\n /**\n * Return object representation of the model to be used for JSON serialization.\n * By default returns a copy of `this.attributes`.\n * You can override this method to customize serialization behavior, such as calling `toJSON` recursively on nested Model instances.\n * @return {object} Object representation of the model to be used for JSON serialization.\n * @example\n * // Basic usage - returns a copy of model attributes:\n * const user = new Model({ name : 'Alice', age : 30 });\n * const json = user.toJSON();\n * console.log(json); // { name : 'Alice', age : 30 }\n * \n * // Override toJSON for recursive serialization of nested models:\n * class User extends Model {}\n * class Order extends Model {\n * parse(data) {\n * // Ensure user is always a User model\n * return { ...data, user : data.user instanceof User ? data.user : new User(data.user) };\n * }\n * \n * toJSON() {\n * const result = {};\n * for (const [key, value] of Object.entries(this.attributes)) {\n * if (value instanceof Model) {\n * result[key] = value.toJSON();\n * } else {\n * result[key] = value;\n * }\n * }\n * return result;\n * }\n * }\n * const order = new Order({ id : 1, user : { name : 'Alice' } });\n * const json = order.toJSON();\n * console.log(json); // { id : 1, user : { name : 'Alice' } }\n */\n toJSON() {\n return Object.assign({}, this.attributes);\n }\n}\n\n/**\n * Static property that defines a prefix for generated getters/setters.\n * When set, all attribute properties will be prefixed (e.g., 'attr_name' instead of 'name').\n * Useful for avoiding naming conflicts or creating a consistent property naming convention.\n * @type {string}\n * @default ''\n * @example\n * // Set prefix for all models of this class\n * class ApiModel extends Model {\n * static attributePrefix = 'attr_';\n * }\n * \n * const user = new ApiModel({ name : 'Alice', email : 'alice@example.com' });\n * console.log(user.attr_name); // 'Alice'\n * console.log(user.attr_email); // 'alice@example.com'\n * \n * // Still access via get/set methods without prefix\n * console.log(user.get('name')); // 'Alice'\n * user.set('name', 'Bob');\n * console.log(user.attr_name); // 'Bob'\n */\nModel.attributePrefix = '';\n"],"names":["getResult"],"mappings":";;;;;;;;;;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,MAAM,KAAK,SAAS,OAAO,CAAC;AAC3C,IAAI,WAAW,GAAG;AAClB,QAAQ,KAAK,EAAE;AACf;AACA,QAAQ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;AACjD;AACA,QAAQ,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAEA,eAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC9G;AACA,QAAQ,IAAI,CAAC,QAAQ,GAAG,EAAE;AAC1B;AACA,QAAQ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7E,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,aAAa,GAAG,CAAC;;AAErB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA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"} |
| 'use strict'; | ||
| var utils_repeat = require('./repeat.cjs'); | ||
| var utils_padEnd = require('./padEnd.cjs'); | ||
| /** | ||
| * The ASCII art cat. | ||
| * @type {string[]} | ||
| * @private | ||
| */ | ||
| const cat = [ | ||
| ' \\| ', | ||
| ' \\ ', | ||
| ' /\\___/\\ ', | ||
| ' ( o . o ) ', | ||
| ' > ︵ < ', | ||
| ' /| |\\ ', | ||
| ' (_| |_) ' | ||
| ]; | ||
| /** | ||
| * Creates a formatted error message with ASCII art (development version). | ||
| * @param {string} message The error message. Supports \n for multiple lines. | ||
| * @return {string} Formatted error message with ASCII art. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function createDevelopmentErrorMessage(message) { | ||
| const title = 'Something went wrong!'; | ||
| const minWidth = 40; | ||
| const lines = message.split('\n'); | ||
| const contentWidth = Math.max(minWidth, title.length, ...lines.map(line => line.length)); | ||
| const catPadding = Math.floor((contentWidth - cat[0].length) / 2); | ||
| const border = '+' + utils_padEnd('-', contentWidth + 2, '-') + '+'; | ||
| return [ | ||
| '', | ||
| ` ${border}`, | ||
| ` | ${utils_padEnd(title, contentWidth)} |`, | ||
| ` | ${utils_padEnd('', contentWidth)} |`, | ||
| ].concat(lines.map(line => ` | ${utils_padEnd(line, contentWidth)} |`)) | ||
| .concat([` ${border}`]) | ||
| .concat(cat.map(line => ` ${utils_repeat(' ', catPadding)} ${line}`)) | ||
| .concat(['']) | ||
| .join('\n'); | ||
| } | ||
| module.exports = createDevelopmentErrorMessage; | ||
| //# sourceMappingURL=createDevelopmentErrorMessage.cjs.map |
| {"version":3,"file":"createDevelopmentErrorMessage.cjs","sources":["../../src/utils/createDevelopmentErrorMessage.js"],"sourcesContent":["import repeat from './repeat.js';\nimport padEnd from './padEnd.js';\n\n/**\n * The ASCII art cat.\n * @type {string[]}\n * @private\n */\nconst cat = [\n ' \\\\| ',\n ' \\\\ ',\n ' /\\\\___/\\\\ ',\n ' ( o . o ) ',\n ' > ︵ < ',\n ' /| |\\\\ ',\n ' (_| |_) '\n];\n\n/**\n * Creates a formatted error message with ASCII art (development version).\n * @param {string} message The error message. Supports \\n for multiple lines.\n * @return {string} Formatted error message with ASCII art.\n * @module\n * @private\n */\nexport default function createDevelopmentErrorMessage(message) {\n const title = 'Something went wrong!';\n const minWidth = 40;\n const lines = message.split('\\n');\n const contentWidth = Math.max(minWidth, title.length, ...lines.map(line => line.length));\n const catPadding = Math.floor((contentWidth - cat[0].length) / 2);\n const border = '+' + padEnd('-', contentWidth + 2, '-') + '+';\n return [\n '',\n ` ${border}`,\n ` | ${padEnd(title, contentWidth)} |`,\n ` | ${padEnd('', contentWidth)} |`,\n ].concat(lines.map(line => ` | ${padEnd(line, contentWidth)} |`))\n .concat([` ${border}`])\n .concat(cat.map(line => ` ${repeat(' ', catPadding)} ${line}`))\n .concat([''])\n .join('\\n');\n}\n"],"names":["padEnd","repeat"],"mappings":";;;;;AAGA;AACA;AACA;AACA;AACA;AACA,MAAM,GAAG,GAAG;AACZ,IAAI,sBAAsB;AAC1B,IAAI,sBAAsB;AAC1B,IAAI,uBAAuB;AAC3B,IAAI,qBAAqB;AACzB,IAAI,qBAAqB;AACzB,IAAI,sBAAsB;AAC1B,IAAI;AACJ,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,6BAA6B,CAAC,OAAO,EAAE;AAC/D,IAAI,MAAM,KAAK,GAAG,uBAAuB;AACzC,IAAI,MAAM,QAAQ,GAAG,EAAE;AACvB,IAAI,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;AACrC,IAAI,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5F,IAAI,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;AACrE,IAAI,MAAM,MAAM,GAAG,GAAG,GAAGA,YAAM,CAAC,GAAG,EAAE,YAAY,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG;AACjE,IAAI,OAAO;AACX,QAAQ,EAAE;AACV,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;AACrB,QAAQ,CAAC,IAAI,EAAEA,YAAM,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;AAC9C,QAAQ,CAAC,IAAI,EAAEA,YAAM,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;AAC3C,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAEA,YAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;AACrE,SAAS,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;AAC/B,SAAS,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,EAAEC,YAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACvE,SAAS,MAAM,CAAC,CAAC,EAAE,CAAC;AACpB,SAAS,IAAI,CAAC,IAAI,CAAC;AACnB;;;;"} |
| 'use strict'; | ||
| /** | ||
| * Production version: returns the message as-is without formatting. | ||
| * @param {string} message The error message. | ||
| * @return {string} Plain error message. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function createProductionErrorMessage(message) { | ||
| return message; | ||
| } | ||
| module.exports = createProductionErrorMessage; | ||
| //# sourceMappingURL=createProductionErrorMessage.cjs.map |
| {"version":3,"file":"createProductionErrorMessage.cjs","sources":["../../src/utils/createProductionErrorMessage.js"],"sourcesContent":["/**\n * Production version: returns the message as-is without formatting.\n * @param {string} message The error message.\n * @return {string} Plain error message.\n * @module\n * @private\n */\nexport default function createProductionErrorMessage(message) {\n return message;\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,4BAA4B,CAAC,OAAO,EAAE;AAC9D,IAAI,OAAO,OAAO;AAClB;;;;"} |
| {"version":3,"file":"deepFlat.cjs","sources":["../../src/utils/deepFlat.js"],"sourcesContent":["/**\n * Flatten an array recursively.\n * @param {Array} arr Array to flat recursively\n * @return {Array} Flat array\n * @module\n * @private\n */\nconst deepFlat = (arr) => arr.reduce((acc, val) => {\n if (Array.isArray(val)) acc.push(...deepFlat(val));\n else acc.push(val);\n return acc;\n}, []);\n\nexport default deepFlat;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACK,MAAC,QAAQ,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK;AACnD,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;AACtD,SAAS,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AACtB,IAAI,OAAO,GAAG;AACd,CAAC,EAAE,EAAE;;;;"} |
| 'use strict'; | ||
| /** | ||
| * Development mode flag. | ||
| * This will be replaced during build: | ||
| * - ESM/CJS: replaced with process.env.NODE_ENV !== 'production' | ||
| * - UMD dev: replaced with true | ||
| * - UMD prod: replaced with false | ||
| * @type {boolean} | ||
| * @private | ||
| */ | ||
| const __DEV__ = process.env.NODE_ENV !== 'production'; | ||
| module.exports = __DEV__; | ||
| //# sourceMappingURL=dev.cjs.map |
| {"version":3,"file":"dev.cjs","sources":["../../src/utils/dev.js"],"sourcesContent":["/**\n * Development mode flag.\n * This will be replaced during build:\n * - ESM/CJS: replaced with process.env.NODE_ENV !== 'production'\n * - UMD dev: replaced with true\n * - UMD prod: replaced with false\n * @type {boolean}\n * @private\n */\nconst __DEV__ = true;\n\nexport default __DEV__;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAA,OAAA,GAAA,OAAA,CAAA,GAAA,CAAA,QAAA,KAAA;;;;"} |
| {"version":3,"file":"findComment.cjs","sources":["../../src/utils/findComment.js"],"sourcesContent":["/**\n * Finds the first comment node whose text matches exactly the given `text`.\n * Uses manual DOM traversal. Skips entire subtrees if `shouldSkip(element)` returns true.\n * Starts searching from `startNode` if provided, otherwise from `root.firstChild`.\n * @param {Node} root - Root node or fragment that limits the search scope.\n * @param {string} text - Exact comment text to match.\n * @param {Function} [shouldSkip] - Function that receives an element and returns true if its subtree should be skipped.\n * @param {Node} [startNode] - Node to start searching from (defaults to root.firstChild).\n * @return {Comment|null} The first matching comment node, or null if not found.\n * @module\n * @private\n */\nexport default function findComment(\n root,\n text,\n shouldSkip = () => false,\n startNode\n) {\n let node = startNode || root.firstChild;\n\n while (node) {\n // Check if current node is a comment with matching text.\n if (node.nodeType === Node.COMMENT_NODE && node.data.trim() === text) {\n return node;\n }\n // Descend into children if allowed and present.\n if (node.nodeType === Node.ELEMENT_NODE && !shouldSkip(node) && node.firstChild) {\n node = node.firstChild;\n continue;\n }\n // Move to next sibling, or climb up until a sibling is found.\n while (node && !node.nextSibling) {\n node = node.parentNode;\n if (!node || node === root) return null;\n }\n if (node) node = node.nextSibling;\n }\n\n return null;\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,WAAW;AACnC,IAAI,IAAI;AACR,IAAI,IAAI;AACR,IAAI,UAAU,GAAG,MAAM,KAAK;AAC5B,IAAI;AACJ,EAAE;AACF,IAAI,IAAI,IAAI,GAAG,SAAS,IAAI,IAAI,CAAC,UAAU;;AAE3C,IAAI,OAAO,IAAI,EAAE;AACjB;AACA,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;AAC9E,YAAY,OAAO,IAAI;AACvB,QAAQ;AACR;AACA,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE;AACzF,YAAY,IAAI,GAAG,IAAI,CAAC,UAAU;AAClC,YAAY;AACZ,QAAQ;AACR;AACA,QAAQ,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;AAC1C,YAAY,IAAI,GAAG,IAAI,CAAC,UAAU;AAClC,YAAY,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,OAAO,IAAI;AACnD,QAAQ;AACR,QAAQ,IAAI,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,WAAW;AACzC,IAAI;;AAEJ,IAAI,OAAO,IAAI;AACf;;;;"} |
| 'use strict'; | ||
| var utils_repeat = require('./repeat.cjs'); | ||
| var utils_padStart = require('./padStart.cjs'); | ||
| /** | ||
| * Converts an expression to its string representation for display. | ||
| * @param {any} expr The expression to convert. | ||
| * @return {string} String representation of the expression. | ||
| * @private | ||
| */ | ||
| function expressionToString(expr) { | ||
| if (typeof expr.toString === 'function') { | ||
| const source = expr.toString(); | ||
| // Keep single line or show first line for multi-line. | ||
| return '${' + (source.includes('\n') ? source.split('\n')[0] + '...' : source) + '}'; | ||
| } | ||
| return '${' + String(expr) + '}'; | ||
| } | ||
| /** | ||
| * Formats the template source with line numbers and highlights the specific error expression. | ||
| * @param {Object} source The original template source object with strings and expressions. | ||
| * @param {any} errorExpression The expression that caused the error. | ||
| * @return {string} Formatted template source. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function formatTemplateSource(source, errorExpression) { | ||
| if (!source || !source.strings || !source.expressions || !errorExpression) return ''; | ||
| const { strings, expressions } = source; | ||
| // Find the index of the error expression in the expressions array. | ||
| const expressionIndex = expressions.indexOf(errorExpression); | ||
| if (expressionIndex === -1) return ''; | ||
| // Build the template source and calculate the error position directly. | ||
| let templateSource = ''; | ||
| let errorPosition = -1; | ||
| for (let i = 0; i < strings.length; i++) { | ||
| templateSource += strings[i]; | ||
| if (i < expressions.length) { | ||
| // Mark the position before adding the error expression. | ||
| if (i === expressionIndex) { | ||
| errorPosition = templateSource.length; | ||
| } | ||
| templateSource += expressionToString(expressions[i]); | ||
| } | ||
| } | ||
| if (errorPosition === -1) return ''; | ||
| // Find the line and column of the error position. | ||
| const lines = templateSource.split('\n'); | ||
| const formattedLines = []; | ||
| const maxLineNumWidth = String(lines.length).length; | ||
| let errorLineNum = -1; | ||
| let errorStartCol = -1; | ||
| let charCount = 0; | ||
| for (let i = 0; i < lines.length; i++) { | ||
| const lineLength = lines[i].length + 1; // +1 for newline character. | ||
| if (charCount + lineLength > errorPosition) { | ||
| errorLineNum = i; | ||
| errorStartCol = errorPosition - charCount; | ||
| break; | ||
| } | ||
| charCount += lineLength; | ||
| } | ||
| if (errorLineNum === -1) return ''; | ||
| // Get the expression string to determine marker length. | ||
| const expressionStr = expressionToString(errorExpression); | ||
| // Format output with context. | ||
| const contextStart = Math.max(0, errorLineNum - 2); | ||
| const contextEnd = Math.min(lines.length - 1, errorLineNum + 2); | ||
| for (let i = contextStart; i <= contextEnd; i++) { | ||
| const lineNum = i + 1; | ||
| const lineNumStr = utils_padStart(String(lineNum), maxLineNumWidth, ' '); | ||
| const isErrorLine = i === errorLineNum; | ||
| const linePrefix = isErrorLine ? ` ${lineNumStr} > ` : ` ${lineNumStr} | `; | ||
| formattedLines.push(linePrefix + lines[i]); | ||
| // Add pointer on error line. | ||
| if (isErrorLine) { | ||
| const markerPos = linePrefix.length + errorStartCol; | ||
| const markerLen = expressionStr.length; | ||
| const pointerLine = utils_repeat(' ', markerPos) + utils_repeat('^', markerLen) + ' <-- Error here!'; | ||
| formattedLines.push(pointerLine); | ||
| } | ||
| } | ||
| // If error expression is multi-line, show full details. | ||
| if (typeof errorExpression === 'function') { | ||
| const fullSource = errorExpression.toString(); | ||
| if (fullSource.includes('\n')) { | ||
| formattedLines.push(''); | ||
| formattedLines.push(' | Expression details:'); | ||
| fullSource.split('\n').forEach(line => { | ||
| formattedLines.push(' | ' + line); | ||
| }); | ||
| } | ||
| } | ||
| return formattedLines.join('\n'); | ||
| } | ||
| module.exports = formatTemplateSource; | ||
| //# sourceMappingURL=formatTemplateSource.cjs.map |
| {"version":3,"file":"formatTemplateSource.cjs","sources":["../../src/utils/formatTemplateSource.js"],"sourcesContent":["import repeat from './repeat.js';\nimport padStart from './padStart.js';\n\n/**\n * Converts an expression to its string representation for display.\n * @param {any} expr The expression to convert.\n * @return {string} String representation of the expression.\n * @private\n */\nfunction expressionToString(expr) {\n if (typeof expr.toString === 'function') {\n const source = expr.toString();\n // Keep single line or show first line for multi-line.\n return '${' + (source.includes('\\n') ? source.split('\\n')[0] + '...' : source) + '}';\n }\n return '${' + String(expr) + '}';\n}\n\n/**\n * Formats the template source with line numbers and highlights the specific error expression.\n * @param {Object} source The original template source object with strings and expressions.\n * @param {any} errorExpression The expression that caused the error.\n * @return {string} Formatted template source.\n * @module\n * @private\n */\nexport default function formatTemplateSource(source, errorExpression) {\n if (!source || !source.strings || !source.expressions || !errorExpression) return '';\n\n const { strings, expressions } = source;\n\n // Find the index of the error expression in the expressions array.\n const expressionIndex = expressions.indexOf(errorExpression);\n if (expressionIndex === -1) return '';\n\n // Build the template source and calculate the error position directly.\n let templateSource = '';\n let errorPosition = -1;\n\n for (let i = 0; i < strings.length; i++) {\n templateSource += strings[i];\n\n if (i < expressions.length) {\n // Mark the position before adding the error expression.\n if (i === expressionIndex) {\n errorPosition = templateSource.length;\n }\n templateSource += expressionToString(expressions[i]);\n }\n }\n\n if (errorPosition === -1) return '';\n\n // Find the line and column of the error position.\n const lines = templateSource.split('\\n');\n const formattedLines = [];\n const maxLineNumWidth = String(lines.length).length;\n\n let errorLineNum = -1;\n let errorStartCol = -1;\n let charCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const lineLength = lines[i].length + 1; // +1 for newline character.\n if (charCount + lineLength > errorPosition) {\n errorLineNum = i;\n errorStartCol = errorPosition - charCount;\n break;\n }\n charCount += lineLength;\n }\n\n if (errorLineNum === -1) return '';\n\n // Get the expression string to determine marker length.\n const expressionStr = expressionToString(errorExpression);\n\n // Format output with context.\n const contextStart = Math.max(0, errorLineNum - 2);\n const contextEnd = Math.min(lines.length - 1, errorLineNum + 2);\n\n for (let i = contextStart; i <= contextEnd; i++) {\n const lineNum = i + 1;\n const lineNumStr = padStart(String(lineNum), maxLineNumWidth, ' ');\n const isErrorLine = i === errorLineNum;\n const linePrefix = isErrorLine ? ` ${lineNumStr} > ` : ` ${lineNumStr} | `;\n\n formattedLines.push(linePrefix + lines[i]);\n\n // Add pointer on error line.\n if (isErrorLine) {\n const markerPos = linePrefix.length + errorStartCol;\n const markerLen = expressionStr.length;\n const pointerLine = repeat(' ', markerPos) + repeat('^', markerLen) + ' <-- Error here!';\n formattedLines.push(pointerLine);\n }\n }\n\n // If error expression is multi-line, show full details.\n if (typeof errorExpression === 'function') {\n const fullSource = errorExpression.toString();\n if (fullSource.includes('\\n')) {\n formattedLines.push('');\n formattedLines.push(' | Expression details:');\n fullSource.split('\\n').forEach(line => {\n formattedLines.push(' | ' + line);\n });\n }\n }\n\n return formattedLines.join('\\n');\n}\n"],"names":["padStart","repeat"],"mappings":"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"} |
| {"version":3,"file":"getAttributesDiff.cjs","sources":["../../src/utils/getAttributesDiff.js"],"sourcesContent":["/**\n * Get difference between current and previous attributes.\n * @param {Object} attributes Current attributes object.\n * @param {Object} previous Previous attributes object.\n * @return {Object} Object with add and remove properties.\n * @module\n * @private\n */\nexport default function getAttributesDiff(attributes, previous = {}) {\n const add = {};\n const remove = [];\n // Find attributes to add/update.\n Object.keys(attributes).forEach(key => {\n let value = attributes[key];\n // Only add if the value is different from previous.\n if (value !== previous[key]) {\n if (value === true) {\n add[key] = '';\n } else if (value !== false) {\n if (value === null || typeof value === 'undefined') value = '';\n add[key] = value;\n }\n }\n });\n // Find attributes to remove.\n Object.keys(previous).forEach(key => {\n if (!(key in attributes) || ((previous[key] !== attributes[key]) && attributes[key] === false)) {\n remove.push(key);\n }\n });\n\n return { add, remove };\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,iBAAiB,CAAC,UAAU,EAAE,QAAQ,GAAG,EAAE,EAAE;AACrE,IAAI,MAAM,GAAG,GAAG,EAAE;AAClB,IAAI,MAAM,MAAM,GAAG,EAAE;AACrB;AACA,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AAC3C,QAAQ,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC;AACnC;AACA,QAAQ,IAAI,KAAK,KAAK,QAAQ,CAAC,GAAG,CAAC,EAAE;AACrC,YAAY,IAAI,KAAK,KAAK,IAAI,EAAE;AAChC,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE;AAC7B,YAAY,CAAC,MAAM,IAAI,KAAK,KAAK,KAAK,EAAE;AACxC,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE;AAC9E,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK;AAChC,YAAY;AACZ,QAAQ;AACR,IAAI,CAAC,CAAC;AACN;AACA,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AACzC,QAAQ,IAAI,EAAE,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,EAAE;AACxG,YAAY,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;AAC5B,QAAQ;AACR,IAAI,CAAC,CAAC;;AAEN,IAAI,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE;AAC1B;;;;"} |
| {"version":3,"file":"getAttributesHTML.cjs","sources":["../../src/utils/getAttributesHTML.js"],"sourcesContent":["/**\n * Generate HTML string from attributes object.\n * @param {Object} attributes Object containing attribute names and values.\n * @return {string} HTML string of attributes.\n * @module\n * @private\n */\nexport default function getAttributesHTML(attributes) {\n const html = [];\n\n Object.keys(attributes).forEach(key => {\n let value = attributes[key];\n\n if (value === true) {\n html.push(key);\n } else if (value !== false) {\n if (value === null || typeof value === 'undefined') value = '';\n html.push(`${key}=\"${value}\"`);\n }\n });\n\n return html.join(' ');\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,iBAAiB,CAAC,UAAU,EAAE;AACtD,IAAI,MAAM,IAAI,GAAG,EAAE;;AAEnB,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AAC3C,QAAQ,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC;;AAEnC,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE;AAC5B,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC1B,QAAQ,CAAC,MAAM,IAAI,KAAK,KAAK,KAAK,EAAE;AACpC,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,KAAK,GAAG,EAAE;AAC1E,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1C,QAAQ;AACR,IAAI,CAAC,CAAC;;AAEN,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACzB;;;;"} |
| {"version":3,"file":"getResult.cjs","sources":["../../src/utils/getResult.js"],"sourcesContent":["/**\n * Evaluate the expression. If it's a function, call it with the provided context and return the result.\n * Otherwise, return the expression as is.\n * @param {any} expression Expression to be evaluated.\n * @param {any} context Context to call the expression.\n * @param {...any} args Arguments to pass to the expression.\n * @return {any} The result of the expression.\n * @module\n * @private\n */\nconst getResult = (expression, context, ...args) =>\n typeof expression !== 'function' ? expression :\n expression.apply(context, args);\n\nexport default getResult;\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACK,MAAC,SAAS,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI;AAC/C,IAAI,OAAO,UAAU,KAAK,UAAU,GAAG,UAAU;AACjD,QAAQ,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI;;;;"} |
| 'use strict'; | ||
| var utils_repeat = require('./repeat.cjs'); | ||
| /** | ||
| * Pads a string to a given length with a given character. | ||
| * @param {string} string The string to pad. | ||
| * @param {number} length The length to pad the string to. | ||
| * @param {string} char The character to pad the string with. | ||
| * @return {string} The padded string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function padEnd(string, length, char = ' ') { | ||
| return string + utils_repeat(char, length - string.length); | ||
| } | ||
| module.exports = padEnd; | ||
| //# sourceMappingURL=padEnd.cjs.map |
| {"version":3,"file":"padEnd.cjs","sources":["../../src/utils/padEnd.js"],"sourcesContent":["import repeat from './repeat.js';\n\n/**\n * Pads a string to a given length with a given character.\n * @param {string} string The string to pad.\n * @param {number} length The length to pad the string to.\n * @param {string} char The character to pad the string with.\n * @return {string} The padded string.\n * @module\n * @private\n */\nexport default function padEnd(string, length, char = ' ') {\n return string + repeat(char, length - string.length);\n}\n"],"names":["repeat"],"mappings":";;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,GAAG,EAAE;AAC3D,IAAI,OAAO,MAAM,GAAGA,YAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACxD;;;;"} |
| 'use strict'; | ||
| var utils_repeat = require('./repeat.cjs'); | ||
| /** | ||
| * Pads a string to a given length with a given character at the start. | ||
| * @param {string} string The string to pad. | ||
| * @param {number} length The length to pad the string to. | ||
| * @param {string} char The character to pad the string with. | ||
| * @return {string} The padded string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function padStart(string, length, char = ' ') { | ||
| return utils_repeat(char, length - string.length) + string; | ||
| } | ||
| module.exports = padStart; | ||
| //# sourceMappingURL=padStart.cjs.map |
| {"version":3,"file":"padStart.cjs","sources":["../../src/utils/padStart.js"],"sourcesContent":["import repeat from './repeat.js';\n\n/**\n * Pads a string to a given length with a given character at the start.\n * @param {string} string The string to pad.\n * @param {number} length The length to pad the string to.\n * @param {string} char The character to pad the string with.\n * @return {string} The padded string.\n * @module\n * @private\n */\nexport default function padStart(string, length, char = ' ') {\n return repeat(char, length - string.length) + string;\n}\n"],"names":["repeat"],"mappings":";;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,GAAG,EAAE;AAC7D,IAAI,OAAOA,YAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM;AACxD;;;;"} |
| {"version":3,"file":"parseHTML.cjs","sources":["../../src/utils/parseHTML.js"],"sourcesContent":["/**\n * Parse HTML string to a DocumentFragment.\n * @param {string} html The HTML string to parse.\n * @return {DocumentFragment} The parsed DocumentFragment.\n * @module\n * @private\n */\nexport default function parseHTML(html) {\n const fragment = document.createElement('template');\n fragment.innerHTML = `${html}`.trim();\n return fragment.content;\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,SAAS,CAAC,IAAI,EAAE;AACxC,IAAI,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;AACvD,IAAI,QAAQ,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;AACzC,IAAI,OAAO,QAAQ,CAAC,OAAO;AAC3B;;;;"} |
| 'use strict'; | ||
| /** | ||
| * Repeats a string a given number of times. | ||
| * @param {string} string The string to repeat. | ||
| * @param {number} length The number of times to repeat the string. | ||
| * @param {string} acc Accumulator for recursion. | ||
| * @return {string} The repeated string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| function repeat(string, length, acc = '') { | ||
| return length > 0 ? repeat(string, length - 1, acc + string) : acc; | ||
| } | ||
| module.exports = repeat; | ||
| //# sourceMappingURL=repeat.cjs.map |
| {"version":3,"file":"repeat.cjs","sources":["../../src/utils/repeat.js"],"sourcesContent":["/**\n * Repeats a string a given number of times.\n * @param {string} string The string to repeat.\n * @param {number} length The number of times to repeat the string.\n * @param {string} acc Accumulator for recursion.\n * @return {string} The repeated string.\n * @module\n * @private\n */\nexport default function repeat(string, length, acc = '') {\n return length > 0 ? repeat(string, length - 1, acc + string) : acc;\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,EAAE;AACzD,IAAI,OAAO,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,GAAG,GAAG;AACtE;;;;"} |
| {"version":3,"file":"replaceNode.cjs","sources":["../../src/utils/replaceNode.js"],"sourcesContent":["/**\n * Replaces an existing DOM node with a new node, preserving internal DOM state.\n * Uses moveBefore if available, otherwise falls back to before.\n * \n * @param {Node} oldNode The existing DOM node to replace.\n * @param {Node} newNode The new DOM node to replace the old node with.\n * @module\n * @private\n */\nexport default function replaceNode(oldNode, newNode) {\n if (Element.prototype.moveBefore) {\n oldNode.parentNode.moveBefore(newNode, oldNode);\n oldNode.parentNode.removeChild(oldNode);\n } else {\n const activeElement = document.activeElement;\n oldNode.parentNode.insertBefore(newNode, oldNode);\n oldNode.parentNode.removeChild(oldNode);\n if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) {\n activeElement.focus();\n }\n }\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE;AACtD,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,UAAU,EAAE;AACtC,QAAQ,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC;AACvD,QAAQ,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC;AAC/C,IAAI,CAAC,MAAM;AACX,QAAQ,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa;AACpD,QAAQ,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC;AACzD,QAAQ,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC;AAC/C,QAAQ,IAAI,aAAa,IAAI,aAAa,KAAK,QAAQ,CAAC,aAAa,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE;AAC1G,YAAY,aAAa,CAAC,KAAK,EAAE;AACjC,QAAQ;AACR,IAAI;AACJ;;;;"} |
| {"version":3,"file":"syncNode.cjs","sources":["../../src/utils/syncNode.js"],"sourcesContent":["/**\n * Properties that should be mirrored from source to target.\n * @type {string[]}\n * @private\n */\nconst SYNC_PROPS = ['value', 'checked', 'selected'];\n\n/**\n * Compares two elements to see if their child nodes are structurally equal.\n * @param {Element} a First element to compare.\n * @param {Element} b Second element to compare.\n * @return {boolean} True if all child nodes are deeply equal.\n * @private\n */\nconst areChildNodesEqual = (a, b) => {\n const aChildren = a.childNodes;\n const bChildren = b.childNodes;\n const aLength = aChildren.length;\n\n if (aLength !== bChildren.length) return false;\n\n for (let i = 0; i < aLength; i++) {\n if (!aChildren[i].isEqualNode(bChildren[i])) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Synchronizes attributes and key DOM properties (value, checked, selected).\n * Assumes tagName is already the same.\n * @param {Element} targetEl Target DOM element to update.\n * @param {Element} sourceEl Source DOM element to copy attributes from.\n * @private\n */\nconst syncAttributes = (targetEl, sourceEl) => {\n // Add, update and remove attributes.\n const srcAttrs = sourceEl.attributes;\n const tgtAttrs = targetEl.attributes;\n const srcAttrNames = new Set();\n\n for (let i = 0, l = srcAttrs.length; i < l; i++) {\n const { name, value } = srcAttrs[i];\n srcAttrNames.add(name);\n if (targetEl.getAttribute(name) !== value) targetEl.setAttribute(name, value);\n }\n\n for (let i = tgtAttrs.length - 1; i >= 0; i--) {\n const { name } = tgtAttrs[i];\n if (!srcAttrNames.has(name)) targetEl.removeAttribute(name);\n }\n // Sync key DOM properties.\n for (let i = 0, l = SYNC_PROPS.length; i < l; i++) {\n const prop = SYNC_PROPS[i];\n if (prop in targetEl && targetEl[prop] !== sourceEl[prop]) {\n targetEl[prop] = sourceEl[prop];\n }\n }\n};\n\n/**\n * Replaces all child nodes of targetEl with those from sourceEl.\n * Moves the nodes without cloning.\n * @param {Element} targetEl The element whose children will be replaced.\n * @param {Element} sourceEl The element providing new children.\n * @module\n * @private\n */\nconst syncNodeContent = (targetEl, sourceEl) => {\n const children = Array.from(sourceEl.childNodes);\n targetEl.replaceChildren(...children);\n};\n\n/**\n * Synchronizes the contents of one DOM node to match another.\n * It performs a shallow sync: tag name, attributes, properties, and child nodes.\n * If tag names or node types differ, it replaces the node entirely.\n * @param {Node} targetNode The node currently in the DOM to be updated.\n * @param {Node} sourceNode The reference node to sync from (not in the DOM).\n * @private\n */\nexport default function syncNode(targetNode, sourceNode) {\n // Replace if node types are different (e.g. element vs text).\n if (targetNode.nodeType !== sourceNode.nodeType) {\n targetNode.replaceWith(sourceNode);\n return;\n }\n // Text node: sync value.\n if (targetNode.nodeType === Node.TEXT_NODE) {\n if (targetNode.nodeValue !== sourceNode.nodeValue) {\n targetNode.nodeValue = sourceNode.nodeValue;\n }\n return;\n }\n // Element node: check tag.\n if (targetNode.tagName !== sourceNode.tagName) {\n targetNode.replaceWith(sourceNode);\n return;\n }\n // Sync attributes and DOM properties.\n syncAttributes(targetNode, sourceNode);\n // Sync child nodes if necessary.\n if (!areChildNodesEqual(targetNode, sourceNode)) {\n syncNodeContent(targetNode, sourceNode);\n }\n}\n"],"names":[],"mappings":"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"} |
| {"version":3,"file":"validateListener.cjs","sources":["../../src/utils/validateListener.js"],"sourcesContent":["import createDevelopmentErrorMessage from './createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './createProductionErrorMessage.js';\nimport __DEV__ from './dev.js';\n\n/**\n * Validates that the listener is a function.\n * @param {Function} listener The listener to validate.\n * @throws {TypeError} If the listener is not a function.\n * @module\n * @private\n */\nexport default function validateListener(listener) {\n if (typeof listener !== 'function') {\n const message = 'Event listener must be a function';\n throw new TypeError(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message));\n }\n}\n"],"names":["__DEV__","createDevelopmentErrorMessage","createProductionErrorMessage"],"mappings":";;;;;;;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,gBAAgB,CAAC,QAAQ,EAAE;AACnD,IAAI,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;AACxC,QAAQ,MAAM,OAAO,GAAG,mCAAmC;AAC3D,QAAQ,MAAM,IAAI,SAAS,CAACA,SAAO,GAAGC,mCAA6B,CAAC,OAAO,CAAC,GAAGC,kCAA4B,CAAC,OAAO,CAAC,CAAC;AACrH,IAAI;AACJ;;;;"} |
| {"version":3,"file":"View.cjs","sources":["../src/View.js"],"sourcesContent":["import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\nimport validateListener from './utils/validateListener.js';\n\n/*\n * These option keys will be extended on the view instance.\n */\nconst viewOptions = ['el', 'tag', 'attributes', 'events', 'model', 'template', 'onDestroy'];\n\n/**\n * - Listens for changes and renders the UI.\n * - Handles user input and interactivity.\n * - Sends captured input to the model.\n *\n * A `View` is an atomic unit of the user interface that can render data from a specific model or multiple models.\n * However, views can also be independent and have no associated data. \n * Models must be unaware of views. Views, on the other hand, may render model data and listen to the change events \n * emitted by the models to re-render themselves based on changes. \n * Each `View` has a root element, `this.el`, which is used for event delegation. \n * All element lookups are scoped to this element, and any rendering or DOM manipulations should be done inside it. \n * If `this.el` is not present, an element will be created using `this.tag` (defaulting to `div`) and `this.attributes`.\n * @module\n * @extends Emitter\n * @param {object} options Object containing options. The following keys will be merged into the view instance: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.\n * @property {node|Function} el Every view has a root DOM element stored at `this.el`. If not present, it will be created. If `this.el` is a function, it will be called to get the element at `this.ensureElement`, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {string|Function} tag If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. Default is `div`. If it is a function, it will be called to get the tag, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} attributes If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. If it is a function, it will be called to get the attributes object, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.\n * @property {object} model A model or any object containing data and business logic.\n * @property {Function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}. \n * @property {number} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.\n * @example\n * import { View, Model } from 'rasti';\n * \n * class Timer extends View {\n * constructor(options) {\n * super(options);\n * // Create model to store internal state. Set `seconds` attribute to 0.\n * this.model = new Model({ seconds : 0 });\n * // Listen to changes in model `seconds` attribute and re-render.\n * this.model.on('change:seconds', this.render.bind(this));\n * // Increment model `seconds` attribute every 1000 milliseconds.\n * this.interval = setInterval(() => this.model.seconds++, 1000);\n * }\n *\n * template(model) {\n * return `Seconds: <span>${model.seconds}</span>`;\n * }\n * }\n * // Render view and append view's element into the body.\n * document.body.appendChild(new Timer().render().el);\n */\nexport default class View extends Emitter {\n constructor(options = {}) {\n super();\n // Call preinitialize.\n this.preinitialize.apply(this, arguments);\n // Store delegated event listeners,\n // so they can be unbound later.\n this.delegatedEventListeners = [];\n // Store child views,\n // so they can be destroyed.\n this.children = [];\n // Mutable array to store handlers to be called on destroy.\n this.destroyQueue = [];\n this.viewOptions = [];\n // Extend \"this\" with options.\n viewOptions.forEach(key => {\n if (key in options) {\n this[key] = options[key];\n this.viewOptions.push(key);\n }\n });\n // Ensure that the view has a unique id at `this.uid`.\n this.ensureUid();\n // Ensure that the view has a root element at `this.el`.\n this.ensureElement();\n }\n\n /**\n * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.\n * @param {object} options The view options.\n */\n preinitialize() {}\n\n /**\n * Returns the first element that matches the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node} Element matching selector within the view's root element (`this.el`).\n */\n $(selector) {\n return this.el.querySelector(selector);\n }\n\n /**\n * Returns a list of elements that match the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node[]} List of elements matching selector within the view's root element (`this.el`).\n */\n $$(selector) {\n return this.el.querySelectorAll(selector);\n }\n\n /**\n * Destroy the view.\n * Destroy children views if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.\n * @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.\n * @return {Rasti.View} Return `this` for chaining.\n */\n destroy() {\n // Call destroy on children.\n this.destroyChildren();\n // Undelegate `this.el` event listeners\n this.undelegateEvents();\n // Stop listening to events.\n this.stopListening();\n // Unbind `this` events.\n this.off();\n // Call destroy queue.\n this.destroyQueue.forEach(fn => fn());\n this.destroyQueue = [];\n // Call onDestroy lifecycle method\n this.onDestroy.apply(this, arguments);\n // Set destroyed flag.\n this.destroyed = true;\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * `onDestroy` lifecycle method is called after the view is destroyed.\n * Override with your code. Useful to stop listening to model's events.\n * @param {object} options Options object or any arguments passed to `destroy` method.\n */\n onDestroy() {}\n\n /**\n * Add a view as a child.\n * Children views are stored at `this.children`, and destroyed when the parent is destroyed.\n * Returns the child for chaining.\n * @param {Rasti.View} child\n * @return {Rasti.View}\n */\n addChild(child) {\n this.children.push(child);\n return child;\n }\n\n /**\n * Call destroy method on children views.\n */\n destroyChildren() {\n this.children.forEach(child => child.destroy());\n this.children = [];\n }\n\n /**\n * Ensure that the view has a unique id at `this.uid`.\n */\n ensureUid() {\n if (!this.uid) this.uid = `r${++View.uid}`;\n }\n\n /**\n * Ensure that the view has a root element at `this.el`.\n * You shouldn't call this method directly. It's called from the constructor.\n * You may override it if you want to use a different logic or to \n * postpone element creation.\n */\n ensureElement() {\n // Element is already present.\n if (this.el) {\n // If \"this.el\" is a function, call it to get the element.\n this.el = getResult(this.el, this);\n } else {\n // If \"this.el\" is not present,\n // create a new element according \"this.tag\"\n // and \"this.attributes\".\n const tag = getResult(this.tag, this);\n const attrs = getResult(this.attributes, this);\n this.el = this.createElement(tag, attrs);\n }\n // Delegate events on element.\n this.delegateEvents();\n }\n\n /**\n * Create an element.\n * Called from the constructor if `this.el` is undefined, to ensure\n * the view has a root element.\n * @param {string} tag Tag for the element. Default to `div`\n * @param {object} attributes Attributes for the element.\n * @return {node} The created element.\n */\n createElement(tag = 'div', attributes = {}) {\n // Create DOM element.\n let el = document.createElement(tag);\n // Add element attributes.\n Object.keys(attributes)\n .forEach(key => el.setAttribute(key, attributes[key]));\n\n return el;\n }\n\n /**\n * Remove `this.el` from the DOM.\n * @return {Rasti.View} Return `this` for chaining.\n */\n removeElement() {\n this.el.parentNode.removeChild(this.el);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Provide declarative listeners for DOM events within a view. If an events object is not provided, \n * it defaults to using `this.events`. If `this.events` is a function, it will be called to get the events object.\n * \n * The events object should follow the format `{'event selector': 'listener'}`:\n * - `event`: The type of event (e.g., 'click').\n * - `selector`: A CSS selector to match the event target. If omitted, the event is bound to the root element.\n * - `listener`: A function or a string representing a method name on the view. The method will be called with `this` bound to the view instance.\n * \n * By default, `delegateEvents` is called within the View's constructor. If you have a simple events object, \n * all of your DOM events will be connected automatically, and you will not need to call this function manually.\n * \n * All attached listeners are bound to the view, ensuring that `this` refers to the view object when the listeners are invoked.\n * When `delegateEvents` is called again, possibly with a different events object, all previous listeners are removed and delegated afresh.\n * \n * **Listener signature:** `(event, view, matched)`\n * - `event`: The native DOM event object.\n * - `view`: The current view instance (`this`).\n * - `matched`: The element that satisfies the selector. If no selector is provided, it will be the view's root element (`this.el`).\n *\n * If more than one ancestor between `event.target` and the view's root element matches the selector, the listener will be\n * invoked **once for each matched element** (from inner to outer).\n *\n * @param {object} [events] Object in the format `{'event selector' : 'listener'}`. Used to bind delegated event listeners to the root element.\n * @return {Rasti.View} Returns `this` for chaining.\n * @example\n * // Using prototype (recommended for static events)\n * class Modal extends View {\n * onClickOk(event, view, matched) {\n * // matched === the button.ok element that was clicked\n * this.close();\n * }\n * \n * onClickCancel() {\n * this.destroy();\n * }\n * }\n * Modal.prototype.events = {\n * 'click button.ok': 'onClickOk',\n * 'click button.cancel': 'onClickCancel',\n * 'submit form': 'onSubmit'\n * };\n * \n * // Using a function for dynamic events\n * class DynamicView extends View {\n * events() {\n * return {\n * [`click .${this.model.buttonClass}`]: 'onButtonClick',\n * 'click': 'onRootClick'\n * };\n * }\n * }\n */\n delegateEvents(events) {\n if (!events) events = getResult(this.events, this);\n if (!events) return this;\n\n if (this.delegatedEventListeners.length) this.undelegateEvents();\n\n // Store events by type i.e.: \"click\", \"submit\", etc.\n let eventTypes = {};\n\n Object.keys(events).forEach(key => {\n const keyParts = key.split(' ');\n const type = keyParts.shift();\n const selector = keyParts.join(' ');\n\n let listener = events[key];\n // Listener may be a string representing a method name on the view, or a function.\n if (typeof listener === 'string') listener = this[listener];\n // Validate listener is a function.\n validateListener(listener);\n\n if (!eventTypes[type]) eventTypes[type] = [];\n\n eventTypes[type].push({ selector, listener });\n });\n\n Object.keys(eventTypes).forEach(type => {\n // Listener for the type of event.\n const typeListener = (event) => {\n // Iterate and run every individual listener if the selector matches.\n eventTypes[type].forEach(({ selector, listener }) => {\n // No selector provided: invoke listener once with root element.\n if (!selector) {\n listener.call(this, event, this, this.el);\n return;\n }\n\n let node = event.target;\n // Traverse ancestors until reaching the view root (`this.el`).\n while (node && node !== this.el) {\n if (node.matches && node.matches(selector)) {\n listener.call(this, event, this, node);\n }\n node = node.parentElement;\n }\n });\n };\n\n this.delegatedEventListeners.push({ type, listener : typeListener });\n this.el.addEventListener(type, typeListener);\n });\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Removes all of the view's delegated events. \n * Useful if you want to disable or remove a view from the DOM temporarily. \n * Called automatically when the view is destroyed and when `delegateEvents` is called again.\n * @return {Rasti.View} Return `this` for chaining.\n */\n undelegateEvents() {\n this.delegatedEventListeners.forEach(({ type, listener }) => {\n this.el.removeEventListener(type, listener);\n });\n\n this.delegatedEventListeners = [];\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Renders the view. \n * This method should be overridden with custom logic.\n * The only convention is to manipulate the DOM within the scope of `this.el`,\n * and to return `this` for chaining. \n * If you add any child views, you should call `this.destroyChildren` before re-rendering. \n * The default implementation updates `this.el`'s innerHTML with the result\n * of calling `this.template`, passing `this.model` as the argument.\n * <br><br> ⚠ **Security Notice:** The default implementation utilizes `innerHTML`, which may introduce Cross-Site Scripting (XSS) risks. \n * Ensure that any user-generated content is properly sanitized before inserting it into the DOM. \n * You can use the {@link #module_view_sanitize View.sanitize} static method to escape HTML entities in a string. \n * For best practices on secure data handling, refer to the \n * [OWASP's XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).<br><br>\n * @return {Rasti.View} Returns `this` for chaining.\n */\n render() {\n if (this.template) this.el.innerHTML = this.template(this.model);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Escape HTML entities in a string.\n * Use this method to sanitize user-generated content before inserting it into the DOM.\n * Override this method to provide a custom escape function.\n * This method is inherited by {@link #module_component Component} and used to escape template interpolations.\n * @static\n * @param {string} value String to escape.\n * @return {string} Escaped string.\n */\n static sanitize(value) {\n return `${value}`.replace(/[&<>\"']/g, match => ({\n '&' : '&',\n '<' : '<',\n '>' : '>',\n '\"' : '"',\n '\\'' : '''\n }[match]));\n }\n}\n\n/**\n * Counter for generating unique IDs for view instances. \n * This is primarily used to assign unique identifiers to each view instance (`this.uid`), which can be helpful for tasks like \n * generating element IDs. \n * {@link #module_component Component}s use `this.uid` to generate data attributes for their elements, to be looked up on hydration. \n * For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated \n * unique IDs match those on the client, enabling seamless hydration of components. \n * @static\n * @type {number}\n * @default 0\n */\nView.uid = 0;\n"],"names":["getResult","validateListener"],"mappings":";;;;;;;;;;;AAIA;AACA;AACA;AACA,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC;;AAE3F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,MAAM,IAAI,SAAS,OAAO,CAAC;AAC1C,IAAI,WAAW,CAAC,OAAO,GAAG,EAAE,EAAE;AAC9B,QAAQ,KAAK,EAAE;AACf;AACA,QAAQ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;AACjD;AACA;AACA,QAAQ,IAAI,CAAC,uBAAuB,GAAG,EAAE;AACzC;AACA;AACA,QAAQ,IAAI,CAAC,QAAQ,GAAG,EAAE;AAC1B;AACA,QAAQ,IAAI,CAAC,YAAY,GAAG,EAAE;AAC9B,QAAQ,IAAI,CAAC,WAAW,GAAG,EAAE;AAC7B;AACA,QAAQ,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;AACnC,YAAY,IAAI,GAAG,IAAI,OAAO,EAAE;AAChC,gBAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;AACxC,gBAAgB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;AAC1C,YAAY;AACZ,QAAQ,CAAC,CAAC;AACV;AACA,QAAQ,IAAI,CAAC,SAAS,EAAE;AACxB;AACA,QAAQ,IAAI,CAAC,aAAa,EAAE;AAC5B,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,aAAa,GAAG,CAAC;;AAErB;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,CAAC,CAAC,QAAQ,EAAE;AAChB,QAAQ,OAAO,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC;AAC9C,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,EAAE,CAAC,QAAQ,EAAE;AACjB,QAAQ,OAAO,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC;AACjD,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,OAAO,GAAG;AACd;AACA,QAAQ,IAAI,CAAC,eAAe,EAAE;AAC9B;AACA,QAAQ,IAAI,CAAC,gBAAgB,EAAE;AAC/B;AACA,QAAQ,IAAI,CAAC,aAAa,EAAE;AAC5B;AACA,QAAQ,IAAI,CAAC,GAAG,EAAE;AAClB;AACA,QAAQ,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;AAC7C,QAAQ,IAAI,CAAC,YAAY,GAAG,EAAE;AAC9B;AACA,QAAQ,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;AAC7C;AACA,QAAQ,IAAI,CAAC,SAAS,GAAG,IAAI;AAC7B;AACA,QAAQ,OAAO,IAAI;AACnB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,GAAG,CAAC;;AAEjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,QAAQ,CAAC,KAAK,EAAE;AACpB,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;AACjC,QAAQ,OAAO,KAAK;AACpB,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,eAAe,GAAG;AACtB,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;AACvD,QAAQ,IAAI,CAAC,QAAQ,GAAG,EAAE;AAC1B,IAAI;;AAEJ;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,aAAa,GAAG;AACpB;AACA,QAAQ,IAAI,IAAI,CAAC,EAAE,EAAE;AACrB;AACA,YAAY,IAAI,CAAC,EAAE,GAAGA,eAAS,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC;AAC9C,QAAQ,CAAC,MAAM;AACf;AACA;AACA;AACA,YAAY,MAAM,GAAG,GAAGA,eAAS,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;AACjD,YAAY,MAAM,KAAK,GAAGA,eAAS,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC;AAC1D,YAAY,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC;AACpD,QAAQ;AACR;AACA,QAAQ,IAAI,CAAC,cAAc,EAAE;AAC7B,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,aAAa,CAAC,GAAG,GAAG,KAAK,EAAE,UAAU,GAAG,EAAE,EAAE;AAChD;AACA,QAAQ,IAAI,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC;AAC5C;AACA,QAAQ,MAAM,CAAC,IAAI,CAAC,UAAU;AAC9B,aAAa,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;;AAElE,QAAQ,OAAO,EAAE;AACjB,IAAI;;AAEJ;AACA;AACA;AACA;AACA,IAAI,aAAa,GAAG;AACpB,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;AAC/C;AACA,QAAQ,OAAO,IAAI;AACnB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,cAAc,CAAC,MAAM,EAAE;AAC3B,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,GAAGA,eAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC1D,QAAQ,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI;;AAEhC,QAAQ,IAAI,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE;;AAExE;AACA,QAAQ,IAAI,UAAU,GAAG,EAAE;;AAE3B,QAAQ,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI;AAC3C,YAAY,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;AAC3C,YAAY,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE;AACzC,YAAY,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;;AAE/C,YAAY,IAAI,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC;AACtC;AACA,YAAY,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;AACvE;AACA,YAAYC,sBAAgB,CAAC,QAAQ,CAAC;;AAEtC,YAAY,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE;;AAExD,YAAY,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACzD,QAAQ,CAAC,CAAC;;AAEV,QAAQ,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI;AAChD;AACA,YAAY,MAAM,YAAY,GAAG,CAAC,KAAK,KAAK;AAC5C;AACA,gBAAgB,UAAU,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK;AACrE;AACA,oBAAoB,IAAI,CAAC,QAAQ,EAAE;AACnC,wBAAwB,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;AACjE,wBAAwB;AACxB,oBAAoB;;AAEpB,oBAAoB,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM;AAC3C;AACA,oBAAoB,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,EAAE,EAAE;AACrD,wBAAwB,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AACpE,4BAA4B,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;AAClE,wBAAwB;AACxB,wBAAwB,IAAI,GAAG,IAAI,CAAC,aAAa;AACjD,oBAAoB;AACpB,gBAAgB,CAAC,CAAC;AAClB,YAAY,CAAC;;AAEb,YAAY,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,YAAY,EAAE,CAAC;AAChF,YAAY,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,YAAY,CAAC;AACxD,QAAQ,CAAC,CAAC;AACV;AACA,QAAQ,OAAO,IAAI;AACnB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,gBAAgB,GAAG;AACvB,QAAQ,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK;AACrE,YAAY,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC;AACvD,QAAQ,CAAC,CAAC;;AAEV,QAAQ,IAAI,CAAC,uBAAuB,GAAG,EAAE;AACzC;AACA,QAAQ,OAAO,IAAI;AACnB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,GAAG;AACb,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;AACxE;AACA,QAAQ,OAAO,IAAI;AACnB,IAAI;;AAEJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,OAAO,QAAQ,CAAC,KAAK,EAAE;AAC3B,QAAQ,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,KAAK;AACxD,YAAY,GAAG,GAAG,OAAO;AACzB,YAAY,GAAG,GAAG,MAAM;AACxB,YAAY,GAAG,GAAG,MAAM;AACxB,YAAY,GAAG,GAAG,QAAQ;AAC1B,YAAY,IAAI,GAAG;AACnB,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAClB,IAAI;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,CAAC,GAAG,GAAG,CAAC;;;;"} |
| import repeat from './repeat.js'; | ||
| import padEnd from './padEnd.js'; | ||
| /** | ||
| * The ASCII art cat. | ||
| * @type {string[]} | ||
| * @private | ||
| */ | ||
| const cat = [ | ||
| ' \\| ', | ||
| ' \\ ', | ||
| ' /\\___/\\ ', | ||
| ' ( o . o ) ', | ||
| ' > ︵ < ', | ||
| ' /| |\\ ', | ||
| ' (_| |_) ' | ||
| ]; | ||
| /** | ||
| * Creates a formatted error message with ASCII art (development version). | ||
| * @param {string} message The error message. Supports \n for multiple lines. | ||
| * @return {string} Formatted error message with ASCII art. | ||
| * @module | ||
| * @private | ||
| */ | ||
| export default function createDevelopmentErrorMessage(message) { | ||
| const title = 'Something went wrong!'; | ||
| const minWidth = 40; | ||
| const lines = message.split('\n'); | ||
| const contentWidth = Math.max(minWidth, title.length, ...lines.map(line => line.length)); | ||
| const catPadding = Math.floor((contentWidth - cat[0].length) / 2); | ||
| const border = '+' + padEnd('-', contentWidth + 2, '-') + '+'; | ||
| return [ | ||
| '', | ||
| ` ${border}`, | ||
| ` | ${padEnd(title, contentWidth)} |`, | ||
| ` | ${padEnd('', contentWidth)} |`, | ||
| ].concat(lines.map(line => ` | ${padEnd(line, contentWidth)} |`)) | ||
| .concat([` ${border}`]) | ||
| .concat(cat.map(line => ` ${repeat(' ', catPadding)} ${line}`)) | ||
| .concat(['']) | ||
| .join('\n'); | ||
| } |
| /** | ||
| * Production version: returns the message as-is without formatting. | ||
| * @param {string} message The error message. | ||
| * @return {string} Plain error message. | ||
| * @module | ||
| * @private | ||
| */ | ||
| export default function createProductionErrorMessage(message) { | ||
| return message; | ||
| } |
| /** | ||
| * Development mode flag. | ||
| * This will be replaced during build: | ||
| * - ESM/CJS: replaced with process.env.NODE_ENV !== 'production' | ||
| * - UMD dev: replaced with true | ||
| * - UMD prod: replaced with false | ||
| * @type {boolean} | ||
| * @private | ||
| */ | ||
| const __DEV__ = true; | ||
| export default __DEV__; |
| import repeat from './repeat.js'; | ||
| import padStart from './padStart.js'; | ||
| /** | ||
| * Converts an expression to its string representation for display. | ||
| * @param {any} expr The expression to convert. | ||
| * @return {string} String representation of the expression. | ||
| * @private | ||
| */ | ||
| function expressionToString(expr) { | ||
| if (typeof expr.toString === 'function') { | ||
| const source = expr.toString(); | ||
| // Keep single line or show first line for multi-line. | ||
| return '${' + (source.includes('\n') ? source.split('\n')[0] + '...' : source) + '}'; | ||
| } | ||
| return '${' + String(expr) + '}'; | ||
| } | ||
| /** | ||
| * Formats the template source with line numbers and highlights the specific error expression. | ||
| * @param {Object} source The original template source object with strings and expressions. | ||
| * @param {any} errorExpression The expression that caused the error. | ||
| * @return {string} Formatted template source. | ||
| * @module | ||
| * @private | ||
| */ | ||
| export default function formatTemplateSource(source, errorExpression) { | ||
| if (!source || !source.strings || !source.expressions || !errorExpression) return ''; | ||
| const { strings, expressions } = source; | ||
| // Find the index of the error expression in the expressions array. | ||
| const expressionIndex = expressions.indexOf(errorExpression); | ||
| if (expressionIndex === -1) return ''; | ||
| // Build the template source and calculate the error position directly. | ||
| let templateSource = ''; | ||
| let errorPosition = -1; | ||
| for (let i = 0; i < strings.length; i++) { | ||
| templateSource += strings[i]; | ||
| if (i < expressions.length) { | ||
| // Mark the position before adding the error expression. | ||
| if (i === expressionIndex) { | ||
| errorPosition = templateSource.length; | ||
| } | ||
| templateSource += expressionToString(expressions[i]); | ||
| } | ||
| } | ||
| if (errorPosition === -1) return ''; | ||
| // Find the line and column of the error position. | ||
| const lines = templateSource.split('\n'); | ||
| const formattedLines = []; | ||
| const maxLineNumWidth = String(lines.length).length; | ||
| let errorLineNum = -1; | ||
| let errorStartCol = -1; | ||
| let charCount = 0; | ||
| for (let i = 0; i < lines.length; i++) { | ||
| const lineLength = lines[i].length + 1; // +1 for newline character. | ||
| if (charCount + lineLength > errorPosition) { | ||
| errorLineNum = i; | ||
| errorStartCol = errorPosition - charCount; | ||
| break; | ||
| } | ||
| charCount += lineLength; | ||
| } | ||
| if (errorLineNum === -1) return ''; | ||
| // Get the expression string to determine marker length. | ||
| const expressionStr = expressionToString(errorExpression); | ||
| // Format output with context. | ||
| const contextStart = Math.max(0, errorLineNum - 2); | ||
| const contextEnd = Math.min(lines.length - 1, errorLineNum + 2); | ||
| for (let i = contextStart; i <= contextEnd; i++) { | ||
| const lineNum = i + 1; | ||
| const lineNumStr = padStart(String(lineNum), maxLineNumWidth, ' '); | ||
| const isErrorLine = i === errorLineNum; | ||
| const linePrefix = isErrorLine ? ` ${lineNumStr} > ` : ` ${lineNumStr} | `; | ||
| formattedLines.push(linePrefix + lines[i]); | ||
| // Add pointer on error line. | ||
| if (isErrorLine) { | ||
| const markerPos = linePrefix.length + errorStartCol; | ||
| const markerLen = expressionStr.length; | ||
| const pointerLine = repeat(' ', markerPos) + repeat('^', markerLen) + ' <-- Error here!'; | ||
| formattedLines.push(pointerLine); | ||
| } | ||
| } | ||
| // If error expression is multi-line, show full details. | ||
| if (typeof errorExpression === 'function') { | ||
| const fullSource = errorExpression.toString(); | ||
| if (fullSource.includes('\n')) { | ||
| formattedLines.push(''); | ||
| formattedLines.push(' | Expression details:'); | ||
| fullSource.split('\n').forEach(line => { | ||
| formattedLines.push(' | ' + line); | ||
| }); | ||
| } | ||
| } | ||
| return formattedLines.join('\n'); | ||
| } |
| import repeat from './repeat.js'; | ||
| /** | ||
| * Pads a string to a given length with a given character. | ||
| * @param {string} string The string to pad. | ||
| * @param {number} length The length to pad the string to. | ||
| * @param {string} char The character to pad the string with. | ||
| * @return {string} The padded string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| export default function padEnd(string, length, char = ' ') { | ||
| return string + repeat(char, length - string.length); | ||
| } |
| import repeat from './repeat.js'; | ||
| /** | ||
| * Pads a string to a given length with a given character at the start. | ||
| * @param {string} string The string to pad. | ||
| * @param {number} length The length to pad the string to. | ||
| * @param {string} char The character to pad the string with. | ||
| * @return {string} The padded string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| export default function padStart(string, length, char = ' ') { | ||
| return repeat(char, length - string.length) + string; | ||
| } |
| /** | ||
| * Repeats a string a given number of times. | ||
| * @param {string} string The string to repeat. | ||
| * @param {number} length The number of times to repeat the string. | ||
| * @param {string} acc Accumulator for recursion. | ||
| * @return {string} The repeated string. | ||
| * @module | ||
| * @private | ||
| */ | ||
| export default function repeat(string, length, acc = '') { | ||
| return length > 0 ? repeat(string, length - 1, acc + string) : acc; | ||
| } |
@@ -1,1 +0,2 @@ | ||
| !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Rasti={})}(this,(function(t){"use strict";function e(t){if("function"!=typeof t)throw new TypeError("Listener must be a function")}class s{on(t,s){return e(s),this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(s),()=>this.off(t,s)}once(t,s){e(s);const i=(...e)=>{s(...e),this.off(t,i)};return this.on(t,i)}off(t,e){this.listeners&&(t?this.listeners[t]&&(e?(this.listeners[t]=this.listeners[t].filter((t=>t!==e)),this.listeners[t].length||delete this.listeners[t]):delete this.listeners[t],Object.keys(this.listeners).length||delete this.listeners):delete this.listeners)}emit(t,...e){this.listeners&&this.listeners[t]&&this.listeners[t].slice().forEach((t=>t(...e)))}listenTo(t,e,s){return t.on(e,s),this.listeningTo||(this.listeningTo=[]),this.listeningTo.push({emitter:t,type:e,listener:s}),()=>this.stopListening(t,e,s)}listenToOnce(t,s,i){e(i);const n=(...e)=>{i(...e),this.stopListening(t,s,n)};return this.listenTo(t,s,n)}stopListening(t,e,s){this.listeningTo&&(this.listeningTo=this.listeningTo.filter((i=>!(!t||t===i.emitter&&!e||t===i.emitter&&e===i.type&&!s||t===i.emitter&&e===i.type&&s===i.listener)||(i.emitter.off(i.type,i.listener),!1))),this.listeningTo.length||delete this.listeningTo)}}const i=(t,e,...s)=>"function"!=typeof t?t:t.apply(e,s);class n extends s{constructor(){super(),this.preinitialize.apply(this,arguments),this.attributes=Object.assign({},i(this.defaults,this),this.parse.apply(this,arguments)),this.previous={},Object.keys(this.attributes).forEach(this.defineAttribute.bind(this))}preinitialize(){}defineAttribute(t){Object.defineProperty(this,`${this.constructor.attributePrefix}${t}`,{get:()=>this.get(t),set:e=>{this.set(t,e)}})}get(t){return this.attributes[t]}set(t,e,...s){let i,n;"object"==typeof t?(i=t,n=[e,...s]):(i={[t]:e},n=s);const r=this._changing;this._changing=!0;const h={};r||(this.previous=Object.assign({},this.attributes)),Object.keys(i).forEach((t=>{i[t]!==this.attributes[t]&&(h[t]=i[t],this.attributes[t]=i[t])}));const o=Object.keys(h);if(o.length&&(this._pending=["change",this,h,...n]),o.forEach((t=>{this.emit(`change:${t}`,this,i[t],...n)})),r)return this;for(;this._pending;){const t=this._pending;this._pending=null,this.emit.apply(this,t)}return this._pending=null,this._changing=!1,this}parse(t){return t}toJSON(){return Object.assign({},this.attributes)}}n.attributePrefix="";const r=["el","tag","attributes","events","model","template","onDestroy"];class h extends s{constructor(t={}){super(),this.preinitialize.apply(this,arguments),this.delegatedEventListeners=[],this.children=[],this.destroyQueue=[],this.viewOptions=[],r.forEach((e=>{e in t&&(this[e]=t[e],this.viewOptions.push(e))})),this.ensureUid(),this.ensureElement()}preinitialize(){}$(t){return this.el.querySelector(t)}$$(t){return this.el.querySelectorAll(t)}destroy(){return this.destroyChildren(),this.undelegateEvents(),this.stopListening(),this.off(),this.destroyQueue.forEach((t=>t())),this.destroyQueue=[],this.onDestroy.apply(this,arguments),this.destroyed=!0,this}onDestroy(){}addChild(t){return this.children.push(t),t}destroyChildren(){this.children.forEach((t=>t.destroy())),this.children=[]}ensureUid(){this.uid||(this.uid="r"+ ++h.uid)}ensureElement(){if(this.el)this.el=i(this.el,this);else{const t=i(this.tag,this),e=i(this.attributes,this);this.el=this.createElement(t,e)}this.delegateEvents()}createElement(t="div",e={}){let s=document.createElement(t);return Object.keys(e).forEach((t=>s.setAttribute(t,e[t]))),s}removeElement(){return this.el.remove(),this}delegateEvents(t){if(t||(t=i(this.events,this)),!t)return this;this.delegatedEventListeners.length&&this.undelegateEvents();let s={};return Object.keys(t).forEach((i=>{const n=i.split(" "),r=n.shift(),h=n.join(" ");let o=t[i];"string"==typeof o&&(o=this[o]),e(o),s[r]||(s[r]=[]),s[r].push({selector:h,listener:o})})),Object.keys(s).forEach((t=>{const e=e=>{s[t].forEach((({selector:t,listener:s})=>{if(!t)return void s.call(this,e,this,this.el);let i=e.target;for(;i&&i!==this.el;)i.matches&&i.matches(t)&&s.call(this,e,this,i),i=i.parentElement}))};this.delegatedEventListeners.push({type:t,listener:e}),this.el.addEventListener(t,e)})),this}undelegateEvents(){return this.delegatedEventListeners.forEach((({type:t,listener:e})=>{this.el.removeEventListener(t,e)})),this.delegatedEventListeners=[],this}render(){return this.template&&(this.el.innerHTML=this.template(this.model)),this}static sanitize(t){return`${t}`.replace(/[&<>"']/g,(t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t])))}}h.uid=0;class o{constructor(t){this.value=t}toString(){return this.value}}class a{constructor(t){this.items=t}}class l{constructor(){this.listeners=[],this.types=new Set,this.previousSize=0}addListener(t,e){return this.types.add(e),this.listeners.push(t),this.listeners.length-1}reset(){this.listeners=[],this.previousSize=this.types.size}hasPendingTypes(){return this.types.size>this.previousSize}}class c{constructor(){}reset(){this.paused=0,this.previous=this.tracked||new Map,this.tracked=new Map,this.positionStack=[0]}push(){this.positionStack.push(0)}pop(){this.positionStack.pop()}increment(){this.positionStack[this.positionStack.length-1]++}pause(){this.paused++}resume(){this.paused--}getPath(){return this.positionStack.join("-")}track(t){return 0===this.paused&&this.tracked.set(this.getPath(),t),t}findRecyclable(t){const e=this.previous.get(this.getPath());return e&&e.constructor===t&&!e.key?e:null}}const u=["value","checked","selected"];let d=class{constructor(t){this.getSelector=t.getSelector,this.getAttributes=t.getAttributes,this.previousAttributes={}}hydrate(t){this.ref=t.querySelector(this.getSelector())}update(){const t=this.getAttributes(),{remove:e,add:s}=function(t,e={}){const s={},i=[];return Object.keys(t).forEach((i=>{let n=t[i];n!==e[i]&&(!0===n?s[i]="":!1!==n&&(null==n&&(n=""),s[i]=n))})),Object.keys(e).forEach((s=>{(!(s in t)||e[s]!==t[s]&&!1===t[s])&&i.push(s)})),{add:s,remove:i}}(t,this.previousAttributes);this.previousAttributes=t,e.forEach((t=>{this.ref.removeAttribute(t),u.includes(t)&&t in this.ref&&(this.ref[t]="value"===t&&"")})),Object.keys(s).forEach((t=>{const e=s[t];this.ref.setAttribute(t,e),u.includes(t)&&t in this.ref&&(this.ref[t]="value"===t?e:!1!==e&&"false"!==e)}))}};const p=["value","checked","selected"];function f(t,e){t.nodeType===e.nodeType?t.nodeType!==Node.TEXT_NODE?t.tagName===e.tagName?(((t,e)=>{const s=e.attributes,i=t.attributes,n=new Set;for(let e=0,i=s.length;e<i;e++){const{name:i,value:r}=s[e];n.add(i),t.getAttribute(i)!==r&&t.setAttribute(i,r)}for(let e=i.length-1;e>=0;e--){const{name:s}=i[e];n.has(s)||t.removeAttribute(s)}for(let s=0,i=p.length;s<i;s++){const i=p[s];i in t&&t[i]!==e[i]&&(t[i]=e[i])}})(t,e),((t,e)=>{const s=t.childNodes,i=e.childNodes,n=s.length;if(n!==i.length)return!1;for(let t=0;t<n;t++)if(!s[t].isEqualNode(i[t]))return!1;return!0})(t,e)||((t,e)=>{const s=Array.from(e.childNodes);t.replaceChildren(...s)})(t,e)):t.replaceWith(e):t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue):t.replaceWith(e)}function g(t,e,s=(()=>!1),i){let n=i||t.firstChild;for(;n;){if(n.nodeType===Node.COMMENT_NODE&&n.data.trim()===e)return n;if(n.nodeType!==Node.ELEMENT_NODE||s(n)||!n.firstChild){for(;n&&!n.nextSibling;)if(n=n.parentNode,!n||n===t)return null;n&&(n=n.nextSibling)}else n=n.firstChild}return null}class E{constructor(t){this.getStart=t.getStart,this.getEnd=t.getEnd,this.expression=t.expression,this.shouldSkipFind=t.shouldSkipFind,this.shouldSkipSync=t.shouldSkipSync,this.previousItems=[]}hydrate(t){const e=g(t,this.getStart(),this.shouldSkipFind),s=g(t,this.getEnd(),this.shouldSkipFind,e);this.ref=[e,s]}update(t,e){let s;const[i,n]=this.ref,r=i.nextSibling,h=r===n,o=!h&&r.nextSibling===n;if(h?n.before(t):!o||1!==t.children.length||this.shouldSkipSync(r)||this.shouldSkipSync(t.firstChild)?(s=document.createComment(""),n.before(s),n.before(t)):f(r,t.firstChild),e(),s){if(this.ref[0].nextSibling===s)s.remove();else{const t=document.createRange();t.setStartAfter(this.ref[0]),t.setEndAfter(s),t.deleteContents()}}}updateElement(t,e,s){const i=document.createComment("");t.after(i),i.after(e.firstChild),s(),t.nextSibling===i&&t.remove(),i.remove()}}const m=t=>t.reduce(((t,e)=>(Array.isArray(e)?t.push(...m(e)):t.push(e),t)),[]);function y(t){const e=document.createElement("template");return e.innerHTML=`${t}`.trim(),e.content}function b(t){const e=[];return Object.keys(t).forEach((s=>{let i=t[s];!0===i?e.push(s):!1!==i&&(null==i&&(i=""),e.push(`${s}="${i}"`))})),e.join(" ")}const T=(t,e)=>i(t,e,e),v=t=>!!(t&&t.dataset&&t.dataset[N.DATASET_ELEMENT]&&t.dataset[N.DATASET_ELEMENT].endsWith("-1")),A=t=>!!(t&&t.dataset&&t.dataset[N.DATASET_ELEMENT])||!!t.querySelector(`[${N.ATTRIBUTE_ELEMENT}]`),S=(t,e)=>t.reduce(((t,s,i)=>(t.push(s),void 0!==e[i]&&t.push(N.PLACEHOLDER(i)),t)),[]).join(""),R=(t,e)=>{const s=N.PLACEHOLDER("(\\d+)"),i=t.match(new RegExp(`^${s}$`));if(i)return[e[parseInt(i[1],10)]];const n=new RegExp(`${s}`,"g"),r=[];let h,o=0;for(;null!==(h=n.exec(t));){const s=t.slice(o,h.index);r.push(N.markAsSafeHTML(s),e[parseInt(h[1],10)]),o=h.index+h[0].length}return r.push(N.markAsSafeHTML(t.slice(o))),r},L=(t,e)=>t.reduce(((t,s)=>{const i=e(s[0]);if(1===s.length)"object"==typeof i?t=Object.assign(t,i):"string"==typeof i&&(t[i]=!0);else{const n=s[2]?e(s[1]):s[1];t[i]=n}return t}),{}),$=(t,e,s)=>{const i={};return Object.keys(t).forEach((n=>{const r=n.match(/on(([A-Z]{1}[a-z]+)+)/);if(r&&r[1]){const h=r[1].toLowerCase(),o=t[n];if(o){const t=e.addListener(o,h);i[s(h)]=t}}else i[n]=t[n]})),i},C=(t,e,s=!1)=>{const i=N.PLACEHOLDER("(\\d+)"),n=new Map;return s||(t=t.replace(new RegExp(i,"g"),((t,s)=>{const i=e[s];if(i&&i.prototype instanceof N){if(n.has(i))return n.get(i);n.set(i,t)}return t}))),t.replace(new RegExp(`<(${i})([^>]*)>([\\s\\S]*?)</\\1>|<(${i})([^>]*)/>`,"g"),((t,s,i,n,r,h,o,l)=>{let c,u,d;if(s?(c=e[i],u=n):(c=void 0!==o?e[o]:h,u=l),!(c.prototype instanceof N))return t;if(s){const t=C(r,e,!0),s=x(t,e);d=R(s,e)}const p=_(u,e);return e.push((function(){const t=L(p,(t=>T(t,this)));return d&&(t.renderChildren=()=>new a(d.map((t=>T(t,this))))),c.mount(t)})),N.PLACEHOLDER(e.length-1)}))},M=(t,e)=>{const s=N.PLACEHOLDER("(?:\\d+)");return t.replace(new RegExp(`<(${s}|[a-z]+[1-6]?)(?:\\s*)((?:"[^"]*"|'[^']*'|[^>])*)(/?>)`,"gi"),e)},k=(t,e,s)=>{const n=N.PLACEHOLDER("(?:\\d+)");if(t.match(new RegExp(`^\\s*${n}\\s*$`)))return t;if(!t.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${n})([^>]*)>([\\s\\S]*?)</(\\1|${n})>\\s*$|^\\s*<([a-z]+[1-6]?|${n})([^>]*)/>\\s*$`)))throw new SyntaxError(`Template must have a single root element or be a container component: "${t.trim()}"`);let r=0;return M(t,((t,h,o,a)=>{const l=0===r,c=++r;if(!l&&!o.match(new RegExp(n)))return t;const u=_(o,e),d=t=>`${t}-${c}`,p=s.length;s.push({getSelector:function(){return`[${N.ATTRIBUTE_ELEMENT}="${d(this.uid)}"]`},getAttributes:function(){const t=$(L(u,(t=>T(t,this))),this.eventsManager,(t=>N.ATTRIBUTE_EVENT(t,this.uid)));return l&&this.attributes&&Object.assign(t,i(this.attributes,this)),t[N.ATTRIBUTE_ELEMENT]=d(this.uid),t}}),e.push((function(){const t=this.template.elements[p],e=t.getAttributes.call(this);return t.previousAttributes=e,N.markAsSafeHTML(b(e))}));return`<${h} ${N.PLACEHOLDER(e.length-1)}${a}`}))},x=(t,e)=>{const s=N.PLACEHOLDER("(?:\\d+)");return M(t,((t,i,n,r)=>{if(!n.match(new RegExp(s)))return t;const h=_(n,e),o=function(){return $(L(h,(t=>T(t,this))),this.eventsManager,(t=>N.ATTRIBUTE_EVENT(t,this.uid)))};e.push((function(){const t=o.call(this);return N.markAsSafeHTML(b(t))}));return`<${i} ${N.PLACEHOLDER(e.length-1)}${r}`}))},O=(t,e,s)=>{const i=N.PLACEHOLDER("(\\d+)");let n=0;return t.replace(new RegExp(i,"g"),(function(i,r,h){const o=t.substring(0,h);if(o.lastIndexOf("<")>o.lastIndexOf(">"))return i;const a=++n;const l=s.length;return s.push({getStart:function(){return N.MARKER_START(`${this.uid}-${a}`)},getEnd:function(){return N.MARKER_END(`${this.uid}-${a}`)},expression:e[r]}),e.push((function(){return this.template.interpolations[l]})),N.PLACEHOLDER(e.length-1)}))},_=(t,e)=>{const s=N.PLACEHOLDER("(\\d+)"),i=[],n=new RegExp(`(?:${s}|([\\w-]+))(?:=(["']?)(?:${s}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))?\\3)?`,"g");let r;for(;null!==(r=n.exec(t));){const[,t,s,n,h,o]=r,a=!!n;let l=void 0!==t?e[parseInt(t,10)]:s,c=void 0!==h?e[parseInt(h,10)]:o;a&&void 0===c&&(c=""),void 0!==c?i.push([l,c,a]):i.push([l])}return i},w=["key","state","onCreate","onChange","onHydrate","onRecycle","onUpdate"];class N extends h{constructor(t={}){super(...arguments),this.componentOptions=[],w.forEach((e=>{e in t&&(this[e]=t[e],this.componentOptions.push(e))}));const e={};Object.keys(t).forEach((s=>{this.viewOptions.includes(s)||this.componentOptions.includes(s)||(e[s]=t[s])})),this.props=new n(e),this.options=t,this.partial=this.partial.bind(this),this.onChange=this.onChange.bind(this),this.onCreate.apply(this,arguments)}events(){const t={};return this.eventsManager.types.forEach((s=>{const i=N.ATTRIBUTE_EVENT(s,this.uid),n=function(t,s,n){const r=n.getAttribute(i);if(r){let i=this.eventsManager.listeners[parseInt(r,10)];"string"==typeof i&&(i=this[i]),e(i),i.call(this,t,s,n)}};t[`${s} [${i}]`]=n,t[s]=n})),t}ensureElement(){this.eventsManager=new l,this.pathManager=new c,this.template=i(this.template,this),this.el&&(this.el=i(this.el,this),this.toString(),this.hydrate(this.el.parentNode))}isContainer(){return 0===this.template.elements.length&&1===this.template.interpolations.length}subscribe(t,e="change",s=this.onChange){return t.on&&this.listenTo(t,e,s),this}hydrate(t){return["model","state","props"].forEach((t=>{this[t]&&this.subscribe(this[t])})),this.isContainer()?(this.children[0].hydrate(t),this.el=this.children[0].el):(this.template.elements.forEach(((e,s)=>{0===s?(e.hydrate(t),this.el?e.ref=this.el:this.el=e.ref):e.hydrate(this.el)})),this.delegateEvents(),this.template.interpolations.forEach((t=>t.hydrate(this.el))),this.children.forEach((t=>t.hydrate(this.el)))),this.onHydrate.call(this),this}recycle(t,e){if(t){!function(t,e){if(Element.prototype.moveBefore)t.parentNode.moveBefore(e,t),t.remove();else{const s=document.activeElement;t.before(e),t.remove(),s&&s!==document.activeElement&&e.contains(s)&&s.focus()}}(g(t,N.MARKER_RECYCLED(this.uid),v),this.el)}return e&&this.props.set(e),this.onRecycle.call(this),this}getRecycledMarker(){return`\x3c!--${N.MARKER_RECYCLED(this.uid)}--\x3e`}partial(t,...e){const s=R(x(C(S(t,e).trim(),e),e),e).map((t=>T(t,this)));return new a(s)}renderTemplatePart(t,e,s=[]){const i=T(t,this),n=t=>{if(null==t||!1===t||!0===t)return"";if(t instanceof o)return s.push(t),t;if(t instanceof N)return s.push(t),e(t);if(t instanceof a){if(1===t.items.length)return n(t.items[0]);this.pathManager.push();const e=t.items.map((t=>(this.pathManager.increment(),n(t)))).join("");return this.pathManager.pop(),e}if(Array.isArray(t)){this.pathManager.pause();const e=m(t).map(n).join("");return this.pathManager.resume(),e}if(t instanceof E){this.pathManager.increment();const e=this.isContainer()?"":`\x3c!--${t.getStart()}--\x3e`,i=this.isContainer()?"":`\x3c!--${t.getEnd()}--\x3e`,r=n(T(t.expression,this));return t.previousItems=s,`${e}${r}${i}`}const i=N.sanitize(t);return s.push(i),i};return`${n(i)}`}toString(){this.destroyChildren(),this.eventsManager.reset(),this.pathManager.reset();const t=t=>(this.pathManager.track(t),this.addChild(t));return this.template.parts.map((e=>this.renderTemplatePart(e,t))).join("")}render(){if(this.destroyed)return this;if(!this.el){const t=y(this);return this.hydrate(t),this}this.eventsManager.reset(),this.pathManager.reset(),this.template.elements.forEach((t=>t.update()));const t=this.children;return this.children=[],this.template.interpolations.forEach((e=>{const s=[],i=[];this.pathManager.increment();const n=e.previousItems,r=[],h=this.renderTemplatePart(e.expression,(e=>{let n=e,r=null;return r=e.key?t.find((t=>t.key===e.key)):this.pathManager.findRecyclable(e.constructor),r?(n=r.getRecycledMarker(),i.push([r,e]),r.key||this.pathManager.track(r)):(s.push(e),this.pathManager.track(e)),n}),r);e.previousItems=r;const o=([t,e],s)=>{this.addChild(t).recycle(s,e.props.toJSON()),e.destroy()};if(1===n.length&&n[0]instanceof N&&1===r.length&&r[0]instanceof N&&1===i.length)return void o(i[0],null);const a=y(h),l=t=>()=>{i.forEach((e=>o(e,t))),s.forEach((e=>this.addChild(e).hydrate(t)))};this.isContainer()?e.updateElement(this.el,a,l(this.el.parentNode)):e.update(a,l(this.el))})),t.forEach((t=>{this.children.indexOf(t)<0&&t.destroy()})),this.isContainer()?this.el=this.children[0].el:this.eventsManager.hasPendingTypes()&&this.delegateEvents(),this.onUpdate.call(this),this}onCreate(){}onChange(){this.render()}onHydrate(){}onRecycle(){}onUpdate(){}onDestroy(){}static markAsSafeHTML(t){return new o(t)}static extend(t){const e=this;class s extends e{}return Object.assign(s.prototype,"function"==typeof t?t(e.prototype):t),s}static mount(t,e,s){const i=new this(t);return e&&(s?i.toString():e.append(y(i)),i.hydrate(e)),i}static create(t,...e){"function"==typeof t&&(e=[t],t=["",""]);const s=[],i=[],n=R(O(k(C(S(t,e).trim(),e),e,s),e,i),e);return this.extend({template(){return{elements:s.map((t=>new d({getSelector:t.getSelector.bind(this),getAttributes:t.getAttributes.bind(this)}))),interpolations:i.map((t=>new E({getStart:t.getStart.bind(this),getEnd:t.getEnd.bind(this),expression:t.expression,shouldSkipFind:v,shouldSkipSync:A}))),parts:n}}})}}N.ATTRIBUTE_ELEMENT="data-rasti-el",N.ATTRIBUTE_EVENT=(t,e)=>`data-rasti-on-${t}-${e}`,N.DATASET_ELEMENT="rastiEl",N.PLACEHOLDER=t=>`__RASTI_PH_${t}__`,N.MARKER_RECYCLED=t=>`rasti-r-${t}`,N.MARKER_START=t=>`rasti-s-${t}`,N.MARKER_END=t=>`rasti-e-${t}`;var j=N.create`<div></div>`;t.Component=j,t.Emitter=s,t.Model=n,t.View=h})); | ||
| !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Rasti={})}(this,(function(t){"use strict";function e(t){if("function"!=typeof t){throw new TypeError("Event listener must be a function")}}class s{on(t,s){return e(s),this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(s),()=>this.off(t,s)}once(t,s){e(s);const i=(...e)=>{s(...e),this.off(t,i)};return this.on(t,i)}off(t,e){this.listeners&&(t?this.listeners[t]&&(e?(this.listeners[t]=this.listeners[t].filter((t=>t!==e)),this.listeners[t].length||delete this.listeners[t]):delete this.listeners[t],Object.keys(this.listeners).length||delete this.listeners):delete this.listeners)}emit(t,...e){this.listeners&&this.listeners[t]&&this.listeners[t].slice().forEach((t=>t(...e)))}listenTo(t,e,s){return t.on(e,s),this.listeningTo||(this.listeningTo=[]),this.listeningTo.push({emitter:t,type:e,listener:s}),()=>this.stopListening(t,e,s)}listenToOnce(t,s,i){e(i);const n=(...e)=>{i(...e),this.stopListening(t,s,n)};return this.listenTo(t,s,n)}stopListening(t,e,s){this.listeningTo&&(this.listeningTo=this.listeningTo.filter((i=>!(!t||t===i.emitter&&!e||t===i.emitter&&e===i.type&&!s||t===i.emitter&&e===i.type&&s===i.listener)||(i.emitter.off(i.type,i.listener),!1))),this.listeningTo.length||delete this.listeningTo)}}const i=(t,e,...s)=>"function"!=typeof t?t:t.apply(e,s);class n extends s{constructor(){super(),this.preinitialize.apply(this,arguments),this.attributes=Object.assign({},i(this.defaults,this),this.parse.apply(this,arguments)),this.previous={},Object.keys(this.attributes).forEach(this.defineAttribute.bind(this))}preinitialize(){}defineAttribute(t){Object.defineProperty(this,`${this.constructor.attributePrefix}${t}`,{get:()=>this.get(t),set:e=>{this.set(t,e)}})}get(t){return this.attributes[t]}set(t,e,...s){let i,n;"object"==typeof t?(i=t,n=[e,...s]):(i={[t]:e},n=s);const r=this._changing;this._changing=!0;const h={};r||(this.previous=Object.assign({},this.attributes)),Object.keys(i).forEach((t=>{i[t]!==this.attributes[t]&&(h[t]=i[t],this.attributes[t]=i[t])}));const o=Object.keys(h);if(o.length&&(this._pending=["change",this,h,...n]),o.forEach((t=>{this.emit(`change:${t}`,this,i[t],...n)})),r)return this;for(;this._pending;){const t=this._pending;this._pending=null,this.emit.apply(this,t)}return this._pending=null,this._changing=!1,this}parse(t){return t}toJSON(){return Object.assign({},this.attributes)}}n.attributePrefix="";const r=["el","tag","attributes","events","model","template","onDestroy"];class h extends s{constructor(t={}){super(),this.preinitialize.apply(this,arguments),this.delegatedEventListeners=[],this.children=[],this.destroyQueue=[],this.viewOptions=[],r.forEach((e=>{e in t&&(this[e]=t[e],this.viewOptions.push(e))})),this.ensureUid(),this.ensureElement()}preinitialize(){}$(t){return this.el.querySelector(t)}$$(t){return this.el.querySelectorAll(t)}destroy(){return this.destroyChildren(),this.undelegateEvents(),this.stopListening(),this.off(),this.destroyQueue.forEach((t=>t())),this.destroyQueue=[],this.onDestroy.apply(this,arguments),this.destroyed=!0,this}onDestroy(){}addChild(t){return this.children.push(t),t}destroyChildren(){this.children.forEach((t=>t.destroy())),this.children=[]}ensureUid(){this.uid||(this.uid="r"+ ++h.uid)}ensureElement(){if(this.el)this.el=i(this.el,this);else{const t=i(this.tag,this),e=i(this.attributes,this);this.el=this.createElement(t,e)}this.delegateEvents()}createElement(t="div",e={}){let s=document.createElement(t);return Object.keys(e).forEach((t=>s.setAttribute(t,e[t]))),s}removeElement(){return this.el.parentNode.removeChild(this.el),this}delegateEvents(t){if(t||(t=i(this.events,this)),!t)return this;this.delegatedEventListeners.length&&this.undelegateEvents();let s={};return Object.keys(t).forEach((i=>{const n=i.split(" "),r=n.shift(),h=n.join(" ");let o=t[i];"string"==typeof o&&(o=this[o]),e(o),s[r]||(s[r]=[]),s[r].push({selector:h,listener:o})})),Object.keys(s).forEach((t=>{const e=e=>{s[t].forEach((({selector:t,listener:s})=>{if(!t)return void s.call(this,e,this,this.el);let i=e.target;for(;i&&i!==this.el;)i.matches&&i.matches(t)&&s.call(this,e,this,i),i=i.parentElement}))};this.delegatedEventListeners.push({type:t,listener:e}),this.el.addEventListener(t,e)})),this}undelegateEvents(){return this.delegatedEventListeners.forEach((({type:t,listener:e})=>{this.el.removeEventListener(t,e)})),this.delegatedEventListeners=[],this}render(){return this.template&&(this.el.innerHTML=this.template(this.model)),this}static sanitize(t){return`${t}`.replace(/[&<>"']/g,(t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t])))}}h.uid=0;class o{constructor(t){this.value=t}toString(){return this.value}}class a{constructor(t){this.items=t}}class l{constructor(){this.listeners=[],this.types=new Set,this.previousSize=0}addListener(t,e){return this.types.add(e),this.listeners.push(t),this.listeners.length-1}reset(){this.listeners=[],this.previousSize=this.types.size}hasPendingTypes(){return this.types.size>this.previousSize}}const c=["value","checked","selected"];let u=class{constructor(t){this.getSelector=t.getSelector,this.getAttributes=t.getAttributes,this.previousAttributes={}}hydrate(t){this.ref=t.querySelector(this.getSelector())}update(){const t=this.getAttributes(),{remove:e,add:s}=function(t,e={}){const s={},i=[];return Object.keys(t).forEach((i=>{let n=t[i];n!==e[i]&&(!0===n?s[i]="":!1!==n&&(null==n&&(n=""),s[i]=n))})),Object.keys(e).forEach((s=>{(!(s in t)||e[s]!==t[s]&&!1===t[s])&&i.push(s)})),{add:s,remove:i}}(t,this.previousAttributes);this.previousAttributes=t,e.forEach((t=>{this.ref.removeAttribute(t),-1!==c.indexOf(t)&&t in this.ref&&(this.ref[t]="value"===t&&"")})),Object.keys(s).forEach((t=>{const e=s[t];this.ref.setAttribute(t,e),-1!==c.indexOf(t)&&t in this.ref&&(this.ref[t]="value"===t?e:!1!==e&&"false"!==e)}))}};class d{constructor(){}reset(){this.paused=0,this.previous=this.tracked||new Map,this.tracked=new Map,this.positionStack=[0]}push(){this.positionStack.push(0)}pop(){this.positionStack.pop()}increment(){this.positionStack[this.positionStack.length-1]++}pause(){this.paused++}resume(){this.paused--}getPath(){return this.positionStack.join("-")}track(t){return 0===this.paused&&this.tracked.set(this.getPath(),t),t}hasSingleComponent(){if(1!==this.tracked.size||1!==this.previous.size)return!1;const[t,e]=this.tracked.entries().next().value,[s,i]=this.previous.entries().next().value;return"0"===t&&"0"===s&&e===i}findRecyclable(t){const e=this.previous.get(this.getPath());return e&&!e.key&&e.constructor===t.constructor?e:null}}const p=["value","checked","selected"];function f(t,e){t.nodeType===e.nodeType?t.nodeType!==Node.TEXT_NODE?t.tagName===e.tagName?(((t,e)=>{const s=e.attributes,i=t.attributes,n=new Set;for(let e=0,i=s.length;e<i;e++){const{name:i,value:r}=s[e];n.add(i),t.getAttribute(i)!==r&&t.setAttribute(i,r)}for(let e=i.length-1;e>=0;e--){const{name:s}=i[e];n.has(s)||t.removeAttribute(s)}for(let s=0,i=p.length;s<i;s++){const i=p[s];i in t&&t[i]!==e[i]&&(t[i]=e[i])}})(t,e),((t,e)=>{const s=t.childNodes,i=e.childNodes,n=s.length;if(n!==i.length)return!1;for(let t=0;t<n;t++)if(!s[t].isEqualNode(i[t]))return!1;return!0})(t,e)||((t,e)=>{const s=Array.from(e.childNodes);t.replaceChildren(...s)})(t,e)):t.replaceWith(e):t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue):t.replaceWith(e)}function E(t,e,s=(()=>!1),i){let n=i||t.firstChild;for(;n;){if(n.nodeType===Node.COMMENT_NODE&&n.data.trim()===e)return n;if(n.nodeType!==Node.ELEMENT_NODE||s(n)||!n.firstChild){for(;n&&!n.nextSibling;)if(n=n.parentNode,!n||n===t)return null;n&&(n=n.nextSibling)}else n=n.firstChild}return null}class g{constructor(t){this.getStart=t.getStart,this.getEnd=t.getEnd,this.expression=t.expression,this.shouldSkipFind=t.shouldSkipFind,this.shouldSkipSync=t.shouldSkipSync,this.tracker=new d}hydrate(t){const e=E(t,this.getStart(),this.shouldSkipFind),s=E(t,this.getEnd(),this.shouldSkipFind,e);this.ref=[e,s]}update(t,e){let s;const[i,n]=this.ref,r=i.nextSibling,h=r===n,o=!h&&r.nextSibling===n;if(h?n.parentNode.insertBefore(t,n):!o||1!==t.children.length||this.shouldSkipSync(r)||this.shouldSkipSync(t.firstChild)?(s=document.createComment(""),n.parentNode.insertBefore(s,n),n.parentNode.insertBefore(t,n)):f(r,t.firstChild),e(),s){if(this.ref[0].nextSibling===s)s.parentNode.removeChild(s);else{const t=document.createRange();t.setStartAfter(this.ref[0]),t.setEndAfter(s),t.deleteContents()}}}updateElement(t,e,s){const i=document.createComment("");t.parentNode.insertBefore(i,t.nextSibling),i.parentNode.insertBefore(e.firstChild,i.nextSibling),s(),t.nextSibling===i&&t.parentNode.removeChild(t),i.parentNode.removeChild(i)}}const m=t=>t.reduce(((t,e)=>(Array.isArray(e)?t.push(...m(e)):t.push(e),t)),[]);function y(t){const e=document.createElement("template");return e.innerHTML=`${t}`.trim(),e.content}function T(t){const e=[];return Object.keys(t).forEach((s=>{let i=t[s];!0===i?e.push(s):!1!==i&&(null==i&&(i=""),e.push(`${s}="${i}"`))})),e.join(" ")}const b=(t,e,s)=>{try{return i(t,e,e)}catch(t){if(s&&!t.cause){let s;s=`Error in ${e.constructor.name}#${e.uid} expression`;const i=new Error(s,{cause:t});throw i.stack=t.stack,i}throw t}},v=t=>!!(t&&t.dataset&&t.dataset[_.DATASET_ELEMENT]&&t.dataset[_.DATASET_ELEMENT].endsWith("-1")),S=t=>!!(t&&t.dataset&&t.dataset[_.DATASET_ELEMENT])||!!t.querySelector(`[${_.ATTRIBUTE_ELEMENT}]`),A=(t,e)=>t.reduce(((t,s,i)=>(t.push(s),void 0!==e[i]&&t.push(_.PLACEHOLDER(i)),t)),[]).join(""),C=(t,e)=>{const s=_.PLACEHOLDER("(\\d+)"),i=t.match(new RegExp(`^${s}$`));if(i)return[e[parseInt(i[1],10)]];const n=new RegExp(`${s}`,"g"),r=[];let h,o=0;for(;null!==(h=n.exec(t));){const s=t.slice(o,h.index);r.push(_.markAsSafeHTML(s),e[parseInt(h[1],10)]),o=h.index+h[0].length}return r.push(_.markAsSafeHTML(t.slice(o))),r},R=(t,e)=>t.reduce(((t,s)=>{const i=e(s[0]);if(1===s.length)"object"==typeof i?t=Object.assign(t,i):"string"==typeof i&&(t[i]=!0);else{const n=s[2]?e(s[1]):s[1];t[i]=n}return t}),{}),$=(t,e,s)=>{const i={};return Object.keys(t).forEach((n=>{const r=n.match(/on(([A-Z]{1}[a-z]+)+)/);if(r&&r[1]){const h=r[1].toLowerCase(),o=t[n];if(o){const t=e.addListener(o,h);i[s(h)]=t}}else i[n]=t[n]})),i},L=(t,e,s=!1)=>{const i=_.PLACEHOLDER("(\\d+)"),n=new Map;return s||(t=t.replace(new RegExp(i,"g"),((t,s)=>{const i=e[s];if(i&&i.prototype instanceof _){if(n.has(i))return n.get(i);n.set(i,t)}return t}))),t.replace(new RegExp(`<(${i})([^>]*)>([\\s\\S]*?)</\\1>|<(${i})([^>]*)/>`,"g"),((t,s,i,n,r,h,o,l)=>{let c,u,d;if(s?(c=e[i],u=n):(c=void 0!==o?e[o]:h,u=l),!(c.prototype instanceof _))return t;if(s){const t=L(r,e,!0),s=O(t,e);d=C(s,e)}const p=w(u,e);return e.push((function(){const t=R(p,(t=>b(t,this,"children options")));return d&&(t.renderChildren=()=>new a(d.map((t=>b(t,this))))),c.mount(t)})),_.PLACEHOLDER(e.length-1)}))},k=(t,e)=>{const s=_.PLACEHOLDER("(?:\\d+)");return t.replace(new RegExp(`<(${s}|[a-z]+[1-6]?)(?:\\s*)((?:"[^"]*"|'[^']*'|[^>])*)(/?>)`,"gi"),e)},x=(t,e,s)=>{const n=_.PLACEHOLDER("(?:\\d+)");if(t.match(new RegExp(`^\\s*${n}\\s*$`)))return t;const r=t.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${n})([^>]*)>([\\s\\S]*?)</(\\1|${n})>\\s*$|^\\s*<([a-z]+[1-6]?|${n})([^>]*)/>\\s*$`));if(!r){throw new Error("Invalid component template")}let h=0;return k(r[0],((t,r,o,a)=>{const l=0===h,c=++h;if(!l&&!o.match(new RegExp(n)))return t;const u=w(o,e),d=t=>`${t}-${c}`,p=s.length;s.push({getSelector:function(){return`[${_.ATTRIBUTE_ELEMENT}="${d(this.uid)}"]`},getAttributes:function(){const t=$(R(u,(t=>b(t,this,"element attribute"))),this.eventsManager,(t=>_.ATTRIBUTE_EVENT(t,this.uid)));return l&&this.attributes&&Object.assign(t,i(this.attributes,this)),t[_.ATTRIBUTE_ELEMENT]=d(this.uid),t}}),e.push((function(){const t=this.template.elements[p],e=t.getAttributes.call(this);return t.previousAttributes=e,_.markAsSafeHTML(T(e))}));return`<${r} ${_.PLACEHOLDER(e.length-1)}${a}`}))},O=(t,e)=>{const s=_.PLACEHOLDER("(?:\\d+)");return k(t,((t,i,n,r)=>{if(!n.match(new RegExp(s)))return t;const h=w(n,e),o=function(){return $(R(h,(t=>b(t,this,"partial element attribute"))),this.eventsManager,(t=>_.ATTRIBUTE_EVENT(t,this.uid)))};e.push((function(){const t=o.call(this);return _.markAsSafeHTML(T(t))}));return`<${i} ${_.PLACEHOLDER(e.length-1)}${r}`}))},N=(t,e,s)=>{const i=_.PLACEHOLDER("(\\d+)");let n=0;return t.replace(new RegExp(i,"g"),(function(i,r,h){const o=t.substring(0,h);if(o.lastIndexOf("<")>o.lastIndexOf(">"))return i;const a=++n;const l=s.length;return s.push({getStart:function(){return _.MARKER_START(`${this.uid}-${a}`)},getEnd:function(){return _.MARKER_END(`${this.uid}-${a}`)},expression:e[r]}),e.push((function(){return this.template.interpolations[l]})),_.PLACEHOLDER(e.length-1)}))},w=(t,e)=>{const s=_.PLACEHOLDER("(\\d+)"),i=[],n=new RegExp(`(?:${s}|([\\w-]+))(?:=(["']?)(?:${s}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))?\\3)?`,"g");let r;for(;null!==(r=n.exec(t));){const[,t,s,n,h,o]=r,a=!!n;let l=void 0!==t?e[parseInt(t,10)]:s,c=void 0!==h?e[parseInt(h,10)]:o;a&&void 0===c&&(c=""),void 0!==c?i.push([l,c,a]):i.push([l])}return i},M=["key","state","onCreate","onChange","onHydrate","onRecycle","onUpdate"];class _ extends h{constructor(t={}){super(...arguments),this.componentOptions=[],M.forEach((e=>{e in t&&(this[e]=t[e],this.componentOptions.push(e))}));const e={};Object.keys(t).forEach((s=>{-1===this.viewOptions.indexOf(s)&&-1===this.componentOptions.indexOf(s)&&(e[s]=t[s])})),this.props=new n(e),this.options=t,this.partial=this.partial.bind(this),this.onChange=this.onChange.bind(this),this.onCreate.apply(this,arguments)}events(){const t={};return this.eventsManager.types.forEach((s=>{const i=_.ATTRIBUTE_EVENT(s,this.uid),n=function(t,s,n){const r=n.getAttribute(i);if(r){let i=this.eventsManager.listeners[parseInt(r,10)];"string"==typeof i&&(i=this[i]),e(i),i.call(this,t,s,n)}};t[`${s} [${i}]`]=n,t[s]=n})),t}ensureElement(){if(this.eventsManager=new l,this.template=i(this.template,this),this.el){if(this.el=i(this.el,this),!this.el.parentNode){const t=`Hydration failed in ${this.constructor.name}#${this.uid}`;throw new Error(t)}this.toString(),this.hydrate(this.el.parentNode)}}isContainer(){return 0===this.template.elements.length&&1===this.template.interpolations.length}subscribe(t,e="change",s=this.onChange){return t.on&&this.listenTo(t,e,s),this}hydrate(t){return["model","state","props"].forEach((t=>{this[t]&&this.subscribe(this[t])})),this.isContainer()?(this.children[0].hydrate(t),this.el=this.children[0].el):(this.template.elements.forEach(((e,s)=>{0===s?(e.hydrate(t),this.el=e.ref):e.hydrate(this.el)})),this.delegateEvents(),this.template.interpolations.forEach((t=>t.hydrate(this.el))),this.children.forEach((t=>t.hydrate(this.el)))),this.onHydrate.call(this),this}recycle(t,e){if(t){!function(t,e){if(Element.prototype.moveBefore)t.parentNode.moveBefore(e,t),t.parentNode.removeChild(t);else{const s=document.activeElement;t.parentNode.insertBefore(e,t),t.parentNode.removeChild(t),s&&s!==document.activeElement&&e.contains(s)&&s.focus()}}(E(t,_.MARKER_RECYCLED(this.uid),v),this.el)}return e&&this.props.set(e),this.onRecycle.call(this),this}getRecycledMarker(){return`\x3c!--${_.MARKER_RECYCLED(this.uid)}--\x3e`}partial(t,...e){const s=C(O(L(A(t,e).trim(),e),e),e).map((t=>b(t,this,"partial")));return new a(s)}renderTemplatePart(t,e,s){const i=b(t,this,"template part");if(null==i||!1===i||!0===i)return"";if(i instanceof o)return`${i}`;if(i instanceof _)return`${e(i,s)}`;if(i instanceof a){if(1===i.items.length)return this.renderTemplatePart(i.items[0],e,s);s.push();const t=i.items.map((t=>(s.increment(),this.renderTemplatePart(t,e,s)))).join("");return s.pop(),t}if(Array.isArray(i)){s.pause();const t=m(i).map((t=>this.renderTemplatePart(t,e,s))).join("");return s.resume(),t}if(i instanceof g){const t=i.tracker;t.reset();const s=this.isContainer()?"":`\x3c!--${i.getStart()}--\x3e`,n=this.isContainer()?"":`\x3c!--${i.getEnd()}--\x3e`;return`${s}${this.renderTemplatePart(i.expression,e,t)}${n}`}return`${_.sanitize(i)}`}toString(){this.destroyChildren(),this.eventsManager.reset();const t=(t,e)=>(e.track(t),this.addChild(t));return this.template.parts.map((e=>this.renderTemplatePart(e,t))).join("")}render(){if(this.destroyed)return this;if(!this.el){const t=y(this);return this.hydrate(t),this}this.eventsManager.reset(),this.template.elements.forEach((t=>t.update()));const t=this.children;return this.children=[],this.template.interpolations.forEach((e=>{const s=e.tracker;s.reset();const i=[],n=[],r=this.renderTemplatePart(e.expression,(e=>{let r=e,h=null;return h=e.key?t.find((t=>t.key===e.key)):s.findRecyclable(e),h?(r=h.getRecycledMarker(),n.push([h,e]),s.track(h)):(i.push(e),s.track(e)),r}),s),h=([t,e],s)=>{this.addChild(t).recycle(s,e.props.toJSON()),e.destroy()};if(s.hasSingleComponent())return void h(n[0],null);const o=y(r),a=t=>()=>{n.forEach((e=>h(e,t))),i.forEach((e=>this.addChild(e).hydrate(t)))};this.isContainer()?e.updateElement(this.el,o,a(this.el.parentNode)):e.update(o,a(this.el))})),t.forEach((t=>{this.children.indexOf(t)<0&&t.destroy()})),this.isContainer()?this.el=this.children[0].el:this.eventsManager.hasPendingTypes()&&this.delegateEvents(),this.onUpdate.call(this),this}onCreate(){}onChange(){this.render()}onHydrate(){}onRecycle(){}onUpdate(){}onDestroy(){}static markAsSafeHTML(t){return new o(t)}static extend(t){const e=this;class s extends e{}return Object.assign(s.prototype,"function"==typeof t?t(e.prototype):t),s}static mount(t,e,s){const i=new this(t);return e&&(s?i.toString():e.append(y(i)),i.hydrate(e)),i}static create(t,...e){"function"==typeof t&&(e=[t],t=["",""]);const s=[],i=[],n=C(N(x(L(A(t,e).trim(),e),e,s),e,i),e);return this.extend({source:null,template(){return{elements:s.map((t=>new u({getSelector:t.getSelector.bind(this),getAttributes:t.getAttributes.bind(this)}))),interpolations:i.map((t=>new g({getStart:t.getStart.bind(this),getEnd:t.getEnd.bind(this),expression:t.expression,shouldSkipFind:v,shouldSkipSync:S}))),parts:n}}})}}_.ATTRIBUTE_ELEMENT="data-rasti-el",_.ATTRIBUTE_EVENT=(t,e)=>`data-rasti-on-${t}-${e}`,_.DATASET_ELEMENT="rastiEl",_.PLACEHOLDER=t=>`__RASTI_PH_${t}__`,_.MARKER_RECYCLED=t=>`rasti-r-${t}`,_.MARKER_START=t=>`rasti-s-${t}`,_.MARKER_END=t=>`rasti-e-${t}`;var j=_.create`<div></div>`;t.Component=j,t.Emitter=s,t.Model=n,t.View=h})); | ||
| //# sourceMappingURL=rasti.min.js.map |
+123
-79
@@ -7,3 +7,2 @@ import './Emitter.js'; | ||
| import EventsManager from './core/EventsManager.js'; | ||
| import PathManager from './core/PathManager.js'; | ||
| import Element from './core/Element.js'; | ||
@@ -18,4 +17,12 @@ import Interpolation from './core/Interpolation.js'; | ||
| import replaceNode from './utils/replaceNode.js'; | ||
| import createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js'; | ||
| import createProductionErrorMessage from './utils/createProductionErrorMessage.js'; | ||
| import formatTemplateSource from './utils/formatTemplateSource.js'; | ||
| import __DEV__ from './utils/dev.js'; | ||
| import './utils/repeat.js'; | ||
| import './utils/padEnd.js'; | ||
| import './utils/getAttributesDiff.js'; | ||
| import './core/PathManager.js'; | ||
| import './utils/syncNode.js'; | ||
| import './utils/padStart.js'; | ||
@@ -27,7 +34,27 @@ /** | ||
| * @param {any} context The context to call the expression with. | ||
| * @param {string} [meta] Optional metadata about the expression type for error messages. | ||
| * @return {any} The result of the evaluated expression. | ||
| * @private | ||
| */ | ||
| const getExpressionResult = (expression, context) => getResult(expression, context, context); | ||
| const getExpressionResult = (expression, context, meta) => { | ||
| try { | ||
| return getResult(expression, context, context); | ||
| } catch (error) { | ||
| if (meta && !error.cause) { | ||
| let message; | ||
| if (__DEV__) { | ||
| const formattedSource = formatTemplateSource(context.source, expression); | ||
| message = createDevelopmentErrorMessage(`Error in ${context.constructor.name}#${context.uid} (${meta})\n${error.message}\n\nTemplate source:\n\n${formattedSource}`); | ||
| } else { | ||
| message = createProductionErrorMessage(`Error in ${context.constructor.name}#${context.uid} expression`); | ||
| } | ||
| const enhancedError = new Error(message, { cause : error }); | ||
| enhancedError.stack = error.stack; | ||
| throw enhancedError; | ||
| } | ||
| throw error; | ||
| } | ||
| }; | ||
| /** | ||
@@ -219,3 +246,3 @@ * Check if an element is a component root element. | ||
| const mount = function() { | ||
| const options = expandAttributes(attributes, value => getExpressionResult(value, this)); | ||
| const options = expandAttributes(attributes, value => getExpressionResult(value, this, 'children options')); | ||
| // Add `renderChildren` function to options. | ||
@@ -266,9 +293,25 @@ if (innerList) { | ||
| if (containerMatch) return template; | ||
| // Validate that template has a root element. | ||
| const rootElementMatch = template.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${PH})([^>]*)>([\\s\\S]*?)</(\\1|${PH})>\\s*$|^\\s*<([a-z]+[1-6]?|${PH})([^>]*)/>\\s*$`)); | ||
| if (!rootElementMatch) throw new SyntaxError(`Template must have a single root element or be a container component: "${template.trim()}"`); | ||
| if (!rootElementMatch) { | ||
| const message = __DEV__ ? | ||
| createDevelopmentErrorMessage( | ||
| 'Invalid component template structure.\n' + | ||
| 'The template must have a single root element or render a single component.\n\n' + | ||
| 'Valid examples:\n' + | ||
| '- `<div>content</div>`\n' + | ||
| '- `<${MyComponent} />`\n\n' + | ||
| 'Invalid examples:\n' + | ||
| '- `<div></div><div></div>` (multiple root elements)\n' + | ||
| '- `text <div></div>` (text outside root element)' | ||
| ) : | ||
| createProductionErrorMessage('Invalid component template'); | ||
| throw new Error(message); | ||
| } | ||
| let elementUid = 0; | ||
| // Match all HTML elements including placeholders and self-closed elements. | ||
| return replaceElements(template, (match, tag, attributesStr, ending) => { | ||
| return replaceElements(rootElementMatch[0], (match, tag, attributesStr, ending) => { | ||
| const isRoot = elementUid === 0; | ||
@@ -288,3 +331,3 @@ const currentElementUid = ++elementUid; | ||
| const attributes = expandEvents( | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this)), | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'element attribute')), | ||
| this.eventsManager, | ||
@@ -347,3 +390,3 @@ type => Component.ATTRIBUTE_EVENT(type, this.uid) | ||
| const attributes = expandEvents( | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this)), | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'partial element attribute')), | ||
| this.eventsManager, | ||
@@ -475,3 +518,3 @@ type => Component.ATTRIBUTE_EVENT(type, this.uid) | ||
| Object.keys(options).forEach(key => { | ||
| if (!this.viewOptions.includes(key) && !this.componentOptions.includes(key)) { | ||
| if (this.viewOptions.indexOf(key) === -1 && this.componentOptions.indexOf(key) === -1) { | ||
| props[key] = options[key]; | ||
@@ -533,4 +576,2 @@ } | ||
| this.eventsManager = new EventsManager(); | ||
| // Store position tracking for recycling. | ||
| this.pathManager = new PathManager(); | ||
| // Call template function. | ||
@@ -542,2 +583,13 @@ this.template = getResult(this.template, this); | ||
| this.el = getResult(this.el, this); | ||
| // Check if the element has a parent node. | ||
| if (!this.el.parentNode) { | ||
| const message = __DEV__ ? | ||
| createDevelopmentErrorMessage( | ||
| `Hydration failed in ${this.constructor.name}#${this.uid}\n` + | ||
| 'The element must have a parent node for hydration to work.\n' + | ||
| 'Make sure the element is mounted in the DOM before hydrating.' | ||
| ) : | ||
| createProductionErrorMessage(`Hydration failed in ${this.constructor.name}#${this.uid}`); | ||
| throw new Error(message); | ||
| } | ||
| // Render the component as a string to generate children components. | ||
@@ -602,4 +654,3 @@ this.toString(); | ||
| element.hydrate(parent); | ||
| if (this.el) element.ref = this.el; | ||
| else this.el = element.ref; | ||
| this.el = element.ref; | ||
| } | ||
@@ -703,3 +754,3 @@ else { | ||
| expressions | ||
| ).map(item => getExpressionResult(item, this)); | ||
| ).map(item => getExpressionResult(item, this, 'partial')); | ||
@@ -717,53 +768,48 @@ return new Partial(items); | ||
| */ | ||
| renderTemplatePart(part, addChild, items = []) { | ||
| const result = getExpressionResult(part, this); | ||
| renderTemplatePart(part, addChild, tracker) { | ||
| const item = getExpressionResult(part, this, 'template part'); | ||
| const parse = item => { | ||
| if (typeof item === 'undefined' || item === null || item === false || item === true) { | ||
| return ''; | ||
| } | ||
| if (typeof item === 'undefined' || item === null || item === false || item === true) { | ||
| return ''; | ||
| } | ||
| if (item instanceof SafeHTML) { | ||
| items.push(item); | ||
| return item; | ||
| } | ||
| if (item instanceof SafeHTML) { | ||
| return `${item}`; | ||
| } | ||
| if (item instanceof Component) { | ||
| items.push(item); | ||
| return addChild(item); | ||
| } | ||
| if (item instanceof Component) { | ||
| return `${addChild(item, tracker)}`; | ||
| } | ||
| if (item instanceof Partial) { | ||
| // Single item. | ||
| if (item.items.length === 1) return parse(item.items[0]); | ||
| // Several items. | ||
| this.pathManager.push(); | ||
| const out = item.items.map(subItem => { this.pathManager.increment(); return parse(subItem); }).join(''); | ||
| this.pathManager.pop(); | ||
| return out; | ||
| } | ||
| // Handle arrays (user loops) - disable tracking. | ||
| if (Array.isArray(item)) { | ||
| this.pathManager.pause(); | ||
| const out = deepFlat(item).map(parse).join(''); | ||
| this.pathManager.resume(); | ||
| return out; | ||
| } | ||
| // Interpolation: add markers and process maintaining tracking. | ||
| if (item instanceof Interpolation) { | ||
| this.pathManager.increment(); | ||
| // Add Interpolation markers. | ||
| const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`; | ||
| const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`; | ||
| const interpolationResult = parse(getExpressionResult(item.expression, this)); | ||
| item.previousItems = items; | ||
| return `${startMarker}${interpolationResult}${endMarker}`; | ||
| } | ||
| if (item instanceof Partial) { | ||
| // Single item. | ||
| if (item.items.length === 1) return this.renderTemplatePart(item.items[0], addChild, tracker); | ||
| // Several items. | ||
| tracker.push(); | ||
| const out = item.items.map(subItem => { | ||
| tracker.increment(); | ||
| return this.renderTemplatePart(subItem, addChild, tracker); | ||
| }).join(''); | ||
| tracker.pop(); | ||
| return out; | ||
| } | ||
| // Handle arrays (user loops) - disable tracking. | ||
| if (Array.isArray(item)) { | ||
| tracker.pause(); | ||
| const out = deepFlat(item).map(subItem => this.renderTemplatePart(subItem, addChild, tracker)).join(''); | ||
| tracker.resume(); | ||
| return out; | ||
| } | ||
| // Interpolation: add markers and process maintaining tracking. | ||
| if (item instanceof Interpolation) { | ||
| // Reset the tracker. | ||
| const tracker = item.tracker; | ||
| tracker.reset(); | ||
| // Add Interpolation markers. | ||
| const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`; | ||
| const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`; | ||
| return `${startMarker}${this.renderTemplatePart(item.expression, addChild, tracker)}${endMarker}`; | ||
| } | ||
| const sanitized = Component.sanitize(item); | ||
| items.push(sanitized); | ||
| return sanitized; | ||
| }; | ||
| return `${parse(result)}`; | ||
| return `${Component.sanitize(item)}`; | ||
| } | ||
@@ -800,7 +846,5 @@ | ||
| this.eventsManager.reset(); | ||
| // Reset position tracking. | ||
| this.pathManager.reset(); | ||
| // Bind addChild method. | ||
| const addChild = component => { | ||
| this.pathManager.track(component); | ||
| const addChild = (component, tracker) => { | ||
| tracker.track(component); | ||
| return this.addChild(component); | ||
@@ -836,4 +880,2 @@ }; | ||
| this.eventsManager.reset(); | ||
| // Reset position tracking. | ||
| this.pathManager.reset(); | ||
| // Update elements. | ||
@@ -847,10 +889,13 @@ this.template.elements.forEach(element => element.update()); | ||
| this.template.interpolations.forEach(interpolation => { | ||
| // Reset the tracker. | ||
| const tracker = interpolation.tracker; | ||
| tracker.reset(); | ||
| const nextChildren = []; | ||
| const recycledChildren = []; | ||
| this.pathManager.increment(); | ||
| // `addChild` handler is called from `renderTemplatePart` for every component. | ||
| // It should handle children and return a HTML string. | ||
| // In this case, where the component updates, it handles children recycling. | ||
| const addChild = component => { | ||
| const addChild = (component) => { | ||
| let out = component; | ||
@@ -862,4 +907,4 @@ let found = null; | ||
| } else { | ||
| // Find by position and type. | ||
| found = this.pathManager.findRecyclable(component.constructor); | ||
| // Find by position and type using tracker. | ||
| found = tracker.findRecyclable(component); | ||
| } | ||
@@ -873,3 +918,3 @@ | ||
| // Track the component. | ||
| if (!found.key) this.pathManager.track(found); | ||
| tracker.track(found); | ||
| } else { | ||
@@ -879,3 +924,3 @@ // Add new component. | ||
| // Track the component. | ||
| this.pathManager.track(component); | ||
| tracker.track(component); | ||
| } | ||
@@ -885,7 +930,4 @@ // Return the component or placeholder. | ||
| }; | ||
| // Render the interpolation content and get the items. | ||
| const previousItems = interpolation.previousItems; | ||
| const items = []; | ||
| const rendered = this.renderTemplatePart(interpolation.expression, addChild, items); | ||
| interpolation.previousItems = items; | ||
| // Render the interpolation content. | ||
| const rendered = this.renderTemplatePart(interpolation.expression, addChild, tracker); | ||
@@ -899,5 +941,3 @@ const recycle = ([recycled, discarded], fragment) => { | ||
| // Same single component in the same position. Don't move it. | ||
| if (previousItems.length === 1 && previousItems[0] instanceof Component && | ||
| items.length === 1 && items[0] instanceof Component && | ||
| recycledChildren.length === 1) { | ||
| if (tracker.hasSingleComponent()) { | ||
| recycle(recycledChildren[0], null); | ||
@@ -1220,2 +1260,4 @@ return; | ||
| } | ||
| // Store original template source for debugging (only in dev mode). | ||
| const source = __DEV__ ? { strings, expressions : [...expressions] } : null; | ||
| // Create elements, interpolations and parts arrays. | ||
@@ -1243,2 +1285,3 @@ const elements = [], interpolations = []; | ||
| return this.extend({ | ||
| source, | ||
| template() { | ||
@@ -1319,1 +1362,2 @@ return { | ||
| export { Component$1 as default }; | ||
| //# sourceMappingURL=Component.js.map |
@@ -39,3 +39,3 @@ import getAttributesDiff from '../utils/getAttributesDiff.js'; | ||
| this.ref.removeAttribute(attr); | ||
| if (SYNC_PROPS.includes(attr) && attr in this.ref) { | ||
| if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) { | ||
| // Reset property to default. | ||
@@ -49,3 +49,3 @@ this.ref[attr] = attr === 'value' ? '' : false; | ||
| this.ref.setAttribute(attr, value); | ||
| if (SYNC_PROPS.includes(attr) && attr in this.ref) { | ||
| if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) { | ||
| this.ref[attr] = attr === 'value' ? value : value !== false && value !== 'false'; | ||
@@ -58,1 +58,2 @@ } | ||
| export { Element as default }; | ||
| //# sourceMappingURL=Element.js.map |
@@ -42,1 +42,2 @@ /** | ||
| export { EventsManager as default }; | ||
| //# sourceMappingURL=EventsManager.js.map |
@@ -0,1 +1,2 @@ | ||
| import PathManager from './PathManager.js'; | ||
| import syncNode from '../utils/syncNode.js'; | ||
@@ -23,3 +24,3 @@ import findComment from '../utils/findComment.js'; | ||
| this.shouldSkipSync = options.shouldSkipSync; | ||
| this.previousItems = []; | ||
| this.tracker = new PathManager(); | ||
| } | ||
@@ -61,3 +62,3 @@ | ||
| // The interpolation is empty. Insert the fragment. | ||
| endMark.before(fragment); | ||
| endMark.parentNode.insertBefore(fragment, endMark); | ||
| } else if ( | ||
@@ -76,4 +77,4 @@ currentSingleChildElement && | ||
| divider = document.createComment(''); | ||
| endMark.before(divider); | ||
| endMark.before(fragment); | ||
| endMark.parentNode.insertBefore(divider, endMark); | ||
| endMark.parentNode.insertBefore(fragment, endMark); | ||
| } | ||
@@ -87,3 +88,3 @@ // Handle the components in the fragment. Execute the handler function. | ||
| if (startMark.nextSibling === divider) { | ||
| divider.remove(); | ||
| divider.parentNode.removeChild(divider); | ||
| } else { | ||
@@ -106,7 +107,7 @@ const range = document.createRange(); | ||
| const divider = document.createComment(''); | ||
| element.after(divider); | ||
| divider.after(fragment.firstChild); | ||
| element.parentNode.insertBefore(divider, element.nextSibling); | ||
| divider.parentNode.insertBefore(fragment.firstChild, divider.nextSibling); | ||
| handleComponents(); | ||
| if (element.nextSibling === divider) element.remove(); | ||
| divider.remove(); | ||
| if (element.nextSibling === divider) element.parentNode.removeChild(element); | ||
| divider.parentNode.removeChild(divider); | ||
| } | ||
@@ -116,1 +117,2 @@ } | ||
| export { Interpolation as default }; | ||
| //# sourceMappingURL=Interpolation.js.map |
@@ -13,1 +13,2 @@ /** | ||
| export { Partial as default }; | ||
| //# sourceMappingURL=Partial.js.map |
@@ -78,12 +78,30 @@ /** | ||
| /** | ||
| * Find recyclable component by path and type. | ||
| * @param {Function} constructor The component constructor. | ||
| * @return {Component|null} The recyclable component or null. | ||
| * Tell if there was only one component rendered in the previous and current tracked maps | ||
| * and that component instance is the same (was recycled). | ||
| * Used by Component to detect simple single component cases and skip DOM moves. | ||
| * @return {boolean} Returns true when there is exactly one component at the first level | ||
| * and it was successfully recycled. | ||
| */ | ||
| findRecyclable(constructor) { | ||
| const prev = this.previous.get(this.getPath()); | ||
| return prev && prev.constructor === constructor && !prev.key ? prev : null; | ||
| hasSingleComponent() { | ||
| if (this.tracked.size !== 1 || this.previous.size !== 1) return false; | ||
| const [currentPath, currentComponent] = this.tracked.entries().next().value; | ||
| const [previousPath, previousComponent] = this.previous.entries().next().value; | ||
| // Ensure the component is at the root level (path '0') so it is not part of a deeper partial/array. | ||
| if (currentPath !== '0' || previousPath !== '0') return false; | ||
| return currentComponent === previousComponent; | ||
| } | ||
| /** | ||
| * Find a recyclable component for the current path based on constructor (type) | ||
| * when the component is un-keyed. | ||
| * @param {Component} candidate The candidate component instance. | ||
| * @return {Component|null} The recyclable component or null if none found. | ||
| */ | ||
| findRecyclable(candidate) { | ||
| const previous = this.previous.get(this.getPath()); | ||
| return previous && !previous.key && previous.constructor === candidate.constructor ? previous : null; | ||
| } | ||
| } | ||
| export { PathManager as default }; | ||
| //# sourceMappingURL=PathManager.js.map |
@@ -18,1 +18,2 @@ /** | ||
| export { SafeHTML as default }; | ||
| //# sourceMappingURL=SafeHTML.js.map |
+6
-0
| import validateListener from './utils/validateListener.js'; | ||
| import './utils/createDevelopmentErrorMessage.js'; | ||
| import './utils/repeat.js'; | ||
| import './utils/padEnd.js'; | ||
| import './utils/createProductionErrorMessage.js'; | ||
| import './utils/dev.js'; | ||
@@ -290,1 +295,2 @@ /** | ||
| export { Emitter as default }; | ||
| //# sourceMappingURL=Emitter.js.map |
+9
-1
@@ -6,2 +6,7 @@ export { default as Emitter } from './Emitter.js'; | ||
| import './utils/validateListener.js'; | ||
| import './utils/createDevelopmentErrorMessage.js'; | ||
| import './utils/repeat.js'; | ||
| import './utils/padEnd.js'; | ||
| import './utils/createProductionErrorMessage.js'; | ||
| import './utils/dev.js'; | ||
| import './utils/getResult.js'; | ||
@@ -11,6 +16,6 @@ import './core/SafeHTML.js'; | ||
| import './core/EventsManager.js'; | ||
| import './core/PathManager.js'; | ||
| import './core/Element.js'; | ||
| import './utils/getAttributesDiff.js'; | ||
| import './core/Interpolation.js'; | ||
| import './core/PathManager.js'; | ||
| import './utils/syncNode.js'; | ||
@@ -22,1 +27,4 @@ import './utils/findComment.js'; | ||
| import './utils/replaceNode.js'; | ||
| import './utils/formatTemplateSource.js'; | ||
| import './utils/padStart.js'; | ||
| //# sourceMappingURL=index.js.map |
+6
-0
| import Emitter from './Emitter.js'; | ||
| import getResult from './utils/getResult.js'; | ||
| import './utils/validateListener.js'; | ||
| import './utils/createDevelopmentErrorMessage.js'; | ||
| import './utils/repeat.js'; | ||
| import './utils/padEnd.js'; | ||
| import './utils/createProductionErrorMessage.js'; | ||
| import './utils/dev.js'; | ||
@@ -361,1 +366,2 @@ /** | ||
| export { Model as default }; | ||
| //# sourceMappingURL=Model.js.map |
@@ -15,1 +15,2 @@ /** | ||
| export { deepFlat as default }; | ||
| //# sourceMappingURL=deepFlat.js.map |
@@ -43,1 +43,2 @@ /** | ||
| export { findComment as default }; | ||
| //# sourceMappingURL=findComment.js.map |
@@ -36,1 +36,2 @@ /** | ||
| export { getAttributesDiff as default }; | ||
| //# sourceMappingURL=getAttributesDiff.js.map |
@@ -26,1 +26,2 @@ /** | ||
| export { getAttributesHTML as default }; | ||
| //# sourceMappingURL=getAttributesHTML.js.map |
@@ -16,1 +16,2 @@ /** | ||
| export { getResult as default }; | ||
| //# sourceMappingURL=getResult.js.map |
@@ -15,1 +15,2 @@ /** | ||
| export { parseHTML as default }; | ||
| //# sourceMappingURL=parseHTML.js.map |
@@ -13,7 +13,7 @@ /** | ||
| oldNode.parentNode.moveBefore(newNode, oldNode); | ||
| oldNode.remove(); | ||
| oldNode.parentNode.removeChild(oldNode); | ||
| } else { | ||
| const activeElement = document.activeElement; | ||
| oldNode.before(newNode); | ||
| oldNode.remove(); | ||
| oldNode.parentNode.insertBefore(newNode, oldNode); | ||
| oldNode.parentNode.removeChild(oldNode); | ||
| if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) { | ||
@@ -26,1 +26,2 @@ activeElement.focus(); | ||
| export { replaceNode as default }; | ||
| //# sourceMappingURL=replaceNode.js.map |
@@ -110,1 +110,2 @@ /** | ||
| export { syncNode as default }; | ||
| //# sourceMappingURL=syncNode.js.map |
@@ -0,1 +1,7 @@ | ||
| import createDevelopmentErrorMessage from './createDevelopmentErrorMessage.js'; | ||
| import createProductionErrorMessage from './createProductionErrorMessage.js'; | ||
| import __DEV__ from './dev.js'; | ||
| import './repeat.js'; | ||
| import './padEnd.js'; | ||
| /** | ||
@@ -10,3 +16,4 @@ * Validates that the listener is a function. | ||
| if (typeof listener !== 'function') { | ||
| throw new TypeError('Listener must be a function'); | ||
| const message = 'Event listener must be a function'; | ||
| throw new TypeError(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message)); | ||
| } | ||
@@ -16,1 +23,2 @@ } | ||
| export { validateListener as default }; | ||
| //# sourceMappingURL=validateListener.js.map |
+7
-1
| import Emitter from './Emitter.js'; | ||
| import getResult from './utils/getResult.js'; | ||
| import validateListener from './utils/validateListener.js'; | ||
| import './utils/createDevelopmentErrorMessage.js'; | ||
| import './utils/repeat.js'; | ||
| import './utils/padEnd.js'; | ||
| import './utils/createProductionErrorMessage.js'; | ||
| import './utils/dev.js'; | ||
@@ -212,3 +217,3 @@ /* | ||
| removeElement() { | ||
| this.el.remove(); | ||
| this.el.parentNode.removeChild(this.el); | ||
| // Return `this` for chaining. | ||
@@ -396,1 +401,2 @@ return this; | ||
| export { View as default }; | ||
| //# sourceMappingURL=View.js.map |
+123
-79
@@ -9,3 +9,2 @@ 'use strict'; | ||
| var core_EventsManager = require('./core/EventsManager.cjs'); | ||
| var core_PathManager = require('./core/PathManager.cjs'); | ||
| var core_Element = require('./core/Element.cjs'); | ||
@@ -20,4 +19,12 @@ var core_Interpolation = require('./core/Interpolation.cjs'); | ||
| var utils_replaceNode = require('./utils/replaceNode.cjs'); | ||
| var utils_createDevelopmentErrorMessage = require('./utils/createDevelopmentErrorMessage.cjs'); | ||
| var utils_createProductionErrorMessage = require('./utils/createProductionErrorMessage.cjs'); | ||
| var utils_formatTemplateSource = require('./utils/formatTemplateSource.cjs'); | ||
| var utils_dev = require('./utils/dev.cjs'); | ||
| require('./utils/repeat.cjs'); | ||
| require('./utils/padEnd.cjs'); | ||
| require('./utils/getAttributesDiff.cjs'); | ||
| require('./core/PathManager.cjs'); | ||
| require('./utils/syncNode.cjs'); | ||
| require('./utils/padStart.cjs'); | ||
@@ -29,7 +36,27 @@ /** | ||
| * @param {any} context The context to call the expression with. | ||
| * @param {string} [meta] Optional metadata about the expression type for error messages. | ||
| * @return {any} The result of the evaluated expression. | ||
| * @private | ||
| */ | ||
| const getExpressionResult = (expression, context) => utils_getResult(expression, context, context); | ||
| const getExpressionResult = (expression, context, meta) => { | ||
| try { | ||
| return utils_getResult(expression, context, context); | ||
| } catch (error) { | ||
| if (meta && !error.cause) { | ||
| let message; | ||
| if (utils_dev) { | ||
| const formattedSource = utils_formatTemplateSource(context.source, expression); | ||
| message = utils_createDevelopmentErrorMessage(`Error in ${context.constructor.name}#${context.uid} (${meta})\n${error.message}\n\nTemplate source:\n\n${formattedSource}`); | ||
| } else { | ||
| message = utils_createProductionErrorMessage(`Error in ${context.constructor.name}#${context.uid} expression`); | ||
| } | ||
| const enhancedError = new Error(message, { cause : error }); | ||
| enhancedError.stack = error.stack; | ||
| throw enhancedError; | ||
| } | ||
| throw error; | ||
| } | ||
| }; | ||
| /** | ||
@@ -221,3 +248,3 @@ * Check if an element is a component root element. | ||
| const mount = function() { | ||
| const options = expandAttributes(attributes, value => getExpressionResult(value, this)); | ||
| const options = expandAttributes(attributes, value => getExpressionResult(value, this, 'children options')); | ||
| // Add `renderChildren` function to options. | ||
@@ -268,9 +295,25 @@ if (innerList) { | ||
| if (containerMatch) return template; | ||
| // Validate that template has a root element. | ||
| const rootElementMatch = template.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${PH})([^>]*)>([\\s\\S]*?)</(\\1|${PH})>\\s*$|^\\s*<([a-z]+[1-6]?|${PH})([^>]*)/>\\s*$`)); | ||
| if (!rootElementMatch) throw new SyntaxError(`Template must have a single root element or be a container component: "${template.trim()}"`); | ||
| if (!rootElementMatch) { | ||
| const message = utils_dev ? | ||
| utils_createDevelopmentErrorMessage( | ||
| 'Invalid component template structure.\n' + | ||
| 'The template must have a single root element or render a single component.\n\n' + | ||
| 'Valid examples:\n' + | ||
| '- `<div>content</div>`\n' + | ||
| '- `<${MyComponent} />`\n\n' + | ||
| 'Invalid examples:\n' + | ||
| '- `<div></div><div></div>` (multiple root elements)\n' + | ||
| '- `text <div></div>` (text outside root element)' | ||
| ) : | ||
| utils_createProductionErrorMessage('Invalid component template'); | ||
| throw new Error(message); | ||
| } | ||
| let elementUid = 0; | ||
| // Match all HTML elements including placeholders and self-closed elements. | ||
| return replaceElements(template, (match, tag, attributesStr, ending) => { | ||
| return replaceElements(rootElementMatch[0], (match, tag, attributesStr, ending) => { | ||
| const isRoot = elementUid === 0; | ||
@@ -290,3 +333,3 @@ const currentElementUid = ++elementUid; | ||
| const attributes = expandEvents( | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this)), | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'element attribute')), | ||
| this.eventsManager, | ||
@@ -349,3 +392,3 @@ type => Component.ATTRIBUTE_EVENT(type, this.uid) | ||
| const attributes = expandEvents( | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this)), | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'partial element attribute')), | ||
| this.eventsManager, | ||
@@ -477,3 +520,3 @@ type => Component.ATTRIBUTE_EVENT(type, this.uid) | ||
| Object.keys(options).forEach(key => { | ||
| if (!this.viewOptions.includes(key) && !this.componentOptions.includes(key)) { | ||
| if (this.viewOptions.indexOf(key) === -1 && this.componentOptions.indexOf(key) === -1) { | ||
| props[key] = options[key]; | ||
@@ -535,4 +578,2 @@ } | ||
| this.eventsManager = new core_EventsManager(); | ||
| // Store position tracking for recycling. | ||
| this.pathManager = new core_PathManager(); | ||
| // Call template function. | ||
@@ -544,2 +585,13 @@ this.template = utils_getResult(this.template, this); | ||
| this.el = utils_getResult(this.el, this); | ||
| // Check if the element has a parent node. | ||
| if (!this.el.parentNode) { | ||
| const message = utils_dev ? | ||
| utils_createDevelopmentErrorMessage( | ||
| `Hydration failed in ${this.constructor.name}#${this.uid}\n` + | ||
| 'The element must have a parent node for hydration to work.\n' + | ||
| 'Make sure the element is mounted in the DOM before hydrating.' | ||
| ) : | ||
| utils_createProductionErrorMessage(`Hydration failed in ${this.constructor.name}#${this.uid}`); | ||
| throw new Error(message); | ||
| } | ||
| // Render the component as a string to generate children components. | ||
@@ -604,4 +656,3 @@ this.toString(); | ||
| element.hydrate(parent); | ||
| if (this.el) element.ref = this.el; | ||
| else this.el = element.ref; | ||
| this.el = element.ref; | ||
| } | ||
@@ -705,3 +756,3 @@ else { | ||
| expressions | ||
| ).map(item => getExpressionResult(item, this)); | ||
| ).map(item => getExpressionResult(item, this, 'partial')); | ||
@@ -719,53 +770,48 @@ return new core_Partial(items); | ||
| */ | ||
| renderTemplatePart(part, addChild, items = []) { | ||
| const result = getExpressionResult(part, this); | ||
| renderTemplatePart(part, addChild, tracker) { | ||
| const item = getExpressionResult(part, this, 'template part'); | ||
| const parse = item => { | ||
| if (typeof item === 'undefined' || item === null || item === false || item === true) { | ||
| return ''; | ||
| } | ||
| if (typeof item === 'undefined' || item === null || item === false || item === true) { | ||
| return ''; | ||
| } | ||
| if (item instanceof core_SafeHTML) { | ||
| items.push(item); | ||
| return item; | ||
| } | ||
| if (item instanceof core_SafeHTML) { | ||
| return `${item}`; | ||
| } | ||
| if (item instanceof Component) { | ||
| items.push(item); | ||
| return addChild(item); | ||
| } | ||
| if (item instanceof Component) { | ||
| return `${addChild(item, tracker)}`; | ||
| } | ||
| if (item instanceof core_Partial) { | ||
| // Single item. | ||
| if (item.items.length === 1) return parse(item.items[0]); | ||
| // Several items. | ||
| this.pathManager.push(); | ||
| const out = item.items.map(subItem => { this.pathManager.increment(); return parse(subItem); }).join(''); | ||
| this.pathManager.pop(); | ||
| return out; | ||
| } | ||
| // Handle arrays (user loops) - disable tracking. | ||
| if (Array.isArray(item)) { | ||
| this.pathManager.pause(); | ||
| const out = utils_deepFlat(item).map(parse).join(''); | ||
| this.pathManager.resume(); | ||
| return out; | ||
| } | ||
| // Interpolation: add markers and process maintaining tracking. | ||
| if (item instanceof core_Interpolation) { | ||
| this.pathManager.increment(); | ||
| // Add Interpolation markers. | ||
| const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`; | ||
| const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`; | ||
| const interpolationResult = parse(getExpressionResult(item.expression, this)); | ||
| item.previousItems = items; | ||
| return `${startMarker}${interpolationResult}${endMarker}`; | ||
| } | ||
| if (item instanceof core_Partial) { | ||
| // Single item. | ||
| if (item.items.length === 1) return this.renderTemplatePart(item.items[0], addChild, tracker); | ||
| // Several items. | ||
| tracker.push(); | ||
| const out = item.items.map(subItem => { | ||
| tracker.increment(); | ||
| return this.renderTemplatePart(subItem, addChild, tracker); | ||
| }).join(''); | ||
| tracker.pop(); | ||
| return out; | ||
| } | ||
| // Handle arrays (user loops) - disable tracking. | ||
| if (Array.isArray(item)) { | ||
| tracker.pause(); | ||
| const out = utils_deepFlat(item).map(subItem => this.renderTemplatePart(subItem, addChild, tracker)).join(''); | ||
| tracker.resume(); | ||
| return out; | ||
| } | ||
| // Interpolation: add markers and process maintaining tracking. | ||
| if (item instanceof core_Interpolation) { | ||
| // Reset the tracker. | ||
| const tracker = item.tracker; | ||
| tracker.reset(); | ||
| // Add Interpolation markers. | ||
| const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`; | ||
| const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`; | ||
| return `${startMarker}${this.renderTemplatePart(item.expression, addChild, tracker)}${endMarker}`; | ||
| } | ||
| const sanitized = Component.sanitize(item); | ||
| items.push(sanitized); | ||
| return sanitized; | ||
| }; | ||
| return `${parse(result)}`; | ||
| return `${Component.sanitize(item)}`; | ||
| } | ||
@@ -802,7 +848,5 @@ | ||
| this.eventsManager.reset(); | ||
| // Reset position tracking. | ||
| this.pathManager.reset(); | ||
| // Bind addChild method. | ||
| const addChild = component => { | ||
| this.pathManager.track(component); | ||
| const addChild = (component, tracker) => { | ||
| tracker.track(component); | ||
| return this.addChild(component); | ||
@@ -838,4 +882,2 @@ }; | ||
| this.eventsManager.reset(); | ||
| // Reset position tracking. | ||
| this.pathManager.reset(); | ||
| // Update elements. | ||
@@ -849,10 +891,13 @@ this.template.elements.forEach(element => element.update()); | ||
| this.template.interpolations.forEach(interpolation => { | ||
| // Reset the tracker. | ||
| const tracker = interpolation.tracker; | ||
| tracker.reset(); | ||
| const nextChildren = []; | ||
| const recycledChildren = []; | ||
| this.pathManager.increment(); | ||
| // `addChild` handler is called from `renderTemplatePart` for every component. | ||
| // It should handle children and return a HTML string. | ||
| // In this case, where the component updates, it handles children recycling. | ||
| const addChild = component => { | ||
| const addChild = (component) => { | ||
| let out = component; | ||
@@ -864,4 +909,4 @@ let found = null; | ||
| } else { | ||
| // Find by position and type. | ||
| found = this.pathManager.findRecyclable(component.constructor); | ||
| // Find by position and type using tracker. | ||
| found = tracker.findRecyclable(component); | ||
| } | ||
@@ -875,3 +920,3 @@ | ||
| // Track the component. | ||
| if (!found.key) this.pathManager.track(found); | ||
| tracker.track(found); | ||
| } else { | ||
@@ -881,3 +926,3 @@ // Add new component. | ||
| // Track the component. | ||
| this.pathManager.track(component); | ||
| tracker.track(component); | ||
| } | ||
@@ -887,7 +932,4 @@ // Return the component or placeholder. | ||
| }; | ||
| // Render the interpolation content and get the items. | ||
| const previousItems = interpolation.previousItems; | ||
| const items = []; | ||
| const rendered = this.renderTemplatePart(interpolation.expression, addChild, items); | ||
| interpolation.previousItems = items; | ||
| // Render the interpolation content. | ||
| const rendered = this.renderTemplatePart(interpolation.expression, addChild, tracker); | ||
@@ -901,5 +943,3 @@ const recycle = ([recycled, discarded], fragment) => { | ||
| // Same single component in the same position. Don't move it. | ||
| if (previousItems.length === 1 && previousItems[0] instanceof Component && | ||
| items.length === 1 && items[0] instanceof Component && | ||
| recycledChildren.length === 1) { | ||
| if (tracker.hasSingleComponent()) { | ||
| recycle(recycledChildren[0], null); | ||
@@ -1222,2 +1262,4 @@ return; | ||
| } | ||
| // Store original template source for debugging (only in dev mode). | ||
| const source = utils_dev ? { strings, expressions : [...expressions] } : null; | ||
| // Create elements, interpolations and parts arrays. | ||
@@ -1245,2 +1287,3 @@ const elements = [], interpolations = []; | ||
| return this.extend({ | ||
| source, | ||
| template() { | ||
@@ -1321,1 +1364,2 @@ return { | ||
| module.exports = Component$1; | ||
| //# sourceMappingURL=Component.cjs.map |
@@ -41,3 +41,3 @@ 'use strict'; | ||
| this.ref.removeAttribute(attr); | ||
| if (SYNC_PROPS.includes(attr) && attr in this.ref) { | ||
| if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) { | ||
| // Reset property to default. | ||
@@ -51,3 +51,3 @@ this.ref[attr] = attr === 'value' ? '' : false; | ||
| this.ref.setAttribute(attr, value); | ||
| if (SYNC_PROPS.includes(attr) && attr in this.ref) { | ||
| if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) { | ||
| this.ref[attr] = attr === 'value' ? value : value !== false && value !== 'false'; | ||
@@ -60,1 +60,2 @@ } | ||
| module.exports = Element; | ||
| //# sourceMappingURL=Element.cjs.map |
@@ -44,1 +44,2 @@ 'use strict'; | ||
| module.exports = EventsManager; | ||
| //# sourceMappingURL=EventsManager.cjs.map |
| 'use strict'; | ||
| var core_PathManager = require('./PathManager.cjs'); | ||
| var utils_syncNode = require('../utils/syncNode.cjs'); | ||
@@ -25,3 +26,3 @@ var utils_findComment = require('../utils/findComment.cjs'); | ||
| this.shouldSkipSync = options.shouldSkipSync; | ||
| this.previousItems = []; | ||
| this.tracker = new core_PathManager(); | ||
| } | ||
@@ -63,3 +64,3 @@ | ||
| // The interpolation is empty. Insert the fragment. | ||
| endMark.before(fragment); | ||
| endMark.parentNode.insertBefore(fragment, endMark); | ||
| } else if ( | ||
@@ -78,4 +79,4 @@ currentSingleChildElement && | ||
| divider = document.createComment(''); | ||
| endMark.before(divider); | ||
| endMark.before(fragment); | ||
| endMark.parentNode.insertBefore(divider, endMark); | ||
| endMark.parentNode.insertBefore(fragment, endMark); | ||
| } | ||
@@ -89,3 +90,3 @@ // Handle the components in the fragment. Execute the handler function. | ||
| if (startMark.nextSibling === divider) { | ||
| divider.remove(); | ||
| divider.parentNode.removeChild(divider); | ||
| } else { | ||
@@ -108,7 +109,7 @@ const range = document.createRange(); | ||
| const divider = document.createComment(''); | ||
| element.after(divider); | ||
| divider.after(fragment.firstChild); | ||
| element.parentNode.insertBefore(divider, element.nextSibling); | ||
| divider.parentNode.insertBefore(fragment.firstChild, divider.nextSibling); | ||
| handleComponents(); | ||
| if (element.nextSibling === divider) element.remove(); | ||
| divider.remove(); | ||
| if (element.nextSibling === divider) element.parentNode.removeChild(element); | ||
| divider.parentNode.removeChild(divider); | ||
| } | ||
@@ -118,1 +119,2 @@ } | ||
| module.exports = Interpolation; | ||
| //# sourceMappingURL=Interpolation.cjs.map |
@@ -15,1 +15,2 @@ 'use strict'; | ||
| module.exports = Partial; | ||
| //# sourceMappingURL=Partial.cjs.map |
@@ -80,12 +80,30 @@ 'use strict'; | ||
| /** | ||
| * Find recyclable component by path and type. | ||
| * @param {Function} constructor The component constructor. | ||
| * @return {Component|null} The recyclable component or null. | ||
| * Tell if there was only one component rendered in the previous and current tracked maps | ||
| * and that component instance is the same (was recycled). | ||
| * Used by Component to detect simple single component cases and skip DOM moves. | ||
| * @return {boolean} Returns true when there is exactly one component at the first level | ||
| * and it was successfully recycled. | ||
| */ | ||
| findRecyclable(constructor) { | ||
| const prev = this.previous.get(this.getPath()); | ||
| return prev && prev.constructor === constructor && !prev.key ? prev : null; | ||
| hasSingleComponent() { | ||
| if (this.tracked.size !== 1 || this.previous.size !== 1) return false; | ||
| const [currentPath, currentComponent] = this.tracked.entries().next().value; | ||
| const [previousPath, previousComponent] = this.previous.entries().next().value; | ||
| // Ensure the component is at the root level (path '0') so it is not part of a deeper partial/array. | ||
| if (currentPath !== '0' || previousPath !== '0') return false; | ||
| return currentComponent === previousComponent; | ||
| } | ||
| /** | ||
| * Find a recyclable component for the current path based on constructor (type) | ||
| * when the component is un-keyed. | ||
| * @param {Component} candidate The candidate component instance. | ||
| * @return {Component|null} The recyclable component or null if none found. | ||
| */ | ||
| findRecyclable(candidate) { | ||
| const previous = this.previous.get(this.getPath()); | ||
| return previous && !previous.key && previous.constructor === candidate.constructor ? previous : null; | ||
| } | ||
| } | ||
| module.exports = PathManager; | ||
| //# sourceMappingURL=PathManager.cjs.map |
@@ -20,1 +20,2 @@ 'use strict'; | ||
| module.exports = SafeHTML; | ||
| //# sourceMappingURL=SafeHTML.cjs.map |
+6
-0
| 'use strict'; | ||
| var utils_validateListener = require('./utils/validateListener.cjs'); | ||
| require('./utils/createDevelopmentErrorMessage.cjs'); | ||
| require('./utils/repeat.cjs'); | ||
| require('./utils/padEnd.cjs'); | ||
| require('./utils/createProductionErrorMessage.cjs'); | ||
| require('./utils/dev.cjs'); | ||
@@ -292,1 +297,2 @@ /** | ||
| module.exports = Emitter; | ||
| //# sourceMappingURL=Emitter.cjs.map |
+9
-1
@@ -8,2 +8,7 @@ 'use strict'; | ||
| require('./utils/validateListener.cjs'); | ||
| require('./utils/createDevelopmentErrorMessage.cjs'); | ||
| require('./utils/repeat.cjs'); | ||
| require('./utils/padEnd.cjs'); | ||
| require('./utils/createProductionErrorMessage.cjs'); | ||
| require('./utils/dev.cjs'); | ||
| require('./utils/getResult.cjs'); | ||
@@ -13,6 +18,6 @@ require('./core/SafeHTML.cjs'); | ||
| require('./core/EventsManager.cjs'); | ||
| require('./core/PathManager.cjs'); | ||
| require('./core/Element.cjs'); | ||
| require('./utils/getAttributesDiff.cjs'); | ||
| require('./core/Interpolation.cjs'); | ||
| require('./core/PathManager.cjs'); | ||
| require('./utils/syncNode.cjs'); | ||
@@ -24,2 +29,4 @@ require('./utils/findComment.cjs'); | ||
| require('./utils/replaceNode.cjs'); | ||
| require('./utils/formatTemplateSource.cjs'); | ||
| require('./utils/padStart.cjs'); | ||
@@ -32,1 +39,2 @@ | ||
| exports.Component = Component; | ||
| //# sourceMappingURL=index.cjs.map |
+6
-0
@@ -6,2 +6,7 @@ 'use strict'; | ||
| require('./utils/validateListener.cjs'); | ||
| require('./utils/createDevelopmentErrorMessage.cjs'); | ||
| require('./utils/repeat.cjs'); | ||
| require('./utils/padEnd.cjs'); | ||
| require('./utils/createProductionErrorMessage.cjs'); | ||
| require('./utils/dev.cjs'); | ||
@@ -364,1 +369,2 @@ /** | ||
| module.exports = Model; | ||
| //# sourceMappingURL=Model.cjs.map |
@@ -17,1 +17,2 @@ 'use strict'; | ||
| module.exports = deepFlat; | ||
| //# sourceMappingURL=deepFlat.cjs.map |
@@ -45,1 +45,2 @@ 'use strict'; | ||
| module.exports = findComment; | ||
| //# sourceMappingURL=findComment.cjs.map |
@@ -38,1 +38,2 @@ 'use strict'; | ||
| module.exports = getAttributesDiff; | ||
| //# sourceMappingURL=getAttributesDiff.cjs.map |
@@ -28,1 +28,2 @@ 'use strict'; | ||
| module.exports = getAttributesHTML; | ||
| //# sourceMappingURL=getAttributesHTML.cjs.map |
@@ -18,1 +18,2 @@ 'use strict'; | ||
| module.exports = getResult; | ||
| //# sourceMappingURL=getResult.cjs.map |
@@ -17,1 +17,2 @@ 'use strict'; | ||
| module.exports = parseHTML; | ||
| //# sourceMappingURL=parseHTML.cjs.map |
@@ -15,7 +15,7 @@ 'use strict'; | ||
| oldNode.parentNode.moveBefore(newNode, oldNode); | ||
| oldNode.remove(); | ||
| oldNode.parentNode.removeChild(oldNode); | ||
| } else { | ||
| const activeElement = document.activeElement; | ||
| oldNode.before(newNode); | ||
| oldNode.remove(); | ||
| oldNode.parentNode.insertBefore(newNode, oldNode); | ||
| oldNode.parentNode.removeChild(oldNode); | ||
| if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) { | ||
@@ -28,1 +28,2 @@ activeElement.focus(); | ||
| module.exports = replaceNode; | ||
| //# sourceMappingURL=replaceNode.cjs.map |
@@ -112,1 +112,2 @@ 'use strict'; | ||
| module.exports = syncNode; | ||
| //# sourceMappingURL=syncNode.cjs.map |
| 'use strict'; | ||
| var utils_createDevelopmentErrorMessage = require('./createDevelopmentErrorMessage.cjs'); | ||
| var utils_createProductionErrorMessage = require('./createProductionErrorMessage.cjs'); | ||
| var utils_dev = require('./dev.cjs'); | ||
| require('./repeat.cjs'); | ||
| require('./padEnd.cjs'); | ||
| /** | ||
@@ -12,3 +18,4 @@ * Validates that the listener is a function. | ||
| if (typeof listener !== 'function') { | ||
| throw new TypeError('Listener must be a function'); | ||
| const message = 'Event listener must be a function'; | ||
| throw new TypeError(utils_dev ? utils_createDevelopmentErrorMessage(message) : utils_createProductionErrorMessage(message)); | ||
| } | ||
@@ -18,1 +25,2 @@ } | ||
| module.exports = validateListener; | ||
| //# sourceMappingURL=validateListener.cjs.map |
+7
-1
@@ -6,2 +6,7 @@ 'use strict'; | ||
| var utils_validateListener = require('./utils/validateListener.cjs'); | ||
| require('./utils/createDevelopmentErrorMessage.cjs'); | ||
| require('./utils/repeat.cjs'); | ||
| require('./utils/padEnd.cjs'); | ||
| require('./utils/createProductionErrorMessage.cjs'); | ||
| require('./utils/dev.cjs'); | ||
@@ -215,3 +220,3 @@ /* | ||
| removeElement() { | ||
| this.el.remove(); | ||
| this.el.parentNode.removeChild(this.el); | ||
| // Return `this` for chaining. | ||
@@ -399,1 +404,2 @@ return this; | ||
| module.exports = View; | ||
| //# sourceMappingURL=View.cjs.map |
+3
-2
| { | ||
| "name": "rasti", | ||
| "version": "4.0.0-alpha.7", | ||
| "version": "4.0.0-alpha.8", | ||
| "description": "Modern MVC for building user interfaces", | ||
@@ -58,6 +58,7 @@ "type": "module", | ||
| "@eslint/js": "^9.34.0", | ||
| "@rollup/plugin-replace": "^6.0.3", | ||
| "@rollup/plugin-terser": "^0.4.4", | ||
| "chai": "^6.0.1", | ||
| "eslint": "^9.34.0", | ||
| "glob": "^11.0.3", | ||
| "glob": "^11.1.0", | ||
| "globals": "^16.3.0", | ||
@@ -64,0 +65,0 @@ "jsdoc-to-markdown": "^9.1.2", |
+17
-10
| <p align="center"> | ||
| <picture> | ||
| <source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.7/docs/logo-dark.svg"> | ||
| <img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.7/docs/logo.svg" height="120"> | ||
| <source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.8/docs/logo-dark.svg"> | ||
| <img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.8/docs/logo.svg" height="120"> | ||
| </picture> | ||
@@ -14,7 +14,7 @@ </p> | ||
| [](https://app.travis-ci.com/8tentaculos/rasti) | ||
| [](https://www.npmjs.com/package/rasti) | ||
| [](https://unpkg.com/rasti/dist/rasti.min.js) | ||
| [](https://www.npmjs.com/package/rasti) | ||
| [](https://www.jsdelivr.com/package/npm/rasti) | ||
| [](https://app.travis-ci.com/8tentaculos/rasti) | ||
| [](https://www.npmjs.com/package/rasti) | ||
| [](https://unpkg.com/rasti/dist/rasti.min.js) | ||
| [](https://www.npmjs.com/package/rasti) | ||
| [](https://www.jsdelivr.com/package/npm/rasti) | ||
@@ -50,3 +50,3 @@ ## Key Features | ||
| ### Using ES modules | ||
| ### Using ES modules via CDN | ||
@@ -57,8 +57,15 @@ ```javascript | ||
| ### Using a `<script>` tag | ||
| ### Using a UMD build via CDN | ||
| Include **Rasti** directly in your HTML using a CDN. Available UMD builds: | ||
| - [https://cdn.jsdelivr.net/npm/rasti/dist/rasti.js](https://cdn.jsdelivr.net/npm/rasti/dist/rasti.js) | ||
| - [https://cdn.jsdelivr.net/npm/rasti/dist/rasti.min.js](https://cdn.jsdelivr.net/npm/rasti/dist/rasti.min.js) | ||
| ```html | ||
| <script src="https://cdn.jsdelivr.net/npm/rasti/dist/rasti.min.js"></script> | ||
| <script src="https://cdn.jsdelivr.net/npm/rasti"></script> | ||
| ``` | ||
| The UMD build exposes the `Rasti` global object: | ||
| ```javascript | ||
@@ -65,0 +72,0 @@ const { Model, Component } = Rasti; |
+118
-79
@@ -6,3 +6,2 @@ import View from './View.js'; | ||
| import EventsManager from './core/EventsManager.js'; | ||
| import PathManager from './core/PathManager.js'; | ||
| import Element from './core/Element.js'; | ||
@@ -17,2 +16,6 @@ import Interpolation from './core/Interpolation.js'; | ||
| import replaceNode from './utils/replaceNode.js'; | ||
| import createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js'; | ||
| import createProductionErrorMessage from './utils/createProductionErrorMessage.js'; | ||
| import formatTemplateSource from './utils/formatTemplateSource.js'; | ||
| import __DEV__ from './utils/dev.js'; | ||
@@ -24,7 +27,27 @@ /** | ||
| * @param {any} context The context to call the expression with. | ||
| * @param {string} [meta] Optional metadata about the expression type for error messages. | ||
| * @return {any} The result of the evaluated expression. | ||
| * @private | ||
| */ | ||
| const getExpressionResult = (expression, context) => getResult(expression, context, context); | ||
| const getExpressionResult = (expression, context, meta) => { | ||
| try { | ||
| return getResult(expression, context, context); | ||
| } catch (error) { | ||
| if (meta && !error.cause) { | ||
| let message; | ||
| if (__DEV__) { | ||
| const formattedSource = formatTemplateSource(context.source, expression); | ||
| message = createDevelopmentErrorMessage(`Error in ${context.constructor.name}#${context.uid} (${meta})\n${error.message}\n\nTemplate source:\n\n${formattedSource}`); | ||
| } else { | ||
| message = createProductionErrorMessage(`Error in ${context.constructor.name}#${context.uid} expression`); | ||
| } | ||
| const enhancedError = new Error(message, { cause : error }); | ||
| enhancedError.stack = error.stack; | ||
| throw enhancedError; | ||
| } | ||
| throw error; | ||
| } | ||
| }; | ||
| /** | ||
@@ -216,3 +239,3 @@ * Check if an element is a component root element. | ||
| const mount = function() { | ||
| const options = expandAttributes(attributes, value => getExpressionResult(value, this)); | ||
| const options = expandAttributes(attributes, value => getExpressionResult(value, this, 'children options')); | ||
| // Add `renderChildren` function to options. | ||
@@ -263,9 +286,25 @@ if (innerList) { | ||
| if (containerMatch) return template; | ||
| // Validate that template has a root element. | ||
| const rootElementMatch = template.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${PH})([^>]*)>([\\s\\S]*?)</(\\1|${PH})>\\s*$|^\\s*<([a-z]+[1-6]?|${PH})([^>]*)/>\\s*$`)); | ||
| if (!rootElementMatch) throw new SyntaxError(`Template must have a single root element or be a container component: "${template.trim()}"`); | ||
| if (!rootElementMatch) { | ||
| const message = __DEV__ ? | ||
| createDevelopmentErrorMessage( | ||
| 'Invalid component template structure.\n' + | ||
| 'The template must have a single root element or render a single component.\n\n' + | ||
| 'Valid examples:\n' + | ||
| '- `<div>content</div>`\n' + | ||
| '- `<${MyComponent} />`\n\n' + | ||
| 'Invalid examples:\n' + | ||
| '- `<div></div><div></div>` (multiple root elements)\n' + | ||
| '- `text <div></div>` (text outside root element)' | ||
| ) : | ||
| createProductionErrorMessage('Invalid component template'); | ||
| throw new Error(message); | ||
| } | ||
| let elementUid = 0; | ||
| // Match all HTML elements including placeholders and self-closed elements. | ||
| return replaceElements(template, (match, tag, attributesStr, ending) => { | ||
| return replaceElements(rootElementMatch[0], (match, tag, attributesStr, ending) => { | ||
| const isRoot = elementUid === 0; | ||
@@ -285,3 +324,3 @@ const currentElementUid = ++elementUid; | ||
| const attributes = expandEvents( | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this)), | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'element attribute')), | ||
| this.eventsManager, | ||
@@ -344,3 +383,3 @@ type => Component.ATTRIBUTE_EVENT(type, this.uid) | ||
| const attributes = expandEvents( | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this)), | ||
| expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'partial element attribute')), | ||
| this.eventsManager, | ||
@@ -472,3 +511,3 @@ type => Component.ATTRIBUTE_EVENT(type, this.uid) | ||
| Object.keys(options).forEach(key => { | ||
| if (!this.viewOptions.includes(key) && !this.componentOptions.includes(key)) { | ||
| if (this.viewOptions.indexOf(key) === -1 && this.componentOptions.indexOf(key) === -1) { | ||
| props[key] = options[key]; | ||
@@ -530,4 +569,2 @@ } | ||
| this.eventsManager = new EventsManager(); | ||
| // Store position tracking for recycling. | ||
| this.pathManager = new PathManager(); | ||
| // Call template function. | ||
@@ -539,2 +576,13 @@ this.template = getResult(this.template, this); | ||
| this.el = getResult(this.el, this); | ||
| // Check if the element has a parent node. | ||
| if (!this.el.parentNode) { | ||
| const message = __DEV__ ? | ||
| createDevelopmentErrorMessage( | ||
| `Hydration failed in ${this.constructor.name}#${this.uid}\n` + | ||
| 'The element must have a parent node for hydration to work.\n' + | ||
| 'Make sure the element is mounted in the DOM before hydrating.' | ||
| ) : | ||
| createProductionErrorMessage(`Hydration failed in ${this.constructor.name}#${this.uid}`); | ||
| throw new Error(message); | ||
| } | ||
| // Render the component as a string to generate children components. | ||
@@ -599,4 +647,3 @@ this.toString(); | ||
| element.hydrate(parent); | ||
| if (this.el) element.ref = this.el; | ||
| else this.el = element.ref; | ||
| this.el = element.ref; | ||
| } | ||
@@ -700,3 +747,3 @@ else { | ||
| expressions | ||
| ).map(item => getExpressionResult(item, this)); | ||
| ).map(item => getExpressionResult(item, this, 'partial')); | ||
@@ -714,53 +761,48 @@ return new Partial(items); | ||
| */ | ||
| renderTemplatePart(part, addChild, items = []) { | ||
| const result = getExpressionResult(part, this); | ||
| renderTemplatePart(part, addChild, tracker) { | ||
| const item = getExpressionResult(part, this, 'template part'); | ||
| const parse = item => { | ||
| if (typeof item === 'undefined' || item === null || item === false || item === true) { | ||
| return ''; | ||
| } | ||
| if (typeof item === 'undefined' || item === null || item === false || item === true) { | ||
| return ''; | ||
| } | ||
| if (item instanceof SafeHTML) { | ||
| items.push(item); | ||
| return item; | ||
| } | ||
| if (item instanceof SafeHTML) { | ||
| return `${item}`; | ||
| } | ||
| if (item instanceof Component) { | ||
| items.push(item); | ||
| return addChild(item); | ||
| } | ||
| if (item instanceof Component) { | ||
| return `${addChild(item, tracker)}`; | ||
| } | ||
| if (item instanceof Partial) { | ||
| // Single item. | ||
| if (item.items.length === 1) return parse(item.items[0]); | ||
| // Several items. | ||
| this.pathManager.push(); | ||
| const out = item.items.map(subItem => { this.pathManager.increment(); return parse(subItem); }).join(''); | ||
| this.pathManager.pop(); | ||
| return out; | ||
| } | ||
| // Handle arrays (user loops) - disable tracking. | ||
| if (Array.isArray(item)) { | ||
| this.pathManager.pause(); | ||
| const out = deepFlat(item).map(parse).join(''); | ||
| this.pathManager.resume(); | ||
| return out; | ||
| } | ||
| // Interpolation: add markers and process maintaining tracking. | ||
| if (item instanceof Interpolation) { | ||
| this.pathManager.increment(); | ||
| // Add Interpolation markers. | ||
| const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`; | ||
| const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`; | ||
| const interpolationResult = parse(getExpressionResult(item.expression, this)); | ||
| item.previousItems = items; | ||
| return `${startMarker}${interpolationResult}${endMarker}`; | ||
| } | ||
| if (item instanceof Partial) { | ||
| // Single item. | ||
| if (item.items.length === 1) return this.renderTemplatePart(item.items[0], addChild, tracker); | ||
| // Several items. | ||
| tracker.push(); | ||
| const out = item.items.map(subItem => { | ||
| tracker.increment(); | ||
| return this.renderTemplatePart(subItem, addChild, tracker); | ||
| }).join(''); | ||
| tracker.pop(); | ||
| return out; | ||
| } | ||
| // Handle arrays (user loops) - disable tracking. | ||
| if (Array.isArray(item)) { | ||
| tracker.pause(); | ||
| const out = deepFlat(item).map(subItem => this.renderTemplatePart(subItem, addChild, tracker)).join(''); | ||
| tracker.resume(); | ||
| return out; | ||
| } | ||
| // Interpolation: add markers and process maintaining tracking. | ||
| if (item instanceof Interpolation) { | ||
| // Reset the tracker. | ||
| const tracker = item.tracker; | ||
| tracker.reset(); | ||
| // Add Interpolation markers. | ||
| const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`; | ||
| const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`; | ||
| return `${startMarker}${this.renderTemplatePart(item.expression, addChild, tracker)}${endMarker}`; | ||
| } | ||
| const sanitized = Component.sanitize(item); | ||
| items.push(sanitized); | ||
| return sanitized; | ||
| }; | ||
| return `${parse(result)}`; | ||
| return `${Component.sanitize(item)}`; | ||
| } | ||
@@ -797,7 +839,5 @@ | ||
| this.eventsManager.reset(); | ||
| // Reset position tracking. | ||
| this.pathManager.reset(); | ||
| // Bind addChild method. | ||
| const addChild = component => { | ||
| this.pathManager.track(component); | ||
| const addChild = (component, tracker) => { | ||
| tracker.track(component); | ||
| return this.addChild(component); | ||
@@ -833,4 +873,2 @@ }; | ||
| this.eventsManager.reset(); | ||
| // Reset position tracking. | ||
| this.pathManager.reset(); | ||
| // Update elements. | ||
@@ -844,10 +882,13 @@ this.template.elements.forEach(element => element.update()); | ||
| this.template.interpolations.forEach(interpolation => { | ||
| // Reset the tracker. | ||
| const tracker = interpolation.tracker; | ||
| tracker.reset(); | ||
| const nextChildren = []; | ||
| const recycledChildren = []; | ||
| this.pathManager.increment(); | ||
| // `addChild` handler is called from `renderTemplatePart` for every component. | ||
| // It should handle children and return a HTML string. | ||
| // In this case, where the component updates, it handles children recycling. | ||
| const addChild = component => { | ||
| const addChild = (component) => { | ||
| let out = component; | ||
@@ -859,4 +900,4 @@ let found = null; | ||
| } else { | ||
| // Find by position and type. | ||
| found = this.pathManager.findRecyclable(component.constructor); | ||
| // Find by position and type using tracker. | ||
| found = tracker.findRecyclable(component); | ||
| } | ||
@@ -870,3 +911,3 @@ | ||
| // Track the component. | ||
| if (!found.key) this.pathManager.track(found); | ||
| tracker.track(found); | ||
| } else { | ||
@@ -876,3 +917,3 @@ // Add new component. | ||
| // Track the component. | ||
| this.pathManager.track(component); | ||
| tracker.track(component); | ||
| } | ||
@@ -882,7 +923,4 @@ // Return the component or placeholder. | ||
| }; | ||
| // Render the interpolation content and get the items. | ||
| const previousItems = interpolation.previousItems; | ||
| const items = []; | ||
| const rendered = this.renderTemplatePart(interpolation.expression, addChild, items); | ||
| interpolation.previousItems = items; | ||
| // Render the interpolation content. | ||
| const rendered = this.renderTemplatePart(interpolation.expression, addChild, tracker); | ||
@@ -896,5 +934,3 @@ const recycle = ([recycled, discarded], fragment) => { | ||
| // Same single component in the same position. Don't move it. | ||
| if (previousItems.length === 1 && previousItems[0] instanceof Component && | ||
| items.length === 1 && items[0] instanceof Component && | ||
| recycledChildren.length === 1) { | ||
| if (tracker.hasSingleComponent()) { | ||
| recycle(recycledChildren[0], null); | ||
@@ -1217,2 +1253,4 @@ return; | ||
| } | ||
| // Store original template source for debugging (only in dev mode). | ||
| const source = __DEV__ ? { strings, expressions : [...expressions] } : null; | ||
| // Create elements, interpolations and parts arrays. | ||
@@ -1240,2 +1278,3 @@ const elements = [], interpolations = []; | ||
| return this.extend({ | ||
| source, | ||
| template() { | ||
@@ -1242,0 +1281,0 @@ return { |
@@ -39,3 +39,3 @@ import getAttributesDiff from '../utils/getAttributesDiff.js'; | ||
| this.ref.removeAttribute(attr); | ||
| if (SYNC_PROPS.includes(attr) && attr in this.ref) { | ||
| if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) { | ||
| // Reset property to default. | ||
@@ -49,3 +49,3 @@ this.ref[attr] = attr === 'value' ? '' : false; | ||
| this.ref.setAttribute(attr, value); | ||
| if (SYNC_PROPS.includes(attr) && attr in this.ref) { | ||
| if (SYNC_PROPS.indexOf(attr) !== -1 && attr in this.ref) { | ||
| this.ref[attr] = attr === 'value' ? value : value !== false && value !== 'false'; | ||
@@ -52,0 +52,0 @@ } |
@@ -0,1 +1,2 @@ | ||
| import PathManager from './PathManager.js'; | ||
| import syncNode from '../utils/syncNode.js'; | ||
@@ -23,3 +24,3 @@ import findComment from '../utils/findComment.js'; | ||
| this.shouldSkipSync = options.shouldSkipSync; | ||
| this.previousItems = []; | ||
| this.tracker = new PathManager(); | ||
| } | ||
@@ -61,3 +62,3 @@ | ||
| // The interpolation is empty. Insert the fragment. | ||
| endMark.before(fragment); | ||
| endMark.parentNode.insertBefore(fragment, endMark); | ||
| } else if ( | ||
@@ -76,4 +77,4 @@ currentSingleChildElement && | ||
| divider = document.createComment(''); | ||
| endMark.before(divider); | ||
| endMark.before(fragment); | ||
| endMark.parentNode.insertBefore(divider, endMark); | ||
| endMark.parentNode.insertBefore(fragment, endMark); | ||
| } | ||
@@ -87,3 +88,3 @@ // Handle the components in the fragment. Execute the handler function. | ||
| if (startMark.nextSibling === divider) { | ||
| divider.remove(); | ||
| divider.parentNode.removeChild(divider); | ||
| } else { | ||
@@ -106,7 +107,7 @@ const range = document.createRange(); | ||
| const divider = document.createComment(''); | ||
| element.after(divider); | ||
| divider.after(fragment.firstChild); | ||
| element.parentNode.insertBefore(divider, element.nextSibling); | ||
| divider.parentNode.insertBefore(fragment.firstChild, divider.nextSibling); | ||
| handleComponents(); | ||
| if (element.nextSibling === divider) element.remove(); | ||
| divider.remove(); | ||
| if (element.nextSibling === divider) element.parentNode.removeChild(element); | ||
| divider.parentNode.removeChild(divider); | ||
| } | ||
@@ -113,0 +114,0 @@ } |
@@ -78,12 +78,29 @@ /** | ||
| /** | ||
| * Find recyclable component by path and type. | ||
| * @param {Function} constructor The component constructor. | ||
| * @return {Component|null} The recyclable component or null. | ||
| * Tell if there was only one component rendered in the previous and current tracked maps | ||
| * and that component instance is the same (was recycled). | ||
| * Used by Component to detect simple single component cases and skip DOM moves. | ||
| * @return {boolean} Returns true when there is exactly one component at the first level | ||
| * and it was successfully recycled. | ||
| */ | ||
| findRecyclable(constructor) { | ||
| const prev = this.previous.get(this.getPath()); | ||
| return prev && prev.constructor === constructor && !prev.key ? prev : null; | ||
| hasSingleComponent() { | ||
| if (this.tracked.size !== 1 || this.previous.size !== 1) return false; | ||
| const [currentPath, currentComponent] = this.tracked.entries().next().value; | ||
| const [previousPath, previousComponent] = this.previous.entries().next().value; | ||
| // Ensure the component is at the root level (path '0') so it is not part of a deeper partial/array. | ||
| if (currentPath !== '0' || previousPath !== '0') return false; | ||
| return currentComponent === previousComponent; | ||
| } | ||
| /** | ||
| * Find a recyclable component for the current path based on constructor (type) | ||
| * when the component is un-keyed. | ||
| * @param {Component} candidate The candidate component instance. | ||
| * @return {Component|null} The recyclable component or null if none found. | ||
| */ | ||
| findRecyclable(candidate) { | ||
| const previous = this.previous.get(this.getPath()); | ||
| return previous && !previous.key && previous.constructor === candidate.constructor ? previous : null; | ||
| } | ||
| } | ||
| export default PathManager; |
@@ -13,7 +13,7 @@ /** | ||
| oldNode.parentNode.moveBefore(newNode, oldNode); | ||
| oldNode.remove(); | ||
| oldNode.parentNode.removeChild(oldNode); | ||
| } else { | ||
| const activeElement = document.activeElement; | ||
| oldNode.before(newNode); | ||
| oldNode.remove(); | ||
| oldNode.parentNode.insertBefore(newNode, oldNode); | ||
| oldNode.parentNode.removeChild(oldNode); | ||
| if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) { | ||
@@ -20,0 +20,0 @@ activeElement.focus(); |
@@ -0,1 +1,5 @@ | ||
| import createDevelopmentErrorMessage from './createDevelopmentErrorMessage.js'; | ||
| import createProductionErrorMessage from './createProductionErrorMessage.js'; | ||
| import __DEV__ from './dev.js'; | ||
| /** | ||
@@ -10,4 +14,5 @@ * Validates that the listener is a function. | ||
| if (typeof listener !== 'function') { | ||
| throw new TypeError('Listener must be a function'); | ||
| const message = 'Event listener must be a function'; | ||
| throw new TypeError(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message)); | ||
| } | ||
| } |
+1
-1
@@ -212,3 +212,3 @@ import Emitter from './Emitter.js'; | ||
| removeElement() { | ||
| this.el.remove(); | ||
| this.el.parentNode.removeChild(this.el); | ||
| // Return `this` for chaining. | ||
@@ -215,0 +215,0 @@ return this; |
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
1303221
152.66%142
118.46%12281
9.77%207
3.5%13
8.33%2
Infinity%